Browse Source

feat: 新增Uload

kailong321200875 1 năm trước cách đây
mục cha
commit
c181887f7f

+ 4 - 2
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<Component, ComponentName> = {
   SelectV2: ElSelectV2,
   InputPassword: InputPassword,
   Editor: Editor,
-  TreeSelect: ElTreeSelect
+  TreeSelect: ElTreeSelect,
+  Upload: ElUpload
 }
 
 export { componentMap }

+ 15 - 2
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<ElFormItemProps> {
   }
 }
 
+export interface UploadComponentProps extends Partial<UploadProps> {
+  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<Partial<SelectComponentProps>, 'props' | 'on' | 'slots'> {
   data?: any[]
@@ -607,6 +619,7 @@ export interface FormSchema {
     | TimePickerComponentProps
     | InputPasswordComponentProps
     | TreeSelectComponentProps
+    | UploadComponentProps
 
   /**
    * formItem组件属性,具体可以查看element-plus文档

+ 42 - 42
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<string[]>,
       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()
-
-      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
-      }
-    )
+    // 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)
+    //     }
+    //   })
+    // }
+
+    // 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 ? (
-        <ElTableColumn
-          className="table-move cursor-move"
-          type="sortable"
-          prop="sortable"
-          width="60px"
-          align="center"
-        >
-          <Icon icon="ant-design:drag-outlined" />
-        </ElTableColumn>
-      ) : null
+      // const sortableEl = sortable ? (
+      //   <ElTableColumn
+      //     className="table-move cursor-move"
+      //     type="sortable"
+      //     prop="sortable"
+      //     width="60px"
+      //     align="center"
+      //   >
+      //     <Icon icon="ant-design:drag-outlined" />
+      //   </ElTableColumn>
+      // ) : null
 
       return (
         <div v-loading={unref(getProps).loading}>
@@ -520,7 +520,7 @@ export default defineComponent({
           ) : null}
           <ElTable ref={elTableRef} data={unref(getProps).data} {...unref(getBindValue)}>
             {{
-              default: () => [sortableEl, ...renderTableColumn()],
+              default: () => renderTableColumn(),
               ...tableSlots
             }}
           </ElTable>

+ 6 - 91
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 () => (
       <>
         <div class="text-right h-28px flex items-center justify-end">
@@ -189,7 +104,7 @@ export default defineComponent({
           </ElTooltip>
 
           {/* <ElTooltip content={t('common.columnSetting')} placement="top"> */}
-          <ElPopover trigger="click" placement="left">
+          {/* <ElPopover trigger="click" placement="left">
             {{
               default: () => {
                 return (
@@ -223,7 +138,7 @@ export default defineComponent({
                 )
               }
             }}
-          </ElPopover>
+          </ElPopover> */}
           {/* </ElTooltip> */}
         </div>
       </>

+ 1 - 1
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)[]

+ 7 - 7
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()

+ 4 - 1
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',

+ 3 - 1
src/locales/zh-CN.ts

@@ -299,7 +299,9 @@ export default {
     multiple: '多选',
     filterable: '可筛选',
     customContent: '自定义内容',
-    lazyLoad: '懒加载'
+    lazyLoad: '懒加载',
+    upload: '上传',
+    userAvatar: '用户头像'
   },
   guideDemo: {
     guide: '引导页',

+ 109 - 1
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<FormSchema[]>([
   {
     field: 'field1',
@@ -1663,6 +1668,88 @@ const schema = reactive<FormSchema[]>([
       },
       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: () => <ElButton type="primary">Click to upload</ElButton>,
+        tip: () => <div class="el-upload__tip">jpg/png files with a size less than 500KB.</div>
+      }
+    }
+  },
+  {
+    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 ? <img src={imageUrl.value} class="avatar" /> : null}
+            {!imageUrl.value ? (
+              <ElIcon class="avatar-uploader-icon" size="large">
+                add
+              </ElIcon>
+            ) : null}
+          </>
+        )
+      }
+    }
   }
 ])
 </script>
@@ -1714,4 +1801,25 @@ const schema = reactive<FormSchema[]>([
   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;
+}
 </style>

+ 2 - 10
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)
-}
 </script>
 
 <template>
@@ -251,7 +245,7 @@ const showOrHiddenSortable = () => {
 
     <ElButton @click="getSelections">{{ t('tableDemo.getSelections') }}</ElButton>
 
-    <ElButton @click="showOrHiddenSortable">{{ t('tableDemo.showOrHiddenSortable') }}</ElButton>
+    <!-- <ElButton @click="showOrHiddenSortable">{{ t('tableDemo.showOrHiddenSortable') }}</ElButton> -->
   </ContentWrap>
   <ContentWrap :title="`UseTable ${t('tableDemo.example')}`">
     <Table
@@ -261,7 +255,6 @@ const showOrHiddenSortable = () => {
       :columns="columns"
       :data="dataList"
       :loading="loading"
-      :sortable="sortable"
       :pagination="
         canShowPagination
           ? {
@@ -271,7 +264,6 @@ const showOrHiddenSortable = () => {
       "
       @register="tableRegister"
       @refresh="refresh"
-      @sortable-change="sortableChange"
     />
   </ContentWrap>
 </template>