import React from 'react'; import { Upload, Modal, message, Tabs, Result, Input, Collapse,Anchor } from 'antd'; import { PlusOutlined, CheckCircleFilled } from '@ant-design/icons'; import ImgCrop from 'antd-img-crop'; import classnames from 'classnames'; import { UploadFile, UploadChangeParam, RcFile } from 'antd/lib/upload/interface'; import { uuid } from '@/utils/tools'; import { get, PUBLISH_SERVICE } from '@/services'; import styles from './index.less'; import { getImageBases } from '@/services/common/api'; import UploadContext from './context' import _, { clone } from 'lodash' const { Link } = Anchor; const { TabPane } = Tabs; const { Search } = Input; // 维护图片分类映射 const wallCateName: any = { icon: '图标', bg: '背景', uploaded: '上传', }; const tabNames: any = { icon: 'icon图标', androidMenu: '移动应用图标', menuNew: '应用图标', logo: '项目logo', menu: '菜单图标', CityTemp: '用户上传', } function getBase64(file: File | Blob) { return new Promise<string>((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result as string); reader.onerror = error => reject(error); }); } interface PicturesWallType { fileList?: UploadFile<any>[]; action?: string; headers?: any; withCredentials?: boolean; maxLen?: number; onChange?: (v: any) => void; cropRate?: number | boolean; isCrop?: boolean; type?: 'CityTemp' | 'icon' | 'androidMenu' | 'menuNew', value?: string | string[], uploadContext: any, search: string, actives: any, picType: any, } class PicturesWall extends React.Component<PicturesWallType> { state = { previewVisible: false, previewImage: '', wallModalVisible: false, previewTitle: '', imgBed: this.props.uploadContext?.imgBed || [], curSelectedImg: '', baseUrl: '', prevProps: {}, search: '', actives: {}, fileList: this.props.value ? Array.isArray(this.props.value) ? this.props.value.map((v) => ({ url: v, uid: uuid(8, 16), name: '熊猫运维中台系统', status: 'done', })) as UploadFile<any>[] : [{ url: this.props.value, uid: uuid(8, 16), name: '熊猫运维中台系统', status: 'done', }] as UploadFile<any>[] : [], }; /** * 判断value是否更新,若更新则更新state.fileList * 判断imgBed是否更新,若更新则更新state.imgBed * @param props * @param state */ static getDerivedStateFromProps = (props, state) => { const { value, uploadContext = {} } = props; const { imgBed, update } = uploadContext; const fileList = state.fileList; const shouldUpdate = fileList.every(f => Array.isArray(value) ? !value.some(v => f.url === v) : f.url !== value) if (value !== state.prevProps.value && shouldUpdate) { return { prevProps: props, fileList: Array.isArray(value) ? value.map((v) => ({ url: v, uid: uuid(8, 16), name: '熊猫运维中台系统', status: 'done', })) as UploadFile<any>[] : value ? [{ url: value, uid: uuid(8, 16), name: '熊猫运维中台系统', status: 'done', }] as UploadFile<any>[] : [] } } if (imgBed != state.prevProps.uploadContext?.imgBed) { const activeKeys = {}; const imgArr = JSON.parse(JSON.stringify(imgBed)) imgArr.forEach(item => { if(item.fileUrls.length > 0) { item.child.unshift({ moduleName: item.moduleName, fileUrls: item.fileUrls, child: [], baseUrl: item.baseUrl }) } activeKeys[item.moduleName] = item.child.map(c => c.moduleName) }); return { prevProps: props, imgBed, actives: activeKeys, } } return { prevProps: props }; } update = () => { if (this.props.value) { this.setState({ fileList: Array.isArray(this.props.value) ? this.props.value.map((v) => ({ url: v, uid: uuid(8, 16), name: '熊猫运维中台系统', status: 'done', })) as UploadFile<any>[] : [{ url: this.props.value, uid: uuid(8, 16), name: '熊猫运维中台系统', status: 'done', }] as UploadFile<any>[] }) } } handleCancel = () => this.setState({ previewVisible: false }); handleModalCancel = () => this.setState({ wallModalVisible: false }); handlePreview = async (file: UploadFile<any>) => { if (!file.url && !file.preview) { file.preview = await getBase64(file.originFileObj!); } this.setState({ previewImage: file.url || file.preview, previewVisible: true, previewTitle: file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1), }); }; handleWallSelect = (url: string) => { this.setState({ wallModalVisible: true, }); }; handleImgSelected = (url: string, baseUrl: any) => { console.log(url,baseUrl); this.setState({ curSelectedImg: url, baseUrl: baseUrl }); }; handleWallShow = () => { this.setState({ wallModalVisible: true, }); }; handleModalOk = () => { const { maxLen = 1 } = this.props; const { baseUrl, curSelectedImg } = this.state const fileList = [ { uid: uuid(8, 16), name: '熊猫运维中台系统', status: 'done', url: curSelectedImg, }, ]; if (!curSelectedImg) { message.warn({ duration: 2, content: '请先选择图片' }) return } if (curSelectedImg.includes('CityTemp')) { localStorage.setItem('pd-baseUrl', baseUrl) } else { localStorage.setItem('pd2-baseUrl', baseUrl) } console.log(fileList[0].url) this.props.onChange && this.props.onChange(maxLen === 1 ? fileList[0].url.replace('civweb4\\', '') : fileList.map(f => f.url.replace('civweb4\\', ''))); this.setState({ fileList, wallModalVisible: false }); }; handleChange = ({ file, fileList }: UploadChangeParam<UploadFile<any>>) => { const { maxLen = 1, uploadContext } = this.props; this.setState({ fileList }); if (file.status === 'done') { const files = fileList.map(item => { const { status, name, thumbUrl } = item; const url = item.response && item.response.data || thumbUrl; return { uid: uuid(8, 16), name, status, url }; }); this.setState({ fileList: files }) this.props.onChange && this.props.onChange(maxLen === 1 ? files[0].url : files.map(f => f.url)); uploadContext?.update && uploadContext.update() } }; handleBeforeUpload = (file: RcFile) => { const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg' || file.type === 'image/gif'; if (!isJpgOrPng) { message.error('只能上传格式为jpeg/png/gif的图片'); } const isLt2M = file.size / 1024 / 1024 < 2; if (!isLt2M) { message.error('图片必须小于2MB!'); } return isJpgOrPng && isLt2M || Upload.LIST_IGNORE;; }; // componentDidMount() { // const { type = ''} = this.props; // getImageBases(type).then(res => { // if(res.code === 0){ // this.setState({imgBed: res.data}) // } // }) // } getImageUrl(path) { if (path && path.indexOf('http') === 0) { return path } if (path && path.indexOf('data:') === 0) { return path } if (path && path.indexOf('assets') === 0) { return `${window.location.origin}/civweb4/${path}`.replace(/\\/g, '/') } return `${window.location.origin}/${path}`.replace(/\\/g, '/') } getFileName(path: string) { const match = path.match(/(?<=[\/\\])([^\\\/\.]*)(\.?\w*)$/) return match && match[1] || '' } renderImgItem(url, baseUrl) { const fileName = this.getFileName(url) const { curSelectedImg, search } = this.state; return ( <div className={classnames({ [styles.imgItem]: true, [styles.hide]: !fileName.includes(search) })} key={url} onClick={() => this.handleImgSelected(url, baseUrl)}> <div className={classnames( curSelectedImg === url ? styles.seleted : '', )} > <img className={classnames({ [styles.svgGray]: /\.svg$/.test(url) })} src={this.getImageUrl(url)} title={url} alt="熊猫运维中台系统" /> <span className={styles.iconBtn}> <CheckCircleFilled /> </span> </div> <span id={`#${curSelectedImg}`} title={fileName}>{fileName}</span> </div> ); } renderCollapse(module) { const { Panel } = Collapse; const items = module.fileUrls.map(url => this.renderImgItem(url, module.baseUrl)) return items.length > 0 && <Panel forceRender header={module.moduleName} key={module.moduleName}> {items} </Panel> } handleCollapseChange(v, moduleName) { const activeKeys = { ...this.state.actives } activeKeys[moduleName] = v; this.setState({ actives: activeKeys }) } render() { const { previewVisible, previewImage, fileList, previewTitle, wallModalVisible, imgBed, actives, curSelectedImg, } = this.state; const { action = `${window.location.origin}${PUBLISH_SERVICE}/FileCenter/UploadSingleFile`, headers, withCredentials = true, maxLen = 1, cropRate = 375 / 158, isCrop, picType, } = this.props; const uploadButton = ( <div> <PlusOutlined /> <div className="ant-upload-text">上传</div> </div> ); const onSearch = (key) => { const bed = this.props.uploadContext.imgBed const nodes = _.cloneDeep(bed) for(const node of nodes){ arrayTreeFilter(node,key) } console.log(nodes) this.setState({imgBed:nodes}) } const arrayTreeFilter = (node,key) => { let reg = new RegExp(`/${key}/g`) for (const nodeItem of node.child) { if ( nodeItem.child.length < 1) { if(!reg.test(nodeItem.moduleName)){ node.child.splice(nodeItem,1) console.log(nodeItem.moduleName,"--------------------移除图包-----------------") } }else{ arrayTreeFilter(nodeItem,key); } } } return ( <> {isCrop ? ( <ImgCrop modalTitle="裁剪图片" modalOk="确定" modalCancel="取消" rotate={true} aspect={cropRate as number} > <Upload fileList={fileList.map(f => ({ ...f, url: this.getImageUrl(f.url) }))} onPreview={this.handlePreview} onChange={this.handleChange} name="singleFile" listType="picture-card" className={styles.avatarUploader} action={action} withCredentials={withCredentials} headers={{ // 'x-requested-with': localStorage.getItem('user') || '', Authorization: 'Bearer ' + localStorage.getItem('token') || '', ...headers, }} beforeUpload={this.handleBeforeUpload} > {fileList.length >= maxLen ? null : uploadButton} </Upload> </ImgCrop> ) : ( <Upload fileList={fileList.map(f => ({ ...f, url: this.getImageUrl(f.url) }))} onPreview={this.handlePreview} onChange={this.handleChange} name="singleFile" listType="picture-card" className={styles.avatarUploader} action={action} withCredentials={withCredentials} headers={{ // 'x-requested-with': localStorage.getItem('user') || '', Authorization: 'Bearer ' + localStorage.getItem('token') || '', ...headers, }} beforeUpload={this.handleBeforeUpload} > {fileList.length >= maxLen ? null : uploadButton} </Upload> )} <div className={styles.wallBtn} onClick={this.handleWallShow}> 从图片库选择 </div> <Modal visible={previewVisible} // title={previewTitle} footer={null} onCancel={this.handleCancel} > <img alt="预览图片" className={styles.svgBg} style={{ width: '100%' }} src={this.getImageUrl(previewImage)} /> </Modal> <Modal visible={wallModalVisible} title="图片库" okText="确定" cancelText="取消" width={1080} onCancel={this.handleModalCancel} onOk={this.handleModalOk} className={styles.modal} > <Input placeholder="搜索图库" className={styles.search} size="middle" value={this.state.search} style={{marginLeft:'102px',width:'50%'}} onChange={e => this.setState({search: e.target.value})} allowClear/> {/* <Search style ={{ width:'200px',margin: '10px 0 10px 100px'}} onSearch={onSearch} /> */} <Tabs defaultActiveKey={imgBed[0]?.moduleName} tabPosition="left" > {imgBed.map((item, i) => { if (item.moduleName == picType || item.moduleName == 'CityTemp') { const child = [...item.child] || []; if (item.fileUrls.length > 0) { child.unshift({ moduleName: item.moduleName, fileUrls: item.fileUrls, child: [], baseUrl: item.baseUrl }) } return ( <TabPane tab={tabNames[item.moduleName] || item.moduleName} key={item.moduleName} style={{overflow:'hidden'}}> <div className={styles.imgBox}> <Collapse bordered activeKey={actives[item.moduleName]} onChange={(v) => this.handleCollapseChange(v, item.moduleName)}> {child.map(child => this.renderCollapse(child))} </Collapse> </div> </TabPane> ); } })} {/* <TabPane tab="更多" key="more"> <Result status="500" title="温馨提示" subTitle="更多素材, 正在筹备中..." /> </TabPane> */} </Tabs> </Modal> </> ); } } const PicturesWallWrapper = (props: any) => { return <UploadContext.Consumer> {(uploadContext) => <PicturesWall {...props} uploadContext={uploadContext} />} </UploadContext.Consumer> } export default PicturesWallWrapper;