feat: add crmTable

This commit is contained in:
teukkk
2025-01-07 19:53:08 +08:00
committed by Craftsman
parent 22454da564
commit 01b404f41e
18 changed files with 957 additions and 6 deletions

View File

@@ -0,0 +1,11 @@
export enum TableKeyEnum {
SYSTEM_USER = 'systemUser', // TODO lmy 没用 可删
}
// 具有特殊功能的列
export enum SpecialColumnEnum {
// 选择框
SELECTION = 'selection',
// 操作列
OPERATION = 'operation',
}

View File

@@ -0,0 +1,28 @@
import { sortBy } from 'lodash-es';
/**
* 比较两个一维数组对象是否相等,不考虑顺序,
* @param arr1 数组1
* @param arr2 数组2
* @returns boolean
*/
export function isArraysEqualWithOrder<T>(arr1: T[], arr2: T[]): boolean {
if (arr1.length !== arr2.length) {
return false;
}
const sortArr1 = sortBy(arr1, 'dataIndex');
const sortArr2 = sortBy(arr2, 'dataIndex');
for (let i = 0; i < sortArr1.length; i++) {
const obj1 = sortArr1[i];
const obj2 = sortArr2[i];
// 逐一比较对象
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
return false;
}
}
return true;
}
export default {};

View File

@@ -5,3 +5,26 @@ export default interface CommonResponse<T> {
messageDetail: string;
data: T;
}
// 表格查询
export interface TableQueryParams {
// 当前页
current?: number;
// 每页条数
pageSize?: number;
// 排序仅针对单个字段
sort?: object;
// 表头筛选
filter?: object;
// 查询条件
keyword?: string;
[key: string]: any;
}
export interface CommonList<T> {
[x: string]: any;
pageSize: number;
total: number;
current: number;
list: T[];
}

View File

