diff --git a/packages/app/src/components/form.fields/upload/index.tsx b/packages/app/src/components/form.fields/upload/index.tsx index ed97075ef..a998d03db 100644 --- a/packages/app/src/components/form.fields/upload/index.tsx +++ b/packages/app/src/components/form.fields/upload/index.tsx @@ -1,7 +1,6 @@ -import React from 'react' +import React, { useState } from 'react' import { connect } from '@formily/react-schema-renderer' -import { Button, Upload as AntdUpload } from 'antd' -import styled from 'styled-components' +import { Button, Upload as AntdUpload, Popconfirm } from 'antd' import { toArr, isArr, isEqual, mapStyledProps } from '../shared' import { LoadingOutlined, @@ -11,282 +10,59 @@ import { } from '@ant-design/icons' const { Dragger: UploadDragger } = AntdUpload -const exts = [ - { - ext: /\.docx?$/i, - icon: '//img.alicdn.com/tfs/TB1n8jfr1uSBuNjy1XcXXcYjFXa-200-200.png' - }, - { - ext: /\.pptx?$/i, - icon: '//img.alicdn.com/tfs/TB1ItgWr_tYBeNjy1XdXXXXyVXa-200-200.png' - }, - { - ext: /\.jpe?g$/i, - icon: '//img.alicdn.com/tfs/TB1wrT5r9BYBeNjy0FeXXbnmFXa-200-200.png' - }, - { - ext: /\.pdf$/i, - icon: '//img.alicdn.com/tfs/TB1GwD8r9BYBeNjy0FeXXbnmFXa-200-200.png' - }, - { - ext: /\.png$/i, - icon: '//img.alicdn.com/tfs/TB1BHT5r9BYBeNjy0FeXXbnmFXa-200-200.png' - }, - { - ext: /\.eps$/i, - icon: '//img.alicdn.com/tfs/TB1G_iGrVOWBuNjy0FiXXXFxVXa-200-200.png' - }, - { - ext: /\.ai$/i, - icon: '//img.alicdn.com/tfs/TB1B2cVr_tYBeNjy1XdXXXXyVXa-200-200.png' - }, - { - ext: /\.gif$/i, - icon: '//img.alicdn.com/tfs/TB1DTiGrVOWBuNjy0FiXXXFxVXa-200-200.png' - }, - { - ext: /\.svg$/i, - icon: '//img.alicdn.com/tfs/TB1uUm9rY9YBuNjy0FgXXcxcXXa-200-200.png' - }, - { - ext: /\.xlsx?$/i, - icon: '//img.alicdn.com/tfs/TB1any1r1OSBuNjy0FdXXbDnVXa-200-200.png' - }, - { - ext: /\.psd?$/i, - icon: '//img.alicdn.com/tfs/TB1_nu1r1OSBuNjy0FdXXbDnVXa-200-200.png' - }, - { - ext: /\.(wav|aif|aiff|au|mp1|mp2|mp3|ra|rm|ram|mid|rmi)$/i, - icon: '//img.alicdn.com/tfs/TB1jPvwr49YBuNjy0FfXXXIsVXa-200-200.png' - }, - { - ext: /\.(avi|wmv|mpg|mpeg|vob|dat|3gp|mp4|mkv|rm|rmvb|mov|flv)$/i, - icon: '//img.alicdn.com/tfs/TB1FrT5r9BYBeNjy0FeXXbnmFXa-200-200.png' - }, - { - ext: /\.(zip|rar|arj|z|gz|iso|jar|ace|tar|uue|dmg|pkg|lzh|cab)$/i, - icon: '//img.alicdn.com/tfs/TB10jmfr29TBuNjy0FcXXbeiFXa-200-200.png' - }, - { - ext: /\.[^.]+$/i, - icon: '//img.alicdn.com/tfs/TB10.R4r3mTBuNjy1XbXXaMrVXa-200-200.png' +function toFileObject(item) { + if (item.id && item.uid && item.url) { + return item; } -] - -const UploadPlaceholder = styled(props => ( -
- {props.loading ? : } -
上传
-
-))`` - -const testOpts = (ext, options) => { - if (options && isArr(options.include)) { - return options.include.some(url => ext.test(url)) + if (item.response && item.response.data) { + return toFileObject(item.response.data); } - - if (options && isArr(options.exclude)) { - return !options.exclude.some(url => ext.test(url)) - } - - return true + return { + id: item.id, + uid: `${item.id}`, + name: item.title, + url: item.url, + status: 'done', + }; } -const getImageByUrl = (url, options) => { - for (let i = 0; i < exts.length; i++) { - if (exts[i].ext.test(url) && testOpts(exts[i].ext, options)) { - return exts[i].icon || url - } +function toFileList(value: any) { + if (!value) { + return []; } - - return url -} - -const normalizeFileList = fileList => { - if (fileList && fileList.length) { - return fileList.map(file => { - return { - uid: file.uid, - status: file.status, - name: file.name, - url: file.downloadURL || file.imgURL || file.url, - ...file.response, - thumbUrl: file.imgURL || getImageByUrl(file.downloadURL || file.url, { - exclude: ['.png', '.jpg', '.jpeg', '.gif'] - }) - } - }) + if (!Array.isArray(value) && typeof value === 'object') { + value = [value]; } - return [] -} - -const shallowClone = val => { - let result = isArr(val) - ? [...val] - : typeof val === 'object' - ? { ...val } - : val - if (isArr(result)) { - result = result.map(item => ({ - ...item, - // 必须要有一个不重复的uid - uid: - item.uid || - Math.random() - .toFixed(16) - .slice(2, 10) - })) - } - return result -} - -export interface IUploaderState { - value: any[] -} - -// TODO 能不能直接引用 antd 里面的接口定义呢 ? -export declare type UploadListType = 'text' | 'picture' | 'picture-card' - -export interface IUploaderProps { - onChange: (value: any[]) => void - locale: { [name: string]: any } - value: any[] - listType?: UploadListType + return value.map(toFileObject); } export const Upload = connect({ getProps: mapStyledProps -})( - class Uploader extends React.Component { - public static defaultProps = { - action: - 'https://www.easy-mock.com/mock/5b713974309d0d7d107a74a3/alifd/upload', - listType: 'text', - multiple: true, - className: 'antd-uploader' - } - - readonly state: IUploaderState - - constructor(props) { - super(props) - this.state = { - value: shallowClone(toArr(props.value)) +})((props) => { + const { value, onChange } = props; + const [fileList, setFileList] = useState(toFileList(value)); + const uploadProps = { + name: 'file', + action: `${process.env.API}/attachments:upload`, + onChange({ file, fileList }) { + if (file.status === 'done') { + const values = toFileList(fileList); + console.log(values); + setFileList(values); + onChange(values.map(item => item.id)); } - } + }, + }; + return ( +
+ + + +
+ ) +}); - public onRemoveHandler = file => { - const { value } = this.state - const fileList = [] - value.forEach(item => { - if (item.uid !== file.uid) { - fileList.push(item) - } - }) - this.props.onChange(fileList) - } - - public onChangeHandler = ({ fileList }) => { - const { onChange } = this.props - fileList = toArr(fileList) - if ( - fileList.every(file => { - if ( - file.status === 'done' || - file.imgURL || - file.downloadURL || - file.url || - file.thumbUrl - ) - return true - if (file.response) { - if ( - file.response.imgURL || - file.response.downloadURL || - file.response.url || - file.response.thumbUrl - ) - return true - } - return false - }) && - fileList.length - ) { - fileList = normalizeFileList(fileList) - this.setState( - { - value: fileList - }, - () => { - onChange(fileList.length > 0 ? fileList : undefined) - } - ) - } else { - this.setState({ - value: fileList - }) - } - } - - public componentDidUpdate(preProps) { - if (this.props.value && !isEqual(this.props.value, preProps.value)) { - this.setState({ - value: shallowClone(this.props.value) - }) - } - } - - public render() { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { listType, locale, onChange, value, ...others } = this.props - - if (listType.indexOf('card') > -1) { - return ( - - - - ) - } - if (listType.indexOf('dragger') > -1) { - return ( - -1 ? 'picture' : 'text'} - > -

- -

-

拖拽上传

-
- ) - } - return ( - - - - ) - } - } -) - -export default Upload +export default Upload; \ No newline at end of file diff --git a/packages/app/src/components/views/Field/index.tsx b/packages/app/src/components/views/Field/index.tsx index e91c6c599..8231c7d0c 100644 --- a/packages/app/src/components/views/Field/index.tsx +++ b/packages/app/src/components/views/Field/index.tsx @@ -223,6 +223,26 @@ export function LinkToFieldLink(props) { ); } +export function AttachmentField(props: any) { + const { value, schema } = props; + if (!value) { + return null; + } + const values = Array.isArray(value) ? value : [value]; + return ( +
+ {values.map(item => )} +
+ ); +} + +export function AttachmentFieldItem(props: any) { + const { title, url } = props.data || {}; + return ( + {title} + ); +} + registerFieldComponents({ string: StringField, textarea: TextareaField, @@ -241,6 +261,7 @@ registerFieldComponents({ updatedBy: RealtionField, subTable: SubTableField, linkTo: LinkToField, + attachment: AttachmentField, }); export default function Field(props: any) { diff --git a/packages/plugin-collections/src/interfaces/types.ts b/packages/plugin-collections/src/interfaces/types.ts index 84fd8fd6f..c7aa2391e 100644 --- a/packages/plugin-collections/src/interfaces/types.ts +++ b/packages/plugin-collections/src/interfaces/types.ts @@ -109,14 +109,17 @@ export const wysiwyg = { */ export const attachment = { title: '附件', - disabled: true, + // disabled: true, options: { interface: 'attachment', type: 'belongsToMany', filterable: true, target: 'attachments', + // storage: { + // name: 'local', + // }, component: { - type: 'fileManager', + type: 'upload', }, }, }; diff --git a/packages/plugin-file-manager/src/collections/attachments.ts b/packages/plugin-file-manager/src/collections/attachments.ts index 17d405ce8..7fc1180ef 100644 --- a/packages/plugin-file-manager/src/collections/attachments.ts +++ b/packages/plugin-file-manager/src/collections/attachments.ts @@ -4,6 +4,7 @@ export default { name: 'attachments', title: '文件管理器', internal: true, + developerMode: true, fields: [ { comment: '用户文件名(不含扩展名)', diff --git a/packages/plugin-file-manager/src/collections/storages.ts b/packages/plugin-file-manager/src/collections/storages.ts index 5c39dd30f..cb63800ea 100644 --- a/packages/plugin-file-manager/src/collections/storages.ts +++ b/packages/plugin-file-manager/src/collections/storages.ts @@ -4,6 +4,7 @@ export default { name: 'storages', title: '存储引擎', internal: true, + developerMode: true, fields: [ { title: '存储引擎名称', diff --git a/packages/plugin-pages/src/actions/getView.ts b/packages/plugin-pages/src/actions/getView.ts index 749a3ec9a..c318ba37f 100644 --- a/packages/plugin-pages/src/actions/getView.ts +++ b/packages/plugin-pages/src/actions/getView.ts @@ -203,7 +203,7 @@ export default async (ctx, next) => { const viewType = view.get('type'); const actionDefaultParams:any = {}; const appends = fields.filter(field => { - if (!['subTable', 'linkTo'].includes(field.get('interface'))) { + if (!['subTable', 'linkTo', 'attachment'].includes(field.get('interface'))) { return false; } if (viewType === 'table') {