浏览代码

feat: Add example-dialog demo

kailong321200875 3 年之前
父节点
当前提交
262f4211cf

+ 41 - 0
mock/table/index.ts

@@ -92,5 +92,46 @@ export default [
         }
       }
     }
+  },
+  // 详情接口
+  {
+    url: '/example/detail',
+    method: 'get',
+    response: ({ query }) => {
+      const { id } = query
+      for (const example of List) {
+        if (example.id === id) {
+          return {
+            code: result_code,
+            data: example
+          }
+        }
+      }
+    }
+  },
+  // 删除接口
+  {
+    url: '/example/delete',
+    method: 'post',
+    response: ({ body }) => {
+      const ids = body.ids
+      if (!ids) {
+        return {
+          code: '500',
+          message: '请选择需要删除的数据'
+        }
+      } else {
+        let i = List.length
+        while (i--) {
+          if (ids.indexOf(List[i].id) !== -1) {
+            List.splice(i, 1)
+          }
+        }
+        return {
+          code: result_code,
+          data: 'success'
+        }
+      }
+    }
   }
 ] as MockMethod[]

二进制
public/favicon.ico


二进制
public/logo.png


+ 23 - 1
src/api/table/index.ts

@@ -10,6 +10,28 @@ export const getTableListApi = ({ params }: AxiosConfig) => {
   }>({ url: '/example/list', method: 'get', params })
 }
 
