From c181887f7f0c5eecc9584edfe99e9065440bdc56 Mon Sep 17 00:00:00 2001 From: kailong321200875 <321200875@qq.com> Date: Sun, 23 Jul 2023 12:49:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9EUload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Form/src/helper/componentMap.ts | 6 +- src/components/Form/src/types/index.ts | 17 ++- src/components/Table/src/Table.vue | 80 ++++++------- .../Table/src/components/TableActions.vue | 97 +-------------- src/components/Table/src/types/index.ts | 2 +- src/hooks/web/useTable.ts | 14 +-- src/locales/en.ts | 5 +- src/locales/zh-CN.ts | 4 +- src/views/Components/Form/DefaultForm.vue | 110 +++++++++++++++++- src/views/Components/Table/UseTableDemo.vue | 12 +- 10 files changed, 191 insertions(+), 156 deletions(-) diff --git a/src/components/Form/src/helper/componentMap.ts b/src/components/Form/src/helper/componentMap.ts index 3cb8e10..b40c8ce 100644 --- a/src/components/Form/src/helper/componentMap.ts +++ b/src/components/Form/src/helper/componentMap.ts @@ -17,7 +17,8 @@ import { ElTransfer, ElAutocomplete, ElDivider, - ElTreeSelect + ElTreeSelect, + ElUpload } from 'element-plus' import { InputPassword } from '@/components/InputPassword' import { Editor } from '@/components/Editor' @@ -45,7 +46,8 @@ const componentMap: Recordable = { SelectV2: ElSelectV2, InputPassword: InputPassword, Editor: Editor, - TreeSelect: ElTreeSelect + TreeSelect: ElTreeSelect, + Upload: ElUpload } export { componentMap } diff --git a/src/components/Form/src/types/index.ts b/src/components/Form/src/types/index.ts index 36e6457..3584697 100644 --- a/src/components/Form/src/types/index.ts +++ b/src/components/Form/src/types/index.ts @@ -17,7 +17,8 @@ import { DatePickerProps, FormItemProps as ElFormItemProps, FormProps as ElFormProps, - ISelectProps + ISelectProps, + UploadProps } from 'element-plus' import { IEditorConfig } from '@wangeditor/editor' import { CSSProperties } from 'vue' @@ -51,7 +52,8 @@ export enum ComponentNameEnum { SELECT_V2 = 'SelectV2', INPUT_PASSWORD = 'InputPassword', EDITOR = 'Editor', - TREE_SELECT = 'TreeSelect' + TREE_SELECT = 'TreeSelect', + UPLOAD = 'Upload' } type CamelCaseComponentName = keyof typeof ComponentNameEnum extends infer K @@ -505,6 +507,16 @@ export interface FormItemProps extends Partial { } } +export interface UploadComponentProps extends Partial { + slots?: { + default?: (...args: any[]) => JSX.Element | null + trigger?: (...args: any[]) => JSX.Element | null + tip?: (...args: any[]) => JSX.Element | null + file?: (...args: any[]) => JSX.Element | null + } + style?: CSSProperties +} + export interface TreeSelectComponentProps extends Omit, 'props' | 'on' | 'slots'> { data?: any[] @@ -607,6 +619,7 @@ export interface FormSchema { | TimePickerComponentProps | InputPasswordComponentProps | TreeSelectComponentProps + | UploadComponentProps /** * formItem组件属性,具体可以查看element-plus文档 diff --git a/src/components/Table/src/Table.vue b/src/components/Table/src/Table.vue index f7f153b..6f84765 100644 --- a/src/components/Table/src/Table.vue +++ b/src/components/Table/src/Table.vue @@ -7,7 +7,7 @@ import { ElTooltipProps, ElImage } from 'element-plus' -import { defineComponent, PropType, ref, computed, unref, watch, onMounted, nextTick } from 'vue' +import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue' import { propTypes } from '@/utils/propTypes' import { setIndex } from './helper' import type { TableProps, TableColumn, Pagination, TableSetProps } from './types' @@ -15,8 +15,8 @@ import { set } from 'lodash-es' import { CSSProperties } from 'vue' import { getSlot } from '@/utils/tsxHelper' import TableActions from './components/TableActions.vue' -import Sortable from 'sortablejs' -import { Icon } from '@/components/Icon' +// import Sortable from 'sortablejs' +// import { Icon } from '@/components/Icon' export default defineComponent({ name: 'Table', @@ -62,7 +62,7 @@ export default defineComponent({ type: Array as PropType, default: () => [] }, - sortable: propTypes.bool.def(false), + // sortable: propTypes.bool.def(false), height: propTypes.oneOfType([Number, String]), maxHeight: propTypes.oneOfType([Number, String]), stripe: propTypes.bool.def(false), @@ -213,32 +213,32 @@ export default defineComponent({ return propsObj }) - const sortableEl = ref() + // const sortableEl = ref() // 初始化拖拽 - const initDropTable = () => { - const el = unref(elTableRef)?.$el.querySelector('.el-table__body tbody') - if (!el) return - if (unref(sortableEl)) unref(sortableEl).destroy() + // const initDropTable = () => { + // const el = unref(elTableRef)?.$el.querySelector('.el-table__body tbody') + // if (!el) return + // if (unref(sortableEl)) unref(sortableEl).destroy() - sortableEl.value = Sortable.create(el, { - handle: '.table-move', - animation: 180, - onEnd(e: any) { - emit('sortable-change', e) - } - }) - } + // sortableEl.value = Sortable.create(el, { + // handle: '.table-move', + // animation: 180, + // onEnd(e: any) { + // emit('sortable-change', e) + // } + // }) + // } - watch( - () => getProps.value.sortable, - async (v) => { - await nextTick() - v && initDropTable() - }, - { - immediate: true - } - ) + // watch( + // () => getProps.value.sortable, + // async (v) => { + // await nextTick() + // v && initDropTable() + // }, + // { + // immediate: true + // } + // ) const setProps = (props: TableProps = {}) => { mergeProps.value = Object.assign(unref(mergeProps), props) @@ -495,19 +495,19 @@ export default defineComponent({ tableSlots['append'] = (...args: any[]) => getSlot(slots, 'append', args) } - const { sortable } = unref(getProps) + // const { sortable } = unref(getProps) - const sortableEl = sortable ? ( - - - - ) : null + // const sortableEl = sortable ? ( + // + // + // + // ) : null return (
@@ -520,7 +520,7 @@ export default defineComponent({ ) : null} {{ - default: () => [sortableEl, ...renderTableColumn()], + default: () => renderTableColumn(), ...tableSlots }} diff --git a/src/components/Table/src/components/TableActions.vue b/src/components/Table/src/components/TableActions.vue index 71e3418..647dcd8 100644 --- a/src/components/Table/src/components/TableActions.vue +++ b/src/components/Table/src/components/TableActions.vue @@ -5,16 +5,16 @@ import { ElDropdown, ElDropdownMenu, ElDropdownItem, - ComponentSize, - ElPopover, - ElTree + ComponentSize + // ElPopover, + // ElTree } from 'element-plus' import { Icon } from '@/components/Icon' import { useI18n } from '@/hooks/web/useI18n' import { useAppStore } from '@/store/modules/app' import { TableColumn } from '../types' import { cloneDeep } from 'lodash-es' -import { eachTree } from '@/utils/tree' +// import { eachTree } from '@/utils/tree' const appStore = useAppStore() const sizeMap = computed(() => appStore.sizeMap) @@ -39,16 +39,6 @@ export default defineComponent({ emit('changSize', size) } - const defaultCheckeds = computed(() => { - const checkeds: string[] = [] - eachTree(unref(columns), (item: TableColumn) => { - if (!item.hidden) { - checkeds.push(item.field) - } - }) - return checkeds - }) - const columns = computed(() => { return cloneDeep(props.columns).filter((v) => { // 去掉type为selection的列和expand的列 @@ -68,81 +58,6 @@ export default defineComponent({ } ) - // 在原始树形数据中更新节点位置 - function updateTreeData(treeData, draggedNode, targetNode, placement) { - let updatedTreeData = cloneDeep(treeData) // 克隆一份原始数据以免修改原数据 - - // 找到被拖拽节点和目标节点 - function findNodes(nodeId, nodes) { - for (const node of nodes) { - if (node?.field === nodeId) { - return node - } - if (node.children && node.children.length) { - const foundNode = findNodes(nodeId, node.children) - if (foundNode) { - return foundNode - } - } - } - return null - } - - const draggedNodeToUpdate = findNodes(draggedNode?.data?.field, updatedTreeData) - const targetNodeToUpdate = targetNode - ? findNodes(targetNode?.data?.field, updatedTreeData) - : null - - if (!draggedNodeToUpdate || (targetNode && !targetNodeToUpdate)) { - // 未找到节点,可能是数据错误 - console.error('无法找到要更新的节点或目标节点') - return treeData // 返回原数据,不做任何修改 - } - - // 在原来的位置移除被拖拽节点 - function removeNode(node) { - const parent = updatedTreeData.find((n) => n.children.includes(node)) - if (parent) { - parent.children = parent.children.filter((n) => n?.field !== node?.field) - } else { - updatedTreeData = updatedTreeData.filter((n) => n?.field !== node?.field) - } - } - removeNode(draggedNodeToUpdate) - - // 将被拖拽节点插入目标节点的位置 - function insertNode(placement) { - if (placement === 'before' || placement === 'after') { - const parent = updatedTreeData.find((n) => n.children.includes(targetNodeToUpdate)) - const index = parent.children.findIndex((n) => n?.field === targetNodeToUpdate?.data?.id) - const insertionIndex = placement === 'before' ? index : index + 1 - parent.children.splice(insertionIndex, 0, draggedNodeToUpdate) - } else if (placement === 'inner') { - targetNodeToUpdate.children.push(draggedNodeToUpdate) - } - } - if (targetNode) { - insertNode(placement) - } else { - // 如果 targetNode 为空,将被拖拽节点放置为根节点 - updatedTreeData.push(draggedNodeToUpdate) - } - - return updatedTreeData - } - - const onNodeDragEnd = (before: any, after: any, inner: string) => { - if (inner === 'none') return - console.log(before, after, inner) - const cloneDeepColumns = cloneDeep(unref(props.columns)) - const newColumns = updateTreeData(cloneDeepColumns, after, before, inner) - console.log(newColumns) - } - - const onCheckChange = (data: TableColumn, isChecked: boolean, childrenHasChecked) => { - console.log(data, isChecked, childrenHasChecked) - } - return () => ( <>
@@ -189,7 +104,7 @@ export default defineComponent({ {/* */} - + {/* {{ default: () => { return ( @@ -223,7 +138,7 @@ export default defineComponent({ ) } }} - + */} {/* */}
diff --git a/src/components/Table/src/types/index.ts b/src/components/Table/src/types/index.ts index e0c93f9..ef7ddbf 100644 --- a/src/components/Table/src/types/index.ts +++ b/src/components/Table/src/types/index.ts @@ -18,7 +18,7 @@ export interface TableColumn { minWidth?: string | number fixed?: boolean | 'left' | 'right' renderHeader?: (...args: any[]) => JSX.Element | null - sortable?: boolean | 'custom' + // sortable?: boolean sortMethod?: (...args: any[]) => number sortBy?: string | string[] | ((...args: any[]) => string | string[]) sortOrders?: (string | null)[] diff --git a/src/hooks/web/useTable.ts b/src/hooks/web/useTable.ts index 2cd4184..e9cfad5 100644 --- a/src/hooks/web/useTable.ts +++ b/src/hooks/web/useTable.ts @@ -154,14 +154,14 @@ export const useTable = (config: UseTableConfig) => { refresh: () => { methods.getList() - }, - - sortableChange: (e: any) => { - console.log('sortableChange', e) - const { oldIndex, newIndex } = e - dataList.value.splice(newIndex, 0, dataList.value.splice(oldIndex, 1)[0]) - // to do something } + + // sortableChange: (e: any) => { + // console.log('sortableChange', e) + // const { oldIndex, newIndex } = e + // dataList.value.splice(newIndex, 0, dataList.value.splice(oldIndex, 1)[0]) + // // to do something + // } // // 删除数据 // delList: async (ids: string[] | number[], multiple: boolean, message = true) => { // const tableRef = await getTable() diff --git a/src/locales/en.ts b/src/locales/en.ts index bd646fe..5391274 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -302,7 +302,10 @@ export default { // 自定义节点内容 customContent: 'Custom content', // 懒加载 - lazyLoad: 'Lazy load' + lazyLoad: 'Lazy load', + upload: 'Upload', + // 用户头像 + userAvatar: 'User avatar' }, guideDemo: { guide: 'Guide', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index c1191a9..e459763 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -299,7 +299,9 @@ export default { multiple: '多选', filterable: '可筛选', customContent: '自定义内容', - lazyLoad: '懒加载' + lazyLoad: '懒加载', + upload: '上传', + userAvatar: '用户头像' }, guideDemo: { guide: '引导页', diff --git a/src/views/Components/Form/DefaultForm.vue b/src/views/Components/Form/DefaultForm.vue index d672921..dffcdf3 100644 --- a/src/views/Components/Form/DefaultForm.vue +++ b/src/views/Components/Form/DefaultForm.vue @@ -13,7 +13,10 @@ import { ElRadioButton, ElCheckbox, ElCheckboxButton, - ElInput + ElInput, + ElMessage, + ElMessageBox, + ElIcon } from 'element-plus' import { getDictOneApi } from '@/api/common' import { Icon } from '@/components/Icon' @@ -441,6 +444,8 @@ const treeSelectData = [ let id = 0 +const imageUrl = ref('') + const schema = reactive([ { field: 'field1', @@ -1663,6 +1668,88 @@ const schema = reactive([ }, data: treeSelectData } + }, + { + field: 'field82', + component: 'Divider', + label: `${t('formDemo.upload')}` + }, + { + field: 'field83', + component: 'Upload', + label: `${t('formDemo.default')}`, + componentProps: { + limit: 3, + action: 'https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15', + fileList: [ + { + name: 'element-plus-logo.svg', + url: 'https://element-plus.org/images/element-plus-logo.svg' + }, + { + name: 'element-plus-logo2.svg', + url: 'https://element-plus.org/images/element-plus-logo.svg' + } + ], + multiple: true, + onPreview: (uploadFile) => { + console.log(uploadFile) + }, + onRemove: (file) => { + console.log(file) + }, + beforeRemove: (uploadFile) => { + return ElMessageBox.confirm(`Cancel the transfer of ${uploadFile.name} ?`).then( + () => true, + () => false + ) + }, + onExceed: (files, uploadFiles) => { + ElMessage.warning( + `The limit is 3, you selected ${files.length} files this time, add up to ${ + files.length + uploadFiles.length + } totally` + ) + }, + slots: { + default: () => Click to upload, + tip: () =>
jpg/png files with a size less than 500KB.
+ } + } + }, + { + field: 'field84', + component: 'Upload', + label: `${t('formDemo.userAvatar')}`, + componentProps: { + action: 'https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15', + showFileList: false, + onSuccess: (_response, uploadFile) => { + imageUrl.value = URL.createObjectURL(uploadFile.raw!) + }, + beforeUpload: (rawFile) => { + if (rawFile.type !== 'image/jpeg') { + ElMessage.error('Avatar picture must be JPG format!') + return false + } else if (rawFile.size / 1024 / 1024 > 2) { + ElMessage.error('Avatar picture size can not exceed 2MB!') + return false + } + return true + }, + slots: { + default: () => ( + <> + {imageUrl.value ? : null} + {!imageUrl.value ? ( + + add + + ) : null} + + ) + } + } } ]) @@ -1714,4 +1801,25 @@ const schema = reactive([ padding: 6px 5px; margin-left: 15px; } + +.el-upload { + position: relative; + overflow: hidden; + cursor: pointer; + border: 1px dashed var(--el-border-color); + border-radius: 6px; + transition: var(--el-transition-duration-fast); +} + +.el-upload:hover { + border-color: var(--el-color-primary); +} + +.el-icon.avatar-uploader-icon { + width: 178px; + height: 178px; + font-size: 28px; + color: #8c939d; + text-align: center; +} diff --git a/src/views/Components/Table/UseTableDemo.vue b/src/views/Components/Table/UseTableDemo.vue index 1b03f93..9b78a90 100644 --- a/src/views/Components/Table/UseTableDemo.vue +++ b/src/views/Components/Table/UseTableDemo.vue @@ -21,8 +21,7 @@ const { tableRegister, tableMethods, tableState } = useTable({ } }) const { loading, dataList, total, currentPage, pageSize } = tableState -const { setProps, setColumn, getElTableExpose, addColumn, delColumn, refresh, sortableChange } = - tableMethods +const { setProps, setColumn, getElTableExpose, addColumn, delColumn, refresh } = tableMethods const { t } = useI18n() @@ -214,11 +213,6 @@ const getSelections = async () => { const selections = elTableRef?.getSelectionRows() console.log(selections) } - -const sortable = ref(false) -const showOrHiddenSortable = () => { - sortable.value = !unref(sortable) -}