Browse Source

feat: 封装xTable 组件

xingyu 2 years ago
parent
commit
f831b43dec

+ 3 - 0
yudao-ui-admin-vue3/src/components/XTable/index.ts

@@ -0,0 +1,3 @@
+import XTable from './src/XTable.vue'
+
+export { XTable }

+ 202 - 0
yudao-ui-admin-vue3/src/components/XTable/src/XTable.vue

@@ -0,0 +1,202 @@
+<template>
+  <VxeGrid v-bind="getProps" ref="xGrid" :class="`${prefixCls}`" class="xtable-scrollbar">
+    <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
+      <slot :name="item" v-bind="data || {}"></slot>
+    </template>
+  </VxeGrid>
+</template>
+<script lang="ts" setup name="XTable">
+import { computed, PropType, ref, unref, useAttrs, watch } from 'vue'
+import { SizeType, VxeGridInstance } from 'vxe-table'
+import { useAppStore } from '@/store/modules/app'
+import { useDesign } from '@/hooks/web/useDesign'
+import { XTableProps } from './type'
+import { isBoolean, isFunction } from '@/utils/is'
+
+const appStore = useAppStore()
+
+const { getPrefixCls } = useDesign()
+const prefixCls = getPrefixCls('x-vxe-table')
+
+const attrs = useAttrs()
+const emit = defineEmits(['register'])
+
+const props = defineProps({
+  options: {
+    type: Object as PropType<XTableProps>,
+    default: () => {}
+  }
+})
+const innerProps = ref<Partial<XTableProps>>()
+
+const getProps = computed(() => {
+  const options = innerProps.value || props.options
+  options.size = currentSize as any
+  options.height = 700
+  getColumnsConfig(options)
+  getProxyConfig(options)
+  getPageConfig(options)
+  getToolBarConfig(options)
+  // console.log(options);
+  return {
+    ...options,
+    ...attrs
+  }
+})
+
+const xGrid = ref<VxeGridInstance>() // 列表 Grid Ref
+watch(
+  () => appStore.getIsDark,
+  () => {
+    if (appStore.getIsDark == true) {
+      import('./style/dark.scss')
+    }
+    if (appStore.getIsDark == false) {
+      import('./style/light.scss')
+    }
+  },
+  { immediate: true }
+)
+const currentSize = computed(() => {
+  let resSize: SizeType = 'small'
+  const appsize = appStore.getCurrentSize
+  switch (appsize) {
+    case 'large':
+      resSize = 'medium'
+      break
+    case 'default':
+      resSize = 'small'
+      break
+    case 'small':
+      resSize = 'mini'
+      break
+  }
+  return resSize
+})
+
+const reload = () => {
+  const g = unref(xGrid)
+  if (!g) {
+    return
+  }
+  g.commitProxy('query')
+}
+
+const getSearchData = () => {
+  const g = unref(xGrid)
+  if (!g) {
+    return
+  }
+  const queryParams = Object.assign({}, JSON.parse(JSON.stringify(g.getProxyInfo()?.form)))
+  return queryParams
+}
+
+let proxyForm = false
+
+// columns
+const getColumnsConfig = (options: XTableProps) => {
+  const { allSchemas } = options
+  if (!allSchemas) return
+  if (allSchemas.printSchema) {
+    options.printConfig = {
+      columns: allSchemas.printSchema
+    }
+  }
+  if (allSchemas.formSchema) {
+    proxyForm = true
+    options.formConfig = {
+      enabled: true,
+      titleWidth: 100,
+      titleAlign: 'right',
+      items: allSchemas.searchSchema
+    }
+  }
+  if (allSchemas.tableSchema) {
+    options.columns = allSchemas.tableSchema
+  }
+}
+
+// 动态请求
+const getProxyConfig = (options: XTableProps) => {
+  const { getListApi, proxyConfig, data } = options
+  if (proxyConfig || data) return
+  if (getListApi && isFunction(getListApi)) {
+    options.proxyConfig = {
+      seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号)
+      form: proxyForm, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为
+      props: { result: 'list', total: 'total' },
+      ajax: {
+        query: async ({ page, form }) => {
+          let queryParams: any = Object.assign({}, JSON.parse(JSON.stringify(form)))
+          if (options.params) {
+            queryParams = Object.assign(queryParams, options.params)
+          }
+          queryParams.pageSize = page.currentPage
+          queryParams.page = page.pageSize
+
+          return new Promise(async (resolve) => {
+            resolve(await getListApi(queryParams))
+          })
+        }
+      }
+    }
+  }
+}
+
+// 分页
+const getPageConfig = (options: XTableProps) => {
+  const { pagination, pagerConfig } = options
+  if (pagerConfig) return
+  if (pagination) {
+    if (isBoolean(pagination)) {
+      options.pagerConfig = {
+        border: false, // 带边框
+        background: true, // 带背景颜色
+        perfect: false, // 配套的样式
+        pageSize: 10, // 每页大小
+        pagerCount: 7, // 显示页码按钮的数量
+        autoHidden: false, // 当只有一页时自动隐藏
+        pageSizes: [5, 10, 20, 30, 50, 100], // 每页大小选项列表
+        layouts: [
+          'PrevJump',
+          'PrevPage',
+          'JumpNumber',
+          'NextPage',
+          'NextJump',
+          'Sizes',
+          'FullJump',
+          'Total'
+        ]
+      }
+      return
+    }
+    options.pagerConfig = pagination
+  }
+}
+
+// tool bar
+const getToolBarConfig = (options: XTableProps) => {
+  const { toolBar, toolbarConfig } = options
+  if (toolbarConfig) return
+  if (toolBar) {
+    if (!isBoolean(toolBar)) {
+      options.toolbarConfig = toolBar
+      return
+    }
+  } else {
+    options.toolbarConfig = {
+      slots: { buttons: 'toolbar_buttons' }
+    }
+  }
+}
+
+const setProps = (prop: Partial<XTableProps>) => {
+  innerProps.value = { ...unref(innerProps), ...prop }
+}
+
+defineExpose({ reload, Ref: xGrid, getSearchData })
+emit('register', { reload, getSearchData, setProps })
+</script>
+<style lang="scss">
+@import './style/index.scss';
+</style>