-export const saveTableApi = ({ data }: AxiosConfig) => {
+export const saveTableApi = ({ data }: AxiosConfig<Recordable, TableData>) => {
   return request({ url: '/example/save', method: 'post', data })
 }
+
+export const getTableDetApi = ({
+  params
+}: AxiosConfig<
+  {
+    id: string
+  },
+  Recordable
+>) => {
+  return request<TableData>({ url: '/example/detail', method: 'get', params })
+}
+
+export const delTableListApi = ({
+  data
+}: AxiosConfig<
+  Recordable,
+  {
+    id: string[] | number[]
+  }
+>) => {
+  return request({ url: '/example/delete', method: 'post', data })
+}

二进制
src/assets/imgs/avatar.png


二进制
src/assets/imgs/logo.png


+ 1 - 1
src/components/Descriptions/src/Descriptions.vue

@@ -100,7 +100,7 @@ const toggleClick = () => {
             </template>
 
             <template #default>
-              <slot :name="item.field">{{ data[item.field] }}</slot>
+              <slot :name="item.field" :row="data">{{ data[item.field] }}</slot>
             </template>
           </ElDescriptionsItem>
         </ElDescriptions>

+ 1 - 0
src/components/Table/index.ts

@@ -3,6 +3,7 @@ import Table from './src/Table.vue'
 export interface TableExpose {
   setProps: (props: Recordable) => void
   setColumn: (columnProps: TableSetPropsType[]) => void
+  selections: Recordable[]
 }
 
 export { Table }

+ 10 - 2
src/components/Table/src/Table.vue

@@ -89,9 +89,16 @@ export default defineComponent({
       }
     }
 
+    const selections = ref<Recordable[]>([])
+
+    const selectionChange = (selection: Recordable[]) => {
+      selections.value = selection
+    }
+
     expose({
       setProps,
-      setColumn
+      setColumn,
+      selections
     })
 
     const pagination = computed(() => {
@@ -226,7 +233,7 @@ export default defineComponent({
                 align={v.align || align}
                 headerAlign={v.headerAlign || headerAlign}
                 label={v.label}
-                width="100px"
+                width="65px"
               ></ElTableColumn>
             )
           } else {
@@ -264,6 +271,7 @@ export default defineComponent({
           // @ts-ignore
           ref={elTableRef}
           data={unref(getProps).data}
+          onSelection-change={selectionChange}
           {...getBindValue}
         >
           {{

+ 62 - 4
src/hooks/web/useTable.ts

@@ -1,12 +1,16 @@
 import { Table, TableExpose } from '@/components/Table'
-import { ElTable } from 'element-plus'
+import { ElTable, ElMessageBox, ElMessage } from 'element-plus'
 import { ref, reactive, watch, computed, unref, nextTick } from 'vue'
 import { AxiosPromise } from 'axios'
 import { get, assign } from 'lodash-es'
 import type { TableProps } from '@/components/Table/src/types'
+import { useI18n } from '@/hooks/web/useI18n'
+
+const { t } = useI18n()
 
 interface UseTableConfig<T, L> {
   getListApi: (option: L) => AxiosPromise<T>
+  delListApi?: (option: AxiosConfig) => AxiosPromise<unknown>
   // 返回数据格式配置
   response: {
     list: string
@@ -22,7 +26,6 @@ interface TableObject<K, L> {
   tableList: K[]
   parmasObj: L
   loading: boolean
-  dialogVisible: boolean
   currentRow: Nullable<K>
 }
 
@@ -42,8 +45,6 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
     parmasObj: {} as L,
     // 加载中
     loading: true,
-    // 弹窗
-    dialogVisible: false,
     // 当前行的数据
     currentRow: null
   })
@@ -95,11 +96,31 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
     return table
   }
 
+  const delData = async (paramsObj: AxiosConfig, ids: string[] | number[]) => {
+    const res = await (config?.delListApi && config?.delListApi(paramsObj))
+    if (res) {
+      ElMessage.success(t('common.delSuccess'))
+
+      // 计算出临界点
+      const currentPage =
+        tableObject.total % tableObject.pageSize === ids.length || tableObject.pageSize === 1
+          ? tableObject.currentPage > 1
+            ? tableObject.currentPage - 1
+            : tableObject.currentPage
+          : tableObject.currentPage
+
+      tableObject.currentPage = currentPage
+      methods.getList()
+    }
+  }
+
   const methods: {
     setProps: (props: Recordable) => void
     getList: () => Promise<void>
     setColumn: (columnProps: TableSetPropsType[]) => void
     setSearchParmas: (data: Recordable) => void
+    getSelections: () => Promise<K[]>
+    delList: (ids: string[] | number[], multiple: boolean, message?: boolean) => Promise<void>
   } = {
     getList: async () => {
       tableObject.loading = true
@@ -122,6 +143,10 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
       const table = await getTable()
       table?.setColumn(columnProps)
     },
+    getSelections: async () => {
+      const table = await getTable()
+      return (table?.selections || []) as K[]
+    },
     // 与Search组件结合
     setSearchParmas: (data: Recordable) => {
       tableObject.currentPage = 1
@@ -133,6 +158,39 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
         }
       })
       methods.getList()
+    },
+    // 删除数据
+    delList: async (ids: string[] | number[], multiple: boolean, message = true) => {
+      const tableRef = await getTable()
+      if (multiple) {
+        if (!tableRef?.selections.length) {
+          ElMessage.warning(t('common.delNoData'))
+          return
+        }
+      } else {
+        if (!tableObject.currentRow) {
+          ElMessage.warning(t('common.delNoData'))
+          return
+        }
+      }
+      const paramsObj: AxiosConfig = {
+        data: {
+          ids
+        }
+      }
+      if (message) {
+        ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), {
+          confirmButtonText: t('common.delOk'),
+          cancelButtonText: t('common.delCancel'),
+          type: 'warning'
+        })
+          .then(async () => {
+            await delData(paramsObj, ids)
+          })
+          .catch(() => {})
+      } else {
+        await delData(paramsObj, ids)
+      }
     }
   }
 

+ 9 - 2
src/locales/en.ts

@@ -33,7 +33,13 @@ export default {
     query: 'Query',
     reset: 'Reset',
     shrink: 'Put away',
-    expand: 'Expand'
+    expand: 'Expand',
+    delMessage: 'Delete the selected data?',
+    delWarning: 'Warning',
+    delOk: 'OK',
+    delCancel: 'Cancel',
+    delNoData: 'Please select the data to delete',
+    delSuccess: 'Deleted successfully'
   },
   setting: {
     projectSetting: 'Project setting',
@@ -374,6 +380,7 @@ export default {
     pageviews: 'Pageviews',
     important: 'Important',
     content: 'Content',
-    save: 'Save'
+    save: 'Save',
+    detail: 'Detail'
   }
 }

+ 9 - 2
src/locales/zh-CN.ts

@@ -33,7 +33,13 @@ export default {
     query: '查询',
     reset: '重置',
     shrink: '收起',
-    expand: '展开'
+    expand: '展开',
+    delMessage: '是否删除所选中数据?',
+    delWarning: '提示',
+    delOk: '确定',
+    delCancel: '取消',
+    delNoData: '请选择需要删除的数据',
+    delSuccess: '删除成功'
   },
   setting: {
     projectSetting: '项目配置',
@@ -371,6 +377,7 @@ export default {
     pageviews: '阅读数',
     important: '重要',
     content: '内容',
-    save: '保存'
+    save: '保存',
+    detail: '详情'
   }
 }

+ 7 - 0
src/store/modules/app.ts

@@ -1,10 +1,13 @@
 import { defineStore } from 'pinia'
 import { store } from '../index'
+import { useCache } from '@/hooks/web/useCache'
 import { appModules } from '@/config/app'
 import type { AppState, LayoutType, ThemeTypes } from '@/config/app'
 import { setCssVar, humpToUnderline } from '@/utils'
 import { ElMessage } from 'element-plus'
 
+const { wsCache } = useCache()
+
 export const useAppStore = defineStore({
   id: 'app',
   state: (): AppState => appModules,
@@ -119,6 +122,7 @@ export const useAppStore = defineStore({
         return
       }
       this.layout = layout
+      wsCache.set('layout', this.layout)
     },
     setTitle(title: string) {
       this.title = title
@@ -132,15 +136,18 @@ export const useAppStore = defineStore({
         document.documentElement.classList.add('light')
         document.documentElement.classList.remove('dark')
       }
+      wsCache.set('isDark', this.isDark)
     },
     setCurrentSize(currentSize: ElememtPlusSzie) {
       this.currentSize = currentSize
+      wsCache.set('currentSize', this.currentSize)
     },
     setMobile(mobile: boolean) {
       this.mobile = mobile
     },
     setTheme(theme: ThemeTypes) {
       this.theme = Object.assign(this.theme, theme)
+      wsCache.set('theme', this.theme)
     },
     setCssVarTheme() {
       for (const key in this.theme) {

+ 4 - 0
src/store/modules/locale.ts

@@ -1,8 +1,11 @@
 import { defineStore } from 'pinia'
 import { store } from '../index'
+import { useCache } from '@/hooks/web/useCache'
 import { localeModules, elLocaleMap } from '@/config/locale'
 import type { LocaleState } from '@/config/locale'
 
+const { wsCache } = useCache()
+
 export const useLocaleStore = defineStore({
   id: 'locales',
   state: (): LocaleState => localeModules,
@@ -22,6 +25,7 @@ export const useLocaleStore = defineStore({
       // this.locale = Object.assign(this.locale, localeMap)
       this.currentLocale.lang = localeMap?.lang
       this.currentLocale.elLocale = elLocaleMap[localeMap?.lang]
+      wsCache.set('lang', localeMap?.lang)
     }
   }
 })

+ 49 - 12
src/views/Example/Dialog/ExampleDialog.vue

@@ -5,11 +5,12 @@ import { Dialog } from '@/components/Dialog'
 import { useI18n } from '@/hooks/web/useI18n'
 import { ElButton, ElTag } from 'element-plus'
 import { Table } from '@/components/Table'
-import { getTableListApi, saveTableApi } from '@/api/table'
+import { getTableListApi, saveTableApi, delTableListApi } from '@/api/table'
 import { useTable } from '@/hooks/web/useTable'
 import { TableData } from '@/api/table/types'
 import { h, reactive, ref, unref } from 'vue'
 import Write from './components/Write.vue'
+import Detail from './components/Detail.vue'
 
 const { register, tableObject, methods } = useTable<
   {
@@ -19,6 +20,7 @@ const { register, tableObject, methods } = useTable<
   TableData
 >({
   getListApi: getTableListApi,
+  delListApi: delTableListApi,
   response: {
     list: 'list',
     total: 'total'
@@ -82,18 +84,43 @@ const columns = reactive<TableColumn[]>([
   },
   {
     field: 'action',
+    width: '260px',
     label: t('tableDemo.action')
   }
 ])
 
+const dialogVisible = ref(false)
+
+const dialogTitle = ref('')
+
 const AddAction = () => {
+  dialogTitle.value = t('exampleDemo.add')
   tableObject.currentRow = null
-  tableObject.dialogVisible = true
+  dialogVisible.value = true
 }
 
-const editAction = (row: TableData) => {
+const delLoading = ref(false)
+
+const delData = async (row: TableData | null, multiple: boolean) => {
   tableObject.currentRow = row
-  tableObject.dialogVisible = true
+  const { delList, getSelections } = methods
+  const selections = await getSelections()
+  delLoading.value = true
+  await delList(
+    multiple ? selections.map((v) => v.id) : [tableObject.currentRow?.id as string],
+    multiple
+  ).finally(() => {
+    delLoading.value = false
+  })
+}
+
+const actionType = ref('')
+
+const action = (row: TableData, type: string) => {
+  dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
+  actionType.value = type
+  tableObject.currentRow = row
+  dialogVisible.value = true
 }
 
 const writeRef = ref<ComponentRef<typeof Write>>()
@@ -105,7 +132,7 @@ const save = async () => {
   const validate = await write?.elFormRef?.validate()?.catch(() => {})
   if (validate) {
     loading.value = true
-    const data = await write?.getFormData()
+    const data = (await write?.getFormData()) as TableData
     const res = await saveTableApi({
       data
     })
@@ -114,7 +141,7 @@ const save = async () => {
         loading.value = false
       })
     if (res) {
-      tableObject.dialogVisible = false
+      dialogVisible.value = false
       tableObject.currentPage = 1
       getList()
     }
@@ -128,7 +155,9 @@ const save = async () => {
 
     <div class="mb-10px">
       <ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton>
-      <ElButton type="danger">{{ t('exampleDemo.del') }}</ElButton>
+      <ElButton :loading="delLoading" type="danger" @click="delData(null, true)">
+        {{ t('exampleDemo.del') }}
+      </ElButton>
     </div>
 
     <Table
@@ -143,21 +172,29 @@ const save = async () => {
       @register="register"
     >
       <template #action="{ row }">
-        <ElButton type="primary" @click="editAction(row)">
+        <ElButton type="primary" @click="action(row, 'edit')">
           {{ t('exampleDemo.edit') }}
         </ElButton>
+        <ElButton type="success" @click="action(row, 'detail')">
+          {{ t('exampleDemo.detail') }}
+        </ElButton>
+        <ElButton type="danger" @click="delData(row, false)">
+          {{ t('exampleDemo.del') }}
+        </ElButton>
       </template>
     </Table>
   </ContentWrap>
 
-  <Dialog v-model="tableObject.dialogVisible" :title="t('exampleDemo.add')">
-    <Write ref="writeRef" :current-row="tableObject.currentRow" />
+  <Dialog v-model="dialogVisible" :title="dialogTitle">
+    <Write v-if="actionType === 'edit'" ref="writeRef" :current-row="tableObject.currentRow" />
+
+    <Detail v-if="actionType === 'detail'" :current-row="tableObject.currentRow" />
 
     <template #footer>
-      <ElButton type="primary" :loading="loading" @click="save">
+      <ElButton v-if="actionType !== 'detail'" type="primary" :loading="loading" @click="save">
         {{ t('exampleDemo.save') }}
       </ElButton>
-      <ElButton @click="tableObject.dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
+      <ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
     </template>
   </Dialog>
 </template>

+ 95 - 0
src/views/Example/Dialog/components/Detail.vue

@@ -0,0 +1,95 @@
+<script setup lang="ts">
+import { getTableDetApi } from '@/api/table'
+import { PropType, watch, ref, reactive } from 'vue'
+import type { TableData } from '@/api/table/types'
+import { Descriptions } from '@/components/Descriptions'
+import { useI18n } from '@/hooks/web/useI18n'
+import { ElTag } from 'element-plus'
+
+const { t } = useI18n()
+
+const props = defineProps({
+  currentRow: {
+    type: Object as PropType<Nullable<TableData>>,
+    default: () => null
+  }
+})
+
+const currentRow = ref<Nullable<TableData>>(null)
+
+const loading = ref(false)
+
+const getTableDet = async () => {
+  loading.value = true
+  const res = await getTableDetApi({
+    params: {
+      id: props.currentRow?.id as string
+    }
+  }).finally(() => {
+    loading.value = false
+  })
+  if (res) {
+    currentRow.value = res.data
+  }
+}
+
+watch(
+  () => props.currentRow,
+  () => {
+    getTableDet()
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+
+const schema = reactive<DescriptionsSchema[]>([
+  {
+    field: 'title',
+    label: t('exampleDemo.title'),
+    span: 24
+  },
+  {
+    field: 'author',
+    label: t('exampleDemo.author')
+  },
+  {
+    field: 'display_time',
+    label: t('exampleDemo.displayTime')
+  },
+  {
+    field: 'importance',
+    label: t('exampleDemo.importance')
+  },
+  {
+    field: 'pageviews',
+    label: t('exampleDemo.pageviews')
+  },
+  {
+    field: 'content',
+    label: t('exampleDemo.content'),
+    span: 24
+  }
+])
+</script>
+
+<template>
+  <Descriptions v-loading="loading" :schema="schema" :data="currentRow || {}">
+    <template #importance="{ row }: { row: TableData }">
+      <ElTag :type="row.importance === 1 ? 'success' : row.importance === 2 ? 'warning' : 'danger'">
+        {{
+          row.importance === 1
+            ? t('tableDemo.important')
+            : row.importance === 2
+            ? t('tableDemo.good')
+            : t('tableDemo.commonly')
+        }}
+      </ElTag>
+    </template>
+
+    <template #content="{ row }: { row: TableData }">
+      <div v-html="row.content"></div>
+    </template>
+  </Descriptions>
+</template>