Commit f831b43dec4d9413f5f0d6e43e51e6ee3b5ebd6b
1 parent
6f8384af
feat: 封装xTable 组件
Showing
8 changed files
with
358 additions
and
0 deletions
yudao-ui-admin-vue3/src/components/XTable/index.ts
0 → 100644
yudao-ui-admin-vue3/src/components/XTable/src/XTable.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <VxeGrid v-bind="getProps" ref="xGrid" :class="`${prefixCls}`" class="xtable-scrollbar"> | ||
| 3 | + <template #[item]="data" v-for="item in Object.keys($slots)" :key="item"> | ||
| 4 | + <slot :name="item" v-bind="data || {}"></slot> | ||
| 5 | + </template> | ||
| 6 | + </VxeGrid> | ||
| 7 | +</template> | ||
| 8 | +<script lang="ts" setup name="XTable"> | ||
| 9 | +import { computed, PropType, ref, unref, useAttrs, watch } from 'vue' | ||
| 10 | +import { SizeType, VxeGridInstance } from 'vxe-table' | ||
| 11 | +import { useAppStore } from '@/store/modules/app' | ||
| 12 | +import { useDesign } from '@/hooks/web/useDesign' | ||
| 13 | +import { XTableProps } from './type' | ||
| 14 | +import { isBoolean, isFunction } from '@/utils/is' | ||
| 15 | + | ||
| 16 | +const appStore = useAppStore() | ||
| 17 | + | ||
| 18 | +const { getPrefixCls } = useDesign() | ||
| 19 | +const prefixCls = getPrefixCls('x-vxe-table') | ||
| 20 | + | ||
| 21 | +const attrs = useAttrs() | ||
| 22 | +const emit = defineEmits(['register']) | ||
| 23 | + | ||
| 24 | +const props = defineProps({ | ||
| 25 | + options: { | ||
| 26 | + type: Object as PropType<XTableProps>, | ||
| 27 | + default: () => {} | ||
| 28 | + } | ||
| 29 | +}) | ||
| 30 | +const innerProps = ref<Partial<XTableProps>>() | ||
| 31 | + | ||
| 32 | +const getProps = computed(() => { | ||
| 33 | + const options = innerProps.value || props.options | ||
| 34 | + options.size = currentSize as any | ||
| 35 | + options.height = 700 | ||
| 36 | + getColumnsConfig(options) | ||
| 37 | + getProxyConfig(options) | ||
| 38 | + getPageConfig(options) | ||
| 39 | + getToolBarConfig(options) | ||
| 40 | + // console.log(options); | ||
| 41 | + return { | ||
| 42 | + ...options, | ||
| 43 | + ...attrs | ||
| 44 | + } | ||
| 45 | +}) | ||
| 46 | + | ||
| 47 | +const xGrid = ref<VxeGridInstance>() // 列表 Grid Ref | ||
| 48 | +watch( | ||
| 49 | + () => appStore.getIsDark, | ||
| 50 | + () => { | ||
| 51 | + if (appStore.getIsDark == true) { | ||
| 52 | + import('./style/dark.scss') | ||
| 53 | + } | ||
| 54 | + if (appStore.getIsDark == false) { | ||
| 55 | + import('./style/light.scss') | ||
| 56 | + } | ||
| 57 | + }, | ||
| 58 | + { immediate: true } | ||
| 59 | +) | ||
| 60 | +const currentSize = computed(() => { | ||
| 61 | + let resSize: SizeType = 'small' | ||
| 62 | + const appsize = appStore.getCurrentSize | ||
| 63 | + switch (appsize) { | ||
| 64 | + case 'large': | ||
| 65 | + resSize = 'medium' | ||
| 66 | + break | ||
| 67 | + case 'default': | ||
| 68 | + resSize = 'small' | ||
| 69 | + break | ||
| 70 | + case 'small': | ||
| 71 | + resSize = 'mini' | ||
| 72 | + break | ||
| 73 | + } | ||
| 74 | + return resSize | ||
| 75 | +}) | ||
| 76 | + | ||
| 77 | +const reload = () => { | ||
| 78 | + const g = unref(xGrid) | ||
| 79 | + if (!g) { | ||
| 80 | + return | ||
| 81 | + } | ||
| 82 | + g.commitProxy('query') | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +const getSearchData = () => { | ||
| 86 | + const g = unref(xGrid) | ||
| 87 | + if (!g) { | ||
| 88 | + return | ||
| 89 | + } | ||
| 90 | + const queryParams = Object.assign({}, JSON.parse(JSON.stringify(g.getProxyInfo()?.form))) | ||
| 91 | + return queryParams | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +let proxyForm = false | ||
| 95 | + | ||
| 96 | +// columns | ||
| 97 | +const getColumnsConfig = (options: XTableProps) => { | ||
| 98 | + const { allSchemas } = options | ||
| 99 | + if (!allSchemas) return | ||
| 100 | + if (allSchemas.printSchema) { | ||
| 101 | + options.printConfig = { | ||
| 102 | + columns: allSchemas.printSchema | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + if (allSchemas.formSchema) { | ||
| 106 | + proxyForm = true | ||
| 107 | + options.formConfig = { | ||
| 108 | + enabled: true, | ||
| 109 | + titleWidth: 100, | ||
| 110 | + titleAlign: 'right', | ||
| 111 | + items: allSchemas.searchSchema | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + if (allSchemas.tableSchema) { | ||
| 115 | + options.columns = allSchemas.tableSchema | ||
| 116 | + } | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +// 动态请求 | ||
| 120 | +const getProxyConfig = (options: XTableProps) => { | ||
| 121 | + const { getListApi, proxyConfig, data } = options | ||
| 122 | + if (proxyConfig || data) return | ||
| 123 | + if (getListApi && isFunction(getListApi)) { | ||
| 124 | + options.proxyConfig = { | ||
| 125 | + seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号) | ||
| 126 | + form: proxyForm, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为 | ||
| 127 | + props: { result: 'list', total: 'total' }, | ||
| 128 | + ajax: { | ||
| 129 | + query: async ({ page, form }) => { | ||
| 130 | + let queryParams: any = Object.assign({}, JSON.parse(JSON.stringify(form))) | ||
| 131 | + if (options.params) { | ||
| 132 | + queryParams = Object.assign(queryParams, options.params) | ||
| 133 | + } | ||
| 134 | + queryParams.pageSize = page.currentPage | ||
| 135 | + queryParams.page = page.pageSize | ||
| 136 | + | ||
| 137 | + return new Promise(async (resolve) => { | ||
| 138 | + resolve(await getListApi(queryParams)) | ||
| 139 | + }) | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +// 分页 | ||
| 147 | +const getPageConfig = (options: XTableProps) => { | ||
| 148 | + const { pagination, pagerConfig } = options | ||
| 149 | + if (pagerConfig) return | ||
| 150 | + if (pagination) { | ||
| 151 | + if (isBoolean(pagination)) { | ||
| 152 | + options.pagerConfig = { | ||
| 153 | + border: false, // 带边框 | ||
| 154 | + background: true, // 带背景颜色 | ||
| 155 | + perfect: false, // 配套的样式 | ||
| 156 | + pageSize: 10, // 每页大小 | ||
| 157 | + pagerCount: 7, // 显示页码按钮的数量 | ||
| 158 | + autoHidden: false, // 当只有一页时自动隐藏 | ||
| 159 | + pageSizes: [5, 10, 20, 30, 50, 100], // 每页大小选项列表 | ||
| 160 | + layouts: [ | ||
| 161 | + 'PrevJump', | ||
| 162 | + 'PrevPage', | ||
| 163 | + 'JumpNumber', | ||
| 164 | + 'NextPage', | ||
| 165 | + 'NextJump', | ||
| 166 | + 'Sizes', | ||
| 167 | + 'FullJump', | ||
| 168 | + 'Total' | ||
| 169 | + ] | ||
| 170 | + } | ||
| 171 | + return | ||
| 172 | + } | ||
| 173 | + options.pagerConfig = pagination | ||
| 174 | + } | ||
| 175 | +} | ||
| 176 | + | ||
| 177 | +// tool bar | ||
| 178 | +const getToolBarConfig = (options: XTableProps) => { | ||
| 179 | + const { toolBar, toolbarConfig } = options | ||
| 180 | + if (toolbarConfig) return | ||
| 181 | + if (toolBar) { | ||
| 182 | + if (!isBoolean(toolBar)) { | ||
| 183 | + options.toolbarConfig = toolBar | ||
| 184 | + return | ||
| 185 | + } | ||
| 186 | + } else { | ||
| 187 | + options.toolbarConfig = { | ||
| 188 | + slots: { buttons: 'toolbar_buttons' } | ||
| 189 | + } | ||
| 190 | + } | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | +const setProps = (prop: Partial<XTableProps>) => { | ||
| 194 | + innerProps.value = { ...unref(innerProps), ...prop } | ||
| 195 | +} | ||
| 196 | + | ||
| 197 | +defineExpose({ reload, Ref: xGrid, getSearchData }) | ||
| 198 | +emit('register', { reload, getSearchData, setProps }) | ||
| 199 | +</script> | ||
| 200 | +<style lang="scss"> | ||
| 201 | +@import './style/index.scss'; | ||
| 202 | +</style> |
yudao-ui-admin-vue3/src/components/XTable/src/style/dark.scss
0 → 100644
| 1 | +// 修改样式变量 | ||
| 2 | +//@import 'vxe-table/styles/variable.scss'; | ||
| 3 | + | ||
| 4 | +/*font*/ | ||
| 5 | +$vxe-font-color: #e5e7eb; | ||
| 6 | +// $vxe-font-size: 14px !default; | ||
| 7 | +// $vxe-font-size-medium: 16px !default; | ||
| 8 | +// $vxe-font-size-small: 14px !default; | ||
| 9 | +// $vxe-font-size-mini: 12px !default; | ||
| 10 | + | ||
| 11 | +/*color*/ | ||
| 12 | +$vxe-primary-color: #409eff !default; | ||
| 13 | +$vxe-success-color: #67c23a !default; | ||
| 14 | +$vxe-info-color: #909399 !default; | ||
| 15 | +$vxe-warning-color: #e6a23c !default; | ||
| 16 | +$vxe-danger-color: #f56c6c !default; | ||
| 17 | +$vxe-disabled-color: #bfbfbf !default; | ||
| 18 | +$vxe-primary-disabled-color: #c0c4cc !default; | ||
| 19 | + | ||
| 20 | +/*loading*/ | ||
| 21 | +$vxe-loading-color: $vxe-primary-color !default; | ||
| 22 | +$vxe-loading-background-color: #1d1e1f !default; | ||
| 23 | +$vxe-loading-z-index: 999 !default; | ||
| 24 | + | ||
| 25 | +/*icon*/ | ||
| 26 | +$vxe-icon-font-family: Verdana, Arial, Tahoma !default; | ||
| 27 | +$vxe-icon-background-color: #e5e7eb !default; | ||
| 28 | + | ||
| 29 | +/*toolbar*/ | ||
| 30 | +$vxe-toolbar-background-color: #1d1e1f !default; | ||
| 31 | +$vxe-toolbar-button-border: #dcdfe6 !default; | ||
| 32 | +$vxe-toolbar-custom-active-background-color: #d9dadb !default; | ||
| 33 | +$vxe-toolbar-panel-background-color: #e5e7eb !default; | ||
| 34 | + | ||
| 35 | +$vxe-table-font-color: #e5e7eb; | ||
| 36 | +$vxe-table-header-background-color: #1d1e1f; | ||
| 37 | +$vxe-table-body-background-color: #141414; | ||
| 38 | +$vxe-table-row-striped-background-color: #1d1d1d; | ||
| 39 | +$vxe-table-row-hover-background-color: #1d1e1f; | ||
| 40 | +$vxe-table-row-hover-striped-background-color: #1e1e1e; | ||
| 41 | +$vxe-table-footer-background-color: #1d1e1f; | ||
| 42 | +$vxe-table-row-current-background-color: #302d2d; | ||
| 43 | +$vxe-table-column-current-background-color: #302d2d; | ||
| 44 | +$vxe-table-column-hover-background-color: #302d2d; | ||
| 45 | +$vxe-table-row-hover-current-background-color: #302d2d; | ||
| 46 | +$vxe-table-row-checkbox-checked-background-color: #3e3c37 !default; | ||
| 47 | +$vxe-table-row-hover-checkbox-checked-background-color: #615a4a !default; | ||
| 48 | +$vxe-table-menu-background-color: #1d1e1f; | ||
| 49 | +$vxe-table-border-width: 1px !default; | ||
| 50 | +$vxe-table-border-color: #4c4d4f !default; | ||
| 51 | +$vxe-table-fixed-left-scrolling-box-shadow: 8px 0px 10px -5px rgba(0, 0, 0, 0.12) !default; | ||
| 52 | +$vxe-table-fixed-right-scrolling-box-shadow: -8px 0px 10px -5px rgba(0, 0, 0, 0.12) !default; | ||
| 53 | + | ||
| 54 | +$vxe-form-background-color: #141414; | ||
| 55 | + | ||
| 56 | +/*pager*/ | ||
| 57 | +$vxe-pager-background-color: #1d1e1f !default; | ||
| 58 | +$vxe-pager-perfect-background-color: #262727 !default; | ||
| 59 | +$vxe-pager-perfect-button-background-color: #a7a3a3 !default; | ||
| 60 | + | ||
| 61 | +$vxe-input-background-color: #141414; | ||
| 62 | +$vxe-input-border-color: #4c4d4f !default; | ||
| 63 | + | ||
| 64 | +$vxe-select-option-hover-background-color: #262626 !default; | ||
| 65 | +$vxe-select-panel-background-color: #141414 !default; | ||
| 66 | +$vxe-select-empty-color: #262626 !default; | ||
| 67 | +$vxe-optgroup-title-color: #909399 !default; | ||
| 68 | + | ||
| 69 | +/*button*/ | ||
| 70 | +$vxe-button-default-background-color: #262626; | ||
| 71 | +$vxe-button-dropdown-panel-background-color: #141414; | ||
| 72 | + | ||
| 73 | +/*modal*/ | ||
| 74 | +$vxe-modal-header-background-color: #141414; | ||
| 75 | +$vxe-modal-body-background-color: #141414; | ||
| 76 | +$vxe-modal-border-color: #3b3b3b; | ||
| 77 | + | ||
| 78 | +/*pulldown*/ | ||
| 79 | +$vxe-pulldown-panel-background-color: #262626 !default; | ||
| 80 | + | ||
| 81 | +@import 'vxe-table/styles/index'; |
yudao-ui-admin-vue3/src/components/XTable/src/style/index.scss
0 → 100644
yudao-ui-admin-vue3/src/components/XTable/src/style/light.scss
0 → 100644
| 1 | +// 修改样式变量 | ||
| 2 | +// /*font*/ | ||
| 3 | +// $vxe-font-size: 12px !default; | ||
| 4 | +// $vxe-font-size-medium: 16px !default; | ||
| 5 | +// $vxe-font-size-small: 14px !default; | ||
| 6 | +// $vxe-font-size-mini: 12px !default; | ||
| 7 | +/*color*/ | ||
| 8 | +$vxe-primary-color: #409eff !default; | ||
| 9 | +$vxe-success-color: #67c23a !default; | ||
| 10 | +$vxe-info-color: #909399 !default; | ||
| 11 | +$vxe-warning-color: #e6a23c !default; | ||
| 12 | +$vxe-danger-color: #f56c6c !default; | ||
| 13 | +$vxe-disabled-color: #bfbfbf !default; | ||
| 14 | +$vxe-primary-disabled-color: #c0c4cc !default; | ||
| 15 | + | ||
| 16 | +@import 'vxe-table/styles/index.scss'; |
yudao-ui-admin-vue3/src/components/XTable/src/type.ts
0 → 100644
| 1 | +import { CrudSchema } from '@/hooks/web/useCrudSchemas' | ||
| 2 | +import type { VxeGridProps, VxeGridPropTypes } from 'vxe-table' | ||
| 3 | + | ||
| 4 | +export type XTableProps<D = any> = VxeGridProps<D> & { | ||
| 5 | + allSchemas?: CrudSchema | ||
| 6 | + getListApi?: Function | ||
| 7 | + deleteApi?: Function | ||
| 8 | + exportListApi?: Function | ||
| 9 | + params?: any | ||
| 10 | + pagination?: boolean | VxeGridPropTypes.PagerConfig | ||
| 11 | + toolBar?: boolean | VxeGridPropTypes.ToolbarConfig | ||
| 12 | + afterFetch?: Function | ||
| 13 | +} | ||
| 14 | +export type XColumns = VxeGridPropTypes.Columns | ||
| 15 | + | ||
| 16 | +export type VxeTableColumn = { | ||
| 17 | + field: string | ||
| 18 | + title?: string | ||
| 19 | + children?: VxeTableColumn[] | ||
| 20 | +} & Recordable |
yudao-ui-admin-vue3/src/components/index.ts
| @@ -4,6 +4,7 @@ import { Form } from '@/components/Form' | @@ -4,6 +4,7 @@ import { Form } from '@/components/Form' | ||
| 4 | import { Table } from '@/components/Table' | 4 | import { Table } from '@/components/Table' |
| 5 | import { Search } from '@/components/Search' | 5 | import { Search } from '@/components/Search' |
| 6 | import { XModal } from '@/components/XModal' | 6 | import { XModal } from '@/components/XModal' |
| 7 | +import { XTable } from '@/components/XTable' | ||
| 7 | import { XButton, XTextButton } from '@/components/XButton' | 8 | import { XButton, XTextButton } from '@/components/XButton' |
| 8 | import { DictTag } from '@/components/DictTag' | 9 | import { DictTag } from '@/components/DictTag' |
| 9 | import { ContentWrap } from '@/components/ContentWrap' | 10 | import { ContentWrap } from '@/components/ContentWrap' |
| @@ -15,6 +16,7 @@ export const setupGlobCom = (app: App<Element>): void => { | @@ -15,6 +16,7 @@ export const setupGlobCom = (app: App<Element>): void => { | ||
| 15 | app.component('Table', Table) | 16 | app.component('Table', Table) |
| 16 | app.component('Search', Search) | 17 | app.component('Search', Search) |
| 17 | app.component('XModal', XModal) | 18 | app.component('XModal', XModal) |
| 19 | + app.component('XTable', XTable) | ||
| 18 | app.component('XButton', XButton) | 20 | app.component('XButton', XButton) |
| 19 | app.component('XTextButton', XTextButton) | 21 | app.component('XTextButton', XTextButton) |
| 20 | app.component('DictTag', DictTag) | 22 | app.component('DictTag', DictTag) |
yudao-ui-admin-vue3/src/hooks/web/useXTable.ts
0 → 100644
| 1 | +import { ref, unref } from 'vue' | ||
| 2 | +import { XTableProps } from '@/components/XTable/src/type' | ||
| 3 | + | ||
| 4 | +export interface tableMethod { | ||
| 5 | + reload: () => void | ||
| 6 | + setProps: (props: XTableProps) => void | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +export function useXTable(props: XTableProps): [Function, tableMethod] { | ||
| 10 | + const tableRef = ref<Nullable<tableMethod>>(null) | ||
| 11 | + | ||
| 12 | + function register(instance) { | ||
| 13 | + tableRef.value = instance | ||
| 14 | + props && instance.setProps(props) | ||
| 15 | + } | ||
| 16 | + function getInstance(): tableMethod { | ||
| 17 | + const table = unref(tableRef) | ||
| 18 | + if (!table) { | ||
| 19 | + console.error('表格实例不存在') | ||
| 20 | + } | ||
| 21 | + return table as tableMethod | ||
| 22 | + } | ||
| 23 | + const methods: tableMethod = { | ||
| 24 | + reload: () => getInstance().reload(), | ||
| 25 | + setProps: (props) => getInstance().setProps(props) | ||
| 26 | + } | ||
| 27 | + return [register, methods] | ||
| 28 | +} |