From 07c6cb0e4b8b6d35251963f3eb5bd9e55e088399 Mon Sep 17 00:00:00 2001 From: manzhenhua Date: Tue, 12 May 2020 10:51:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AD=90=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/upload/common.less | 86 +++++++++++++ components/upload/config.js | 31 +++++ components/upload/detail.less | 202 +++++++++++++++++++++++++++++++ components/upload/index.js | 222 ++++++++++++++++++++++++++++++++++ components/upload/preview.js | 49 ++++++++ components/upload/styles.less | 13 ++ components/upload/utils.js | 37 ++++++ 7 files changed, 640 insertions(+) create mode 100644 components/upload/common.less create mode 100644 components/upload/config.js create mode 100644 components/upload/detail.less create mode 100644 components/upload/index.js create mode 100644 components/upload/preview.js create mode 100644 components/upload/styles.less create mode 100644 components/upload/utils.js diff --git a/components/upload/common.less b/components/upload/common.less new file mode 100644 index 0000000..37ac035 --- /dev/null +++ b/components/upload/common.less @@ -0,0 +1,86 @@ +:global .upload-list-picture { + position: relative; + .ant-upload-list{ + margin-top: 50px; + } + .ant-upload-select-picture-card{ + background-color: transparent !important; + border-style: none !important; + } + .ant-upload-list-picture-card .ant-upload-list-item { + border-style: none; + display: flex; + margin:0; + :first-child{ + // height: 100%; + flex: 1; + } + } + .ant-upload-list-item-info::before { + position: absolute; + top: 0; + left: 0; + z-index: 1; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.5); + opacity: 0; + -webkit-transition: all .3s; + transition: all .3s; + content: ' '; + } + .ant-upload-list-item:hover .ant-upload-list-item-info::before{ + opacity: 1; + } + .ant-upload-list-item-card-actions.picture{ + position: absolute; + top: 50%; + left: 50%; + z-index: 10; + opacity: 1; + transform: translate(-30%, -50%); + white-space: nowrap; + } + .ant-upload-list-item-card-actions .anticon{ + color: #fff; + } + .ant-upload-list-item-uploading.ant-upload-list-item{ + background-color: transparent !important; + } +.ant-upload-animate-enter { + animation-name: uploadAnimateInlineIn; + } + .ant-upload-animate-leave { + animation-name: uploadAnimateInlineOut; + } + + .ant-upload-list-item-name{ + position: absolute; + top:68px; + margin: 0; + padding: 0; + font-size: 12px; + display: block; + } + .ant-upload-list-picture-card .ant-upload-list-item-thumbnail{ + position: inherit; + } + .ant-upload-list-picture-card .ant-upload-list-item-thumbnail img{ + margin: 0 auto; + height: 60px; + width: auto; + max-width: 80px + } + .ant-upload.ant-upload-select-picture-card{ + display: block; + height: auto; + position: absolute; + top: 0; + } +} +:global .upload-list-text{ + +} +:global .ant-upload-list-item-info{ + padding: 0 20px 0 4px; +} diff --git a/components/upload/config.js b/components/upload/config.js new file mode 100644 index 0000000..a609b40 --- /dev/null +++ b/components/upload/config.js @@ -0,0 +1,31 @@ +/* eslint-disable */ +export const fileConfig={ + 'image':{}, + 'ppt':{ + thumbUrl:`${CDN_PATH}/pptIcon.png` + }, + 'pptx':{ + thumbUrl:`${CDN_PATH}/pptIcon.png` + }, + 'zip':{ + thumbUrl:`${CDN_PATH}/zipIcon.png` + }, + 'xlsx':{ + thumbUrl:`${CDN_PATH}/xlsIcon.png` + }, + 'xls':{ + thumbUrl:`${CDN_PATH}/xlsIcon.png` + }, + 'doc':{ + thumbUrl:`${CDN_PATH}/docIcon.png` + }, + 'docx':{ + thumbUrl:`${CDN_PATH}/docIcon.png` + }, + 'pdf':{ + thumbUrl:`${CDN_PATH}/pdfIcon.png` + }, + 'other':{ + thumbUrl:`${CDN_PATH}/otherIcon.png` + } +} diff --git a/components/upload/detail.less b/components/upload/detail.less new file mode 100644 index 0000000..b7592ac --- /dev/null +++ b/components/upload/detail.less @@ -0,0 +1,202 @@ +@import "~@/theme/common"; + +.fileList { + display: flex; + flex-flow: row; + flex-wrap: wrap; +} + +.fileListCenter { + justify-content: center; +} + +.wrap { + position: relative; + cursor: pointer; +} + +.wrap:hover .fileBoxHove { + display: flex; +} + +.fileBox { + padding: 10px; + width: 100px; + display: flex; + flex-flow: column; + justify-items: center; + align-items: center; + border-radius: 4px; + overflow: hidden; +} + +.fileBoxHove { + position: absolute; + top: 0; + left: 0; + display: none; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + border-radius: 4px; + + .icon { + color: #fff; + padding: 5px; + } +} + +.fileName { + margin-top: 5px; + width: 80px; + font-size: 12px; + font-weight: 400; + color: rgba(90, 104, 118, 1); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: center; +} + +.fileStyle { + height: 56px; +} + +.dialog { + width: 900px !important; + min-height: 500px; +} + +.textUploadList { + box-sizing: border-box; + margin: 0; + padding: 0; + color: rgba(0, 0, 0, 0.65); + font-size: 14px; + font-variant: tabular-nums; + line-height: 1.5; + list-style: none; + -webkit-font-feature-settings: 'tnum'; + font-feature-settings: 'tnum'; + zoom: 1; + + .textUploadListItem { + position: relative; + height: 22px; + margin-top: 8px; + font-size: 14px; + + .textUploadListItemInfo { + height: 100%; + padding: 0 12px 0 4px; + + .textUploadListItemBox { + display: flex; + align-items: center; + + .textUploadListItemInfoName { + display: inline-block; + margin-right: 10px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + color: #5a6876; + + &:hover { + color: #5c99ff; + } + } + } + } + } +} + +.downIcon { + display: inline-block; + position: absolute; + right: 0; + top: 50%; + transform: translate(0, -50%); + cursor: pointer; +} + +.pictureTextUploadList { + box-sizing: border-box; + margin: 0; + padding: 0; + color: rgba(0, 0, 0, 0.65); + font-size: 14px; + font-variant: tabular-nums; + line-height: 1.5; + list-style: none; + -webkit-font-feature-settings: 'tnum'; + font-feature-settings: 'tnum'; + zoom: 1; + overflow: hidden; + width: 100%; + + .textUploadListItem { + position: relative; + display: flex; + justify-items: center; + height: 41px; + margin-top: 10px; + font-size: 14px; + width: 100%; + + .textUploadListItemInfo { + width: 100%; + height: 100%; + padding: 0 12px 0 4px; + + .textUploadListItemBox { + display: flex; + align-items: center; + height: 100%; + + .fileImgStyle { + width: 32px; + margin-right: 10px; + cursor: pointer; + } + + .textUploadListItemInfoName { + display: inline-block; + margin-right: 10px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + color: @textGeneralColor; + + &:hover { + color: @primaryColor; + } + } + &:hover{ + .downIcon{ + opacity: 1; + } + } + + .downIcon { + display: inline-block; + position: absolute; + right: 0; + top: 50%; + transform: translate(0, -50%); + cursor: pointer; + color: @textSupplyColor; + opacity: 0; + transition: opacity 0.2s linear; + } + } + } + } +} + + +// .textUploadListItemInfo:hover { +// background: #e6f7ff; +// } diff --git a/components/upload/index.js b/components/upload/index.js new file mode 100644 index 0000000..f43f36f --- /dev/null +++ b/components/upload/index.js @@ -0,0 +1,222 @@ +import React from 'react'; +import { Button, Icon, message } from 'antd'; +import moment from 'moment'; +import Upload from '@/submodule/ant_components/BIUpload'; +import { getToken } from '@/services/api'; +import { getRandom } from '@/utils/utils'; +import { checkoutFileType } from './utils'; +import Preview from './preview'; +import styles from './styles.less'; +import './common.less'; +// 此组件用于处理airtable里面附件上传 +/* eslint-disable */ +message.config({ + maxCount: 1, +}); +class UploadCom extends React.Component { + state = { + fileList: [], + token: null, + domain: null, + }; + preFileList = []; // 即将要上传的附件 + componentDidMount() { + const fileList = this.formateFileList(this.props.value) || []; + this.preFileList = fileList; + this.setState({ fileList }); + const dom = document.querySelector('.Upload'); + if (dom) { + dom.addEventListener('mouseover', this.hoverFileName, false); + dom.addEventListener('mouseout', this.fileOut, false); + } + } + componentWillUnmount() { + const dom = document.querySelector('.Upload'); + if (dom) { + dom.removeEventListener('mouseover', this.hoverFileName, false); + dom.removeEventListener('mouseout', this.fileOut, false); + } + } + + UNSAFE_componentWillReceiveProps(nextProps) { + if (JSON.stringify(nextProps.value) !== JSON.stringify(this.props.value)) { + const value = Array.isArray(nextProps.value) ? nextProps.value : []; + const fileList = this.formateFileList(value); + this.preFileList = fileList; + this.setState({ fileList }); + } + } + hoverFileName = (e) => { + if (e.target.className === 'ant-upload-list-item-info') { + const dom = document.getElementById('show-file-name'); + var rect = e.target.getBoundingClientRect(); + dom.innerHTML = e.target.lastChild.lastChild.innerHTML; + dom.style.display = 'block'; + dom.style.top = rect.top + rect.height + 'px'; + dom.style.left = e.x - 100 + 'px'; + } + }; + fileOut = (e) => { + if (e.target.className === 'ant-upload-list-item-info') { + const dom = document.getElementById('show-file-name'); + dom.innerHTML = ''; + dom.style.display = 'none'; + dom.style.top = e.y + 30 + 'px'; + dom.style.left = e.x - 50 + 'px'; + } + }; + //获取七牛上传token + getToken = async () => { + let response = await getToken(); + if (response && response.success && response.data) { + this.setState({ + token: response.data.token, + domain: response.data.domain, + }); + } + }; + //自定义七牛文件唯一key + getKey = (file) => { + if (!file) return; + let suffix = file.name.match(/\.\w+$/)[0]; + let rand6 = getRandom(); + let time = moment().format('YYYYMMDDHHmmss'); + return time + rand6 + suffix; + }; + //七牛上传额外数据,token和key + getData = (file) => { + const { token } = this.state; + return { + token, + key: this.getKey(file), + }; + }; + + static checkoutFileType = checkoutFileType; + formateFileList = (list = []) => { + const { fileList, domain } = this.state; + return list.map((item, key) => { + //增加自定义回显数据格式 + let value = this.props.setFormat ? this.props.setFormat(item) : item; + //处理上传成功但没保存到后台的数据(onSaveFileList把数据同步到父组件导致子组件重新渲染处理数据不一致) + let file = fileList + .filter((f) => f.status === 'done' && f.response) + .find((f) => { + let url = ''; + if (f.response && f.response.key) { + url = `${domain ? 'https://' + domain : CDN_HOST}/${f.response.key}`; + } + return url === value.value; + }); + if (file) { + return file; + } + //处理保存到后台的数据 + const url = `${item.domain ? 'https://' + item.domain : CDN_HOST}/${item.value}`; + return { + ...value, + uid: value.value, + url, + status: value.status || 'done', + ...checkoutFileType(url), + }; + }); + }; + onSaveFileList = (fileList = []) => { + const { domain } = this.state; + // if (this.preFileList.length > fileList.length) return; + if (this.props.onChange && this.preFileList.length === fileList.length) { + const newList = fileList + .filter((item) => item.value || (item.response || {}).key) + .map((item) => ({ + name: item.name, + value: item.value || (item.response || {}).key, + size: item.size, + url: item.url || `${domain ? 'https://' + domain : CDN_HOST}/${(item.response || {}).key}`, + domain: item.domain || domain, + })); + this.props.onChange(newList); + } + }; + beforeUpload = async (file, fileList) => { + this.preFileList = this.state.fileList.concat([], fileList); + await this.getToken(); + return true; + }; + onChange = async ({ file, fileList, event }) => { + //ant upload组件实时更新上传状态必须实时setState最新文件列表 + let newList = fileList.slice(); + + this.setState({ fileList }, () => { + if (file.status === 'done' && file.response) { + this.onSaveFileList(newList.filter((ls) => ls.status === 'done' || ls.value)); + } + }); + }; + previewFile = (file) => { + return new Promise((res, rej) => { + const obj = checkoutFileType(file.name); + const typeArr = file.name.match(/\.[a-zA-Z]+$/); + const type = typeArr && typeArr[0] ? typeArr[0].replace('.', '') : ''; + if (!['png', 'gif', 'bmp', 'jpg', 'jpeg'].includes(type)) { + res(obj.thumbUrl); + } + }); + }; + onPreview = (file) => { + if (this.model && this.model.onPreview) { + this.model.onPreview(file.url, file.value); + } + }; + onRemove = (file) => { + const fileList = this.state.fileList || []; + const newFileList = fileList.filter((item) => item.uid !== file.uid); + this.preFileList = newFileList; + this.onSaveFileList(newFileList); + this.setState({ fileList: newFileList }); + }; + + render() { + const { btnText, renderButton } = this.props; + const { fileList } = this.state; + const classObj = { + picture: 'upload-list-picture', + text: 'upload-list-text', + }; + return ( +
+ + {renderButton ? ( + renderButton + ) : ( + + )} + + (this.model = dom)} /> + {/* 用户处理hover */} +
+
+ ); + } +} + +UploadCom.Preview = Preview; +export default UploadCom; diff --git a/components/upload/preview.js b/components/upload/preview.js new file mode 100644 index 0000000..b615640 --- /dev/null +++ b/components/upload/preview.js @@ -0,0 +1,49 @@ +import React from 'react'; +import { message, Modal } from 'antd'; +import styles from './detail.less'; + +/* eslint-disable */ +export default class uploadDetail extends React.Component { + state = { + showDialog: false, + type: 'image', + url: '', + }; + onPreview = (url, name) => { + const typeArr = (name || url).match(/\.[a-zA-Z]+$/); + let type = typeArr && typeArr[0] ? typeArr[0].replace('.', '') : ''; + type = type.toLowerCase(); + if (['png', 'gif', 'bmp', 'jpg', 'jpeg', 'heic'].includes(type)) { + this.setState({ showDialog: true, type: 'image', url }); + } else if (['doc', 'docx', 'document', 'xls', 'xlsx', 'ppt', 'pptx'].includes(type)) { + this.setState({ showDialog: true, type: 'office', url }); + } else if (type === 'pdf') { + this.setState({ showDialog: true, type: 'pdf', url }); + } else { + message.warn('暂不支持预览'); + } + }; + handleCancel = () => { + this.setState({ showDialog: false }); + }; + render() { + const { showDialog, type, url } = this.state; + return ( +
+ + {type === 'image' ? ( + example + ) : null} + {type === 'office' ? ( +