@@ -23,7 +23,8 @@
"@vicons/ionicons5": "^0.13.0",
"naive-ui": "^2.40.4",
"nprogress": "^0.2.0",
"vfonts": "^0.0.3"
"vfonts": "^0.0.3",
"vue-draggable-plus": "^0.6.0"
},
"devDependencies": {
"@vitejs/plugin-legacy": "^6.0.0",

View File

@@ -1,6 +1,8 @@
<template>
<n-config-provider :theme-overrides="themeOverridesConfig">
<RouterView />
<Suspense>
<RouterView />
</Suspense>
</n-config-provider>
</template>

View File

@@ -0,0 +1,84 @@
<template>
<n-popover trigger="click" placement="bottom-end" @update:show="handleUpdateShow">
<template #trigger>
<CrmIcon type="icon-icon-setting" class="cursor-pointer" />
</template>
<div class="flex w-[175px] items-center justify-between text-[12px]">
<div class="font-medium text-[var(--text-n1)]">{{ t('crmTable.columnSetting.tableHeaderDisplaySettings') }}</div>
<n-button text size="tiny" :disabled="!hasChange" @click="handleReset">
{{ t('crmTable.columnSetting.resetDefault') }}
</n-button>
</div>
<VueDraggable v-model="cachedColumns" handle=".sort-handle" @change="handleChange">
<div
v-for="element in cachedColumns"
:key="element.key"
class="flex w-[175px] items-center justify-between py-[6px]"
>
<div class="flex items-center">
<CrmIcon type="icon-icon_drag" class="sort-handle cursor-move text-[var(--text-n4)]" :size="12" />
<span class="one-line-text ml-[8px] text-[12px]">
{{ t(element.title as string) }}
</span>
</div>
<n-switch v-model:value="element.showInTable" size="small" @update:value="handleChange" />
</div>
</VueDraggable>
</n-popover>
</template>
<script setup lang="ts">
import { NButton, NPopover, NSwitch } from 'naive-ui';
import type { CrmDataTableColumn } from '@/components/pure/crm-table/type';
import { useI18n } from '@/hooks/useI18n';
import { useTableStore } from '@/store';
import type { TableKeyEnum } from '@lib/shared/enums/tableEnum';
import { VueDraggable } from 'vue-draggable-plus';
const props = defineProps<{
tableKey: TableKeyEnum;
}>();
const emit = defineEmits<{
(e: 'changeColumnsSetting'): void; // 数据发生变化
}>();
const { t } = useI18n();
const tableStore = useTableStore();
const hasChange = ref(false); // 是否有改动
const cachedColumns = ref<CrmDataTableColumn[]>([]);
async function getCachedColumns() {
const columns = await tableStore.getCanSetColumns(props.tableKey);
cachedColumns.value = columns;
}
onBeforeMount(() => {
if (props.tableKey) {
getCachedColumns();
}
});
function handleReset() {
getCachedColumns();
hasChange.value = false;
}
function handleChange() {
hasChange.value = true;
}
async function handleUpdateShow(show: boolean) {
if (!show) {
if (hasChange.value) {
await tableStore.setColumns(props.tableKey, [...cachedColumns.value]);
emit('changeColumnsSetting');
hasChange.value = false;
}
}
}
</script>

View File

@@ -0,0 +1,115 @@
<template>
<n-data-table
v-bind="{ ...$attrs }"
v-model:checked-row-keys="checkedRowKeys"
:columns="currentColumns"
:row-key="getRowKey"
@update:sorter="handleSorterChange"
@update:filters="handleFiltersChange"
@update:checked-row-keys="handleCheck"
@update:page="handlePageChange"
@update:page-size="handlePageSizeChange"
/>
</template>
<script lang="ts" setup>
import { NDataTable } from 'naive-ui';
import { cloneDeep } from 'lodash-es';
import type { CrmDataTableColumn } from '@/components/pure/crm-table/type';
import ColumnSetting from './components/columnSetting.vue';
import { useI18n } from '@/hooks/useI18n';
import { useTableStore } from '@/store';
import { SpecialColumnEnum, TableKeyEnum } from '@lib/shared/enums/tableEnum';
import type { DataTableFilterState, DataTableRowKey, DataTableSortState } from 'naive-ui';
const props = defineProps<{
columns: CrmDataTableColumn[];
tableRowKey?: string;
}>();
const emit = defineEmits<{
(e: 'pageChange', value: number): void;
(e: 'pageSizeChange', value: number): void;
(e: 'sorterChange', value: { [key: string]: string }): void;
(e: 'filterChange', value: DataTableFilterState): void;
}>();
const attrs = useAttrs();
const { t } = useI18n();
const tableStore = useTableStore();
const checkedRowKeys = defineModel<DataTableRowKey[]>('checkedRowKeys', { default: [] });
const currentColumns = ref<CrmDataTableColumn[]>([]);
// TODO lmy 设置列
async function initColumn() {
let columns = cloneDeep(props.columns);
if (attrs.showSetting) {
columns = await tableStore.getShowInTableColumns(attrs.tableKey as TableKeyEnum);
currentColumns.value = columns.map((column) => {
// 操作列
if (column.key === SpecialColumnEnum.OPERATION) {
return {
...column,
title() {
const children = [h('div', t('common.operation'))];
if (attrs.showSetting) {
children.push(
h(ColumnSetting, {
tableKey: attrs.tableKey as TableKeyEnum,
onChangeColumnsSetting: () => {
initColumn();
},
})
);
}
return h('div', { class: 'flex items-center gap-[8px]' }, children);
},
};
}
return column;
});
} else {
currentColumns.value = columns;
}
}
watch(
() => props.columns,
() => {
initColumn();
},
{ immediate: true }
);
function getRowKey(rowData: Record<string, any>) {
return props.tableRowKey ? rowData[props.tableRowKey] : rowData.id;
}
function handlePageChange(page: number) {
emit('pageChange', page);
}
function handlePageSizeChange(pageSize: number) {
emit('pageSizeChange', pageSize);
}
function handleSorterChange(sorter: DataTableSortState) {
let sortOrder = '';
if (sorter.order === 'ascend') {
sortOrder = 'asc';
} else if (sorter.order === 'descend') {
sortOrder = 'desc';
}
emit('sorterChange', !sorter.order ? {} : { [sorter.columnKey]: sortOrder });
}
function handleFiltersChange(filters: DataTableFilterState) {
emit('filterChange', filters);
}
function handleCheck(rowKeys: DataTableRowKey[]) {
console.log('🤔️ => handleCheck', rowKeys);
}
</script>

View File

@@ -0,0 +1,4 @@
export default {
'crmTable.columnSetting.tableHeaderDisplaySettings': 'Table header display settings',
'crmTable.columnSetting.resetDefault': 'Restore default',
};

View File

@@ -0,0 +1,4 @@
export default {
'crmTable.columnSetting.tableHeaderDisplaySettings': '表头显示设置',
'crmTable.columnSetting.resetDefault': '恢复默认',
};

View File

@@ -0,0 +1,33 @@
import { VNodeChild } from 'vue';
import type { TableKeyEnum } from '@lib/shared/enums/tableEnum';
import type { DataTableColumn, DataTableColumnKey, DataTableProps, DataTableRowData, DataTableRowKey } from 'naive-ui';
export type CrmTableDataItem<T> = T & {
updateTime?: string | number | null;
createTime?: string | number | null;
children?: CrmTableDataItem<T>[];
} & DataTableRowData;
export type CrmDataTableColumn = DataTableColumn & {
showInTable?: boolean; // 是否展示在表格上
key?: DataTableColumnKey; // 这一列的 key不可重复
title?: string | (() => VNodeChild);
};
export interface CrmTableProps<T> extends DataTableProps {
'columns': CrmDataTableColumn[];
'tableKey'?: TableKeyEnum; // 表格key, 用于存储表格列配置,pageSize等
'tableRowKey'?: string; // 表格行的key
'data': CrmTableDataItem<T>[];
'showSetting'?: boolean; // 是否显示表格配置
'showPagination'?: boolean; // 是否显示分页
'onUpdate:checkedRowKeys'?: (key: DataTableRowKey[]) => void; // 覆写类型防止报错
}
// 表格存储
export interface TableStorageConfigItem {
column: CrmDataTableColumn[]; // 列配置
pageSize?: number;
columnBackup: CrmDataTableColumn[]; // 列配置的备份,用于比较当前定义的列配置是否和备份的列配置相同
}

View File

@@ -0,0 +1,169 @@
import { UnwrapRef } from 'vue';
import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs';
import { useAppStore, useTableStore } from '@/store';
import type { CrmTableDataItem, CrmTableProps } from './type';
import type { CommonList, TableQueryParams } from '@lib/shared/models/common';
import type { DataTableFilterState, PaginationProps } from 'naive-ui';
const tableStore = useTableStore();
const appStore = useAppStore();
export default function useTable<T>(
loadListFunc?: (v?: TableQueryParams | any) => Promise<CommonList<CrmTableDataItem<T>> | CrmTableDataItem<T>>,
props?: Partial<CrmTableProps<T>>
) {
const defaultProps: CrmTableProps<T> = {
bordered: false,
loading: false, // 加载效果
data: [], // 表格数据
columns: [],
tableRowKey: 'id', // 表格行的key
pagination: {
page: 1,
itemCount: 0,
pageSize: appStore.pageSize,
pageSizes: appStore.pageSizes,
showSizePicker: appStore.showSizePicker,
showQuickJumper: appStore.showQuickJumper,
}, // false | PaginationProps; false表示不分页
...props,
};
const propsRes = ref<CrmTableProps<T>>(cloneDeep(defaultProps));
// 如果表格设置了tableKey设置缓存的分页大小
if (propsRes.value.pagination && typeof propsRes.value.pagination === 'object' && propsRes.value.tableKey) {
tableStore.getPageSize(propsRes.value.tableKey).then((res) => {
if (propsRes.value.pagination && res) {
propsRes.value.pagination.pageSize = res;
}
});
}
// 加载效果
function setLoading(status: boolean) {
propsRes.value.loading = status;
}
// 设置请求参数
const loadListParams = ref<TableQueryParams>({});
function setLoadListParams(params?: TableQueryParams) {
loadListParams.value = params || {};
}
// 获取分页参数
async function getPaginationParams() {
const { page, pageSize } = propsRes.value.pagination as PaginationProps;
if (propsRes.value.tableKey) {
const cachedPageSize = await tableStore.getPageSize(propsRes.value.tableKey);
return { current: page, pageSize: cachedPageSize };
}
return { current: page, pageSize };
}
/**
* 分页设置
* @param page 当前页
* @param total 总页数
*/
function setPagination(page: number, total?: number) {
if (propsRes.value.pagination && typeof propsRes.value.pagination === 'object') {
propsRes.value.pagination.page = page;
if (total !== undefined) {
propsRes.value.pagination.itemCount = total;
}
}
}
const tableQueryParams = ref<TableQueryParams>({}); // 表格请求参数集合
const keyword = ref('');
const sortItem = ref<Record<string, any>>({}); // 排序
const filterItem = ref<Record<string, any>>({}); // 筛选
function processRecordItem(item: CrmTableDataItem<T>): CrmTableDataItem<T> {
if (item.updateTime) {
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');
}
if (item.createTime) {
item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss');
}
return item;
}
async function loadList() {
if (!loadListFunc) return;
setLoading(true);
try {
tableQueryParams.value = {
...(!propsRes.value.pagination ? {} : await getPaginationParams()),
keyword: keyword.value,
sort: sortItem.value,
...loadListParams.value,
filter: filterItem.value,
};
const data = await loadListFunc(tableQueryParams.value);
if (!propsRes.value.pagination && Array.isArray(data)) {
propsRes.value.data = data.map((item: CrmTableDataItem<T>) => {
return processRecordItem(item);
}) as unknown as UnwrapRef<CrmTableDataItem<T>[]>;
} else {
const tmpArr = data as CommonList<CrmTableDataItem<T>>;
propsRes.value.data = tmpArr.list.map((item: CrmTableDataItem<T>) => {
return processRecordItem(item);
}) as unknown as UnwrapRef<CrmTableDataItem<T>[]>;
// 设置分页
setPagination(tmpArr.current, tmpArr.total);
}
} catch (error) {
propsRes.value.data = [];
// eslint-disable-next-line no-console
console.error(error);
throw error;
} finally {
setLoading(false);
}
}
// 事件触发组
const propsEvent = ref({
// 分页触发
pageChange: async (page: number) => {
setPagination(page);
await loadList();
},
// 修改每页显示条数触发
pageSizeChange: async (pageSize: number) => {
if (propsRes.value.pagination && typeof propsRes.value.pagination === 'object') {
propsRes.value.pagination.pageSize = pageSize;
// 如果表格设置了tableKey缓存分页大小
if (propsRes.value.tableKey) {
await tableStore.setPageSize(propsRes.value.tableKey, pageSize);
}
}
loadList();
},
// 排序触发
sorterChange: (sortObj: { [key: string]: string }) => {
sortItem.value = sortObj;
loadList();
},
// 筛选触发
filterChange: (filters: DataTableFilterState) => {
filterItem.value = { ...filters };
loadList();
},
});
return {
propsRes,
propsEvent,
setLoading,
setLoadListParams,
loadList,
setPagination,
};
}

View File

@@ -0,0 +1,109 @@
import localforage from 'localforage';
import useAppStore from '@/store/modules/app';
export default function useLocalForage() {
const appStore = useAppStore();
/**
* 检测并序列化函数
* @param val 要存储的值
*/
const serializeValue = (val: any): any => {
if (typeof val === 'function') {
return `function:${val.toString()}`;
}
if (val && typeof val === 'object' && !Array.isArray(val)) {
const newVal = { ...val };
Object.keys(newVal).forEach((key) => {
newVal[key] = serializeValue(newVal[key]);
});
return newVal;
}
if (Array.isArray(val)) {
return val.map((item) => serializeValue(item));
}
return val;
};
const deserializeFunction = (funcStr: string) => {
try {
if (!funcStr.trim().startsWith('function') && !funcStr.trim().startsWith('(')) {
funcStr = `function ${funcStr}`;
}
// eslint-disable-next-line no-eval
const func = eval(`(${funcStr})`);
return func;
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
return null;
}
};
/**
* 反序列化值,将特殊格式字符串转换回函数
* @param val 从存储中读取的值
*/
const deserializeValue = <T>(val: any): T | null => {
if (typeof val === 'string' && val.startsWith('function:')) {
return deserializeFunction(val.slice(9)) as T;
}
if (val && typeof val === 'object' && !Array.isArray(val)) {
const newVal = { ...val };
Object.keys(newVal).forEach((key) => {
newVal[key] = deserializeValue(newVal[key]);
});
return newVal as T;
}
if (Array.isArray(val)) {
return val.map((item) => deserializeValue(item) as T) as T;
}
return val;
};
/**
* 读取本地存储的数据
* @param key 唯一 key
* @param notIsolatedByProject 存储数据时是否不按项目隔离数据
*/
const getItem = async <T>(key: string, notIsolatedByProject = false): Promise<T | null> => {
const itemKey = notIsolatedByProject ? key : `${appStore.currentProjectId}-${key}`;
try {
const res = await localforage.getItem<T>(itemKey);
if (!res) {
return null;
}
return deserializeValue<T>(res);
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
return null;
}
};
/**
* 永久存储数据
* @param key 唯一 key
* @param val 存储的值
* @param notIsolatedByProject 是否不按项目隔离数据
*/
const setItem = async (
key: string,
val: string | number | boolean | Record<string, any>,
notIsolatedByProject = false
) => {
try {
const itemKey = notIsolatedByProject ? key : `${appStore.currentProjectId}-${key}`;
await localforage.setItem(itemKey, serializeValue(val));
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
};
return {
getItem,
setItem,
};
}

View File

@@ -0,0 +1,135 @@
import { cloneDeep } from 'lodash-es';
import type { CrmDataTableColumn, TableStorageConfigItem } from '@/components/pure/crm-table/type';
import { useAppStore } from '@/store';
import useLocalForage from './useLocalForage';
import { SpecialColumnEnum, TableKeyEnum } from '@lib/shared/enums/tableEnum';
import { isArraysEqualWithOrder } from '@lib/shared/method/equal';
export default function useTableStore() {
const { getItem, setItem } = useLocalForage();
const appStore = useAppStore();
async function getTableColumnsMap(tableKey: TableKeyEnum): Promise<TableStorageConfigItem | null> {
const isSystemOrOrgKey = tableKey.startsWith('SYSTEM') || tableKey.startsWith('ORGANIZATION');
const tableColumnsMap = await getItem<TableStorageConfigItem>(tableKey, isSystemOrOrgKey);
return tableColumnsMap;
}
async function setTableColumnsMap(tableKey: TableKeyEnum, tableColumnsMap: TableStorageConfigItem) {
const isSystemOrOrgKey = tableKey.startsWith('SYSTEM') || tableKey.startsWith('ORGANIZATION');
await setItem(tableKey, tableColumnsMap, isSystemOrOrgKey);
}
function columnsTransform(columns: CrmDataTableColumn[]) {
columns.forEach((item) => {
if (item.showInTable === undefined) {
// 默认在表格中展示
item.showInTable = true;
}
});
return columns;
}
async function initColumn(tableKey: TableKeyEnum, column: CrmDataTableColumn[]) {
try {
const tableColumnsMap = await getTableColumnsMap(tableKey);
if (!tableColumnsMap) {
// 如果没有在indexDB里初始化
column = columnsTransform(column);
setTableColumnsMap(tableKey, {
column,
columnBackup: cloneDeep(column),
});
} else {
// 初始化过了,但是可能有新变动,如列的顺序,列的显示隐藏,列的拖拽
column = columnsTransform(column);
const { columnBackup: oldColumn } = tableColumnsMap;
// 比较页面上定义的 column 和 浏览器备份的column 是否相同
const isEqual = isArraysEqualWithOrder(oldColumn, column);
if (!isEqual) {
column.forEach((col) => {
const storedCol = tableColumnsMap.column.find((sc) => sc.key === col.key);
if (storedCol) {
col.width = storedCol.width; // 使用上一次拖拽存储的宽度,避免组件里边使用时候初始化到最初的列宽
}
});
// 如果不相等说明有变动将新的column存入indexDB
setTableColumnsMap(tableKey, {
column,
columnBackup: cloneDeep(column),
});
}
}
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
}
// 表头显示设置的列
async function getCanSetColumns(tableKey: TableKeyEnum) {
const tableColumnsMap = await getTableColumnsMap(tableKey);
if (tableColumnsMap) {
return tableColumnsMap.column.filter(
(item) => item.key !== SpecialColumnEnum.OPERATION && item.type !== SpecialColumnEnum.SELECTION
);
}
return [];
}
// 在表格上展示的列
async function getShowInTableColumns(tableKey: TableKeyEnum) {
const tableColumnsMap = await getTableColumnsMap(tableKey);
if (tableColumnsMap) {
return tableColumnsMap.column.filter((i) => i.showInTable);
}
return [];
}
async function setColumns(tableKey: TableKeyEnum, columns: CrmDataTableColumn[]) {
try {
const tableColumnsMap = await getTableColumnsMap(tableKey);
if (tableColumnsMap) {
const operationColumn = tableColumnsMap.column.find((i) => i.key === SpecialColumnEnum.OPERATION);
const selectColumn = tableColumnsMap.column.find((i) => i.type === SpecialColumnEnum.SELECTION);
if (selectColumn) columns.unshift(selectColumn); // 加上选择框列
if (operationColumn) columns.push(operationColumn); // 加上操作列
tableColumnsMap.column = cloneDeep(columns);
await setTableColumnsMap(tableKey, tableColumnsMap);
}
} catch (e) {
// eslint-disable-next-line no-console
console.error('tableStore.setColumns', e);
}
}
async function getPageSize(tableKey: TableKeyEnum) {
const tableColumnsMap = await getTableColumnsMap(tableKey);
return tableColumnsMap ? tableColumnsMap.pageSize : appStore.pageSize;
}
async function setPageSize(tableKey: TableKeyEnum, pageSize: number): Promise<void> {
try {
const tableColumnsMap = await getTableColumnsMap(tableKey);
if (tableColumnsMap) {
tableColumnsMap.pageSize = pageSize;
await setTableColumnsMap(tableKey, tableColumnsMap);
}
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
}
return {
initColumn,
getCanSetColumns,
setColumns,
getShowInTableColumns,
setPageSize,
getPageSize,
};
}

View File

@@ -1,5 +1,4 @@
import { createApp } from 'vue';
import { createPinia } from 'pinia';
// TODO 国际化接口对接
// import localforage from 'localforage';
@@ -11,11 +10,12 @@ import App from './App.vue';
import { setupI18n } from './locale';
import useLocale from './locale/useLocale';
import router from './router';
import store from './store';
async function setupApp() {
const app = createApp(App);
app.use(createPinia());
app.use(store);
// 注册国际化,需要异步阻塞,确保语言包加载完毕
await setupI18n(app);

View File

@@ -1,7 +1,13 @@
import { createPinia } from 'pinia';
import useTableStore from '@/hooks/useTableStore';
import useAppStore from './modules/app';
import { debouncePlugin } from './plugins';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia().use(debouncePlugin).use(piniaPluginPersistedstate);
export { useAppStore, useTableStore };
export default pinia;

View File

@@ -1,12 +1,25 @@
import { defineStore } from 'pinia';
import type { PaginationSizeOption } from 'naive-ui';
export interface AppState {
currentProjectId: string;
menuCollapsed: boolean;
pageSize: number;
showSizePicker: boolean;
showQuickJumper: boolean;
pageSizes: Array<number | PaginationSizeOption>;
}
const useAppStore = defineStore('app', {
state: (): AppState => ({
currentProjectId: '',
menuCollapsed: false,
// 分页
pageSize: 10,
showSizePicker: true,
showQuickJumper: true,
pageSizes: [10, 20, 30, 40, 50],
}),
getters: {
getMenuCollapsed(state: AppState) {
@@ -18,6 +31,9 @@ const useAppStore = defineStore('app', {
this.menuCollapsed = collapsed;
},
},
persist: {
paths: ['currentProjectId'],
},
});
export default useAppStore;

View File

@@ -36,15 +36,18 @@
</div>
</div>
</CrmCard>
<TableDemo class="my-[16px]" />
</div>
</template>
<script setup lang="ts">
import { NAlert, NButton } from 'naive-ui';
import CrmCard from '@/components/pure/crm-card/index.vue';
import TableDemo from './TableDemo.vue';
import useDiscreteApi from '@/hooks/useDiscreteApi';
import { NAlert, NButton } from 'naive-ui';
// 暂时提供参考 you can delete it ^_^
const { message, notification, dialog } = useDiscreteApi();

View File

@@ -0,0 +1,208 @@
<template>
<CrmTable
v-bind="propsRes"
@page-change="propsEvent.pageChange"
@page-size-change="propsEvent.pageSizeChange"
@sorter-change="propsEvent.sorterChange"
@filter-change="propsEvent.filterChange"
/>
</template>
<script setup lang="ts">
import CrmTable from '@/components/pure/crm-table/index.vue';
import { CrmDataTableColumn, CrmTableDataItem } from '@/components/pure/crm-table/type';
import useTable from '@/components/pure/crm-table/useTable';
import { useTableStore } from '@/store';
import { TableKeyEnum } from '@lib/shared/enums/tableEnum';
import type { CommonList } from '@lib/shared/models/common';
const tableStore = useTableStore();
interface RoleItem {
id: string;
num: string;
status: string;
title: string;
}
const columns: CrmDataTableColumn[] = [
{
type: 'selection',
// multiple: false, // 设置单选
},
{
title: 'common.creator',
key: 'num',
width: 60,
sortOrder: false,
sorter: 'default',
},
{
title: 'common.execute',
key: 'title',
width: 100,
ellipsis: {
tooltip: true,
},
sortOrder: false,
sorter: 'default',
filterOptions: [
{
label: '222',
value: '222',
},
{
label: 'string',
value: 'string',
},
],
filter(value, row) {
return row.title === value;
},
},
{
title: 'common.text',
key: 'status',
width: 100,
ellipsis: {
tooltip: true,
},
filterOptions: [
{
label: 'London',
value: 'London',
},
{
label: 'New York',
value: 'New York',
},
],
filter(value, row) {
return row.status === value;
},
},
{ key: 'operation', width: 80 },
];
function getRoleList() {
const data: CommonList<CrmTableDataItem<RoleItem>> = {
list: [
{
id: '11',
num: 'string',
title: 'string',
status: 'string',
updateTime: null,
createTime: null,
},
{
id: '22',
num: '232324323',
title: '222',
status: 'aaaa',
updateTime: null,
createTime: null,
},
{
id: '33',
num: 'string',
title: 'string',
status: 'string',
updateTime: null,
createTime: null,
},
{
id: '44',
num: '232324323',
title: '222',
status: 'aaaa',
updateTime: null,
createTime: null,
},
{
id: '55',
num: 'string',
title: 'string',
status: 'string',
updateTime: null,
createTime: null,
},
{
id: '66',
num: '232324323',
title: '222',
status: 'aaaa',
updateTime: null,
createTime: null,
},
{
id: '77',
num: 'string',
title: 'string',
status: 'string',
updateTime: null,
createTime: null,
},
{
id: '88',
num: '232324323',
title: '222',
status: 'aaaa',
updateTime: null,
createTime: null,
},
{
id: '99',
num: 'string',
title: 'string',
status: 'string',
updateTime: null,
createTime: null,
},
{
id: '1',
num: '232324323',
title: '222',
status: 'aaaa',
updateTime: null,
createTime: null,
},
{
id: '2',
num: '232324323',
title: '222',
status: 'aaaa',
updateTime: null,
createTime: null,
},
],
total: 11,
pageSize: 10,
current: 1,
};
return new Promise<CommonList<CrmTableDataItem<RoleItem>>>((resolve) => {
setTimeout(() => {
resolve(data);
}, 1000);
});
}
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getRoleList, {
tableKey: TableKeyEnum.SYSTEM_USER,
showSetting: true,
columns,
});
function searchData() {
setLoadListParams({ keyword: '' });
loadList();
}
onMounted(() => {
searchData();
});
await tableStore.initColumn(TableKeyEnum.SYSTEM_USER, columns);
</script>