+ 81 - 0
yudao-ui-admin-vue3/src/components/XTable/src/style/dark.scss

@@ -0,0 +1,81 @@
+// 修改样式变量
+//@import 'vxe-table/styles/variable.scss';
+
+/*font*/
+$vxe-font-color: #e5e7eb;
+// $vxe-font-size: 14px !default;
+// $vxe-font-size-medium: 16px !default;
+// $vxe-font-size-small: 14px !default;
+// $vxe-font-size-mini: 12px !default;
+
+/*color*/
+$vxe-primary-color: #409eff !default;
+$vxe-success-color: #67c23a !default;
+$vxe-info-color: #909399 !default;
+$vxe-warning-color: #e6a23c !default;
+$vxe-danger-color: #f56c6c !default;
+$vxe-disabled-color: #bfbfbf !default;
+$vxe-primary-disabled-color: #c0c4cc !default;
+
+/*loading*/
+$vxe-loading-color: $vxe-primary-color !default;
+$vxe-loading-background-color: #1d1e1f !default;
+$vxe-loading-z-index: 999 !default;
+
+/*icon*/
+$vxe-icon-font-family: Verdana, Arial, Tahoma !default;
+$vxe-icon-background-color: #e5e7eb !default;
+
+/*toolbar*/
+$vxe-toolbar-background-color: #1d1e1f !default;
+$vxe-toolbar-button-border: #dcdfe6 !default;
+$vxe-toolbar-custom-active-background-color: #d9dadb !default;
+$vxe-toolbar-panel-background-color: #e5e7eb !default;
+
+$vxe-table-font-color: #e5e7eb;
+$vxe-table-header-background-color: #1d1e1f;
+$vxe-table-body-background-color: #141414;
+$vxe-table-row-striped-background-color: #1d1d1d;
+$vxe-table-row-hover-background-color: #1d1e1f;
+$vxe-table-row-hover-striped-background-color: #1e1e1e;
+$vxe-table-footer-background-color: #1d1e1f;
+$vxe-table-row-current-background-color: #302d2d;
+$vxe-table-column-current-background-color: #302d2d;
+$vxe-table-column-hover-background-color: #302d2d;
+$vxe-table-row-hover-current-background-color: #302d2d;
+$vxe-table-row-checkbox-checked-background-color: #3e3c37 !default;
+$vxe-table-row-hover-checkbox-checked-background-color: #615a4a !default;
+$vxe-table-menu-background-color: #1d1e1f;
+$vxe-table-border-width: 1px !default;
+$vxe-table-border-color: #4c4d4f !default;
+$vxe-table-fixed-left-scrolling-box-shadow: 8px 0px 10px -5px rgba(0, 0, 0, 0.12) !default;
+$vxe-table-fixed-right-scrolling-box-shadow: -8px 0px 10px -5px rgba(0, 0, 0, 0.12) !default;
+
+$vxe-form-background-color: #141414;
+
+/*pager*/
+$vxe-pager-background-color: #1d1e1f !default;
+$vxe-pager-perfect-background-color: #262727 !default;
+$vxe-pager-perfect-button-background-color: #a7a3a3 !default;
+
+$vxe-input-background-color: #141414;
+$vxe-input-border-color: #4c4d4f !default;
+
+$vxe-select-option-hover-background-color: #262626 !default;
+$vxe-select-panel-background-color: #141414 !default;
+$vxe-select-empty-color: #262626 !default;
+$vxe-optgroup-title-color: #909399 !default;
+
+/*button*/
+$vxe-button-default-background-color: #262626;
+$vxe-button-dropdown-panel-background-color: #141414;
+
+/*modal*/
+$vxe-modal-header-background-color: #141414;
+$vxe-modal-body-background-color: #141414;
+$vxe-modal-border-color: #3b3b3b;
+
+/*pulldown*/
+$vxe-pulldown-panel-background-color: #262626 !default;
+
+@import 'vxe-table/styles/index';

+ 6 - 0
yudao-ui-admin-vue3/src/components/XTable/src/style/index.scss

@@ -0,0 +1,6 @@
+@import 'vxe-table/styles/variable.scss';
+@import 'vxe-table/styles/modules.scss';
+// @import './theme/light.scss';
+i {
+  border-color: initial;
+}

+ 16 - 0
yudao-ui-admin-vue3/src/components/XTable/src/style/light.scss

@@ -0,0 +1,16 @@
+// 修改样式变量
+// /*font*/
+// $vxe-font-size: 12px !default;
+// $vxe-font-size-medium: 16px !default;
+// $vxe-font-size-small: 14px !default;
+// $vxe-font-size-mini: 12px !default;
+/*color*/
+$vxe-primary-color: #409eff !default;
+$vxe-success-color: #67c23a !default;
+$vxe-info-color: #909399 !default;
+$vxe-warning-color: #e6a23c !default;
+$vxe-danger-color: #f56c6c !default;
+$vxe-disabled-color: #bfbfbf !default;
+$vxe-primary-disabled-color: #c0c4cc !default;
+
+@import 'vxe-table/styles/index.scss';

+ 20 - 0
yudao-ui-admin-vue3/src/components/XTable/src/type.ts

@@ -0,0 +1,20 @@
+import { CrudSchema } from '@/hooks/web/useCrudSchemas'
+import type { VxeGridProps, VxeGridPropTypes } from 'vxe-table'
+
+export type XTableProps<D = any> = VxeGridProps<D> & {
+  allSchemas?: CrudSchema
+  getListApi?: Function
+  deleteApi?: Function
+  exportListApi?: Function
+  params?: any
+  pagination?: boolean | VxeGridPropTypes.PagerConfig
+  toolBar?: boolean | VxeGridPropTypes.ToolbarConfig
+  afterFetch?: Function
+}
+export type XColumns = VxeGridPropTypes.Columns
+
+export type VxeTableColumn = {
+  field: string
+  title?: string
+  children?: VxeTableColumn[]
+} & Recordable

+ 2 - 0
yudao-ui-admin-vue3/src/components/index.ts

@@ -4,6 +4,7 @@ import { Form } from '@/components/Form'
 import { Table } from '@/components/Table'
 import { Search } from '@/components/Search'
 import { XModal } from '@/components/XModal'
+import { XTable } from '@/components/XTable'
 import { XButton, XTextButton } from '@/components/XButton'
 import { DictTag } from '@/components/DictTag'
 import { ContentWrap } from '@/components/ContentWrap'
@@ -15,6 +16,7 @@ export const setupGlobCom = (app: App<Element>): void => {
   app.component('Table', Table)
   app.component('Search', Search)
   app.component('XModal', XModal)
+  app.component('XTable', XTable)
   app.component('XButton', XButton)
   app.component('XTextButton', XTextButton)
   app.component('DictTag', DictTag)

+ 28 - 0
yudao-ui-admin-vue3/src/hooks/web/useXTable.ts

@@ -0,0 +1,28 @@
+import { ref, unref } from 'vue'
+import { XTableProps } from '@/components/XTable/src/type'
+
+export interface tableMethod {
+  reload: () => void
+  setProps: (props: XTableProps) => void
+}
+
+export function useXTable(props: XTableProps): [Function, tableMethod] {
+  const tableRef = ref<Nullable<tableMethod>>(null)
+
+  function register(instance) {
+    tableRef.value = instance
+    props && instance.setProps(props)
+  }
+  function getInstance(): tableMethod {
+    const table = unref(tableRef)
+    if (!table) {
+      console.error('表格实例不存在')
+    }
+    return table as tableMethod
+  }
+  const methods: tableMethod = {
+    reload: () => getInstance().reload(),
+    setProps: (props) => getInstance().setProps(props)
+  }
+  return [register, methods]
+}