mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-17 17:26:38 +08:00
refactor(projects): refactor useTable
This commit is contained in:
parent
51d7758903
commit
c3efa1b6e0
@ -2,5 +2,8 @@ import useBoolean from './use-boolean';
|
|||||||
import useLoading from './use-loading';
|
import useLoading from './use-loading';
|
||||||
import useContext from './use-context';
|
import useContext from './use-context';
|
||||||
import useSvgIconRender from './use-svg-icon-render';
|
import useSvgIconRender from './use-svg-icon-render';
|
||||||
|
import useTable from './use-table';
|
||||||
|
|
||||||
export { useBoolean, useLoading, useContext, useSvgIconRender };
|
export { useBoolean, useLoading, useContext, useSvgIconRender, useTable };
|
||||||
|
|
||||||
|
export * from './use-table';
|
||||||
|
163
packages/hooks/src/use-table.ts
Normal file
163
packages/hooks/src/use-table.ts
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
import { computed, reactive, ref } from 'vue';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import useBoolean from './use-boolean';
|
||||||
|
import useLoading from './use-loading';
|
||||||
|
|
||||||
|
export type MaybePromise<T> = T | Promise<T>;
|
||||||
|
|
||||||
|
export type ApiFn = (args: any) => Promise<unknown>;
|
||||||
|
|
||||||
|
export type TableData = Record<string, unknown>;
|
||||||
|
|
||||||
|
export type TableColumn = Record<string, any>;
|
||||||
|
|
||||||
|
export type TableColumnCheck = {
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
checked: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TransformedData<T extends TableData = TableData> = {
|
||||||
|
data: T[];
|
||||||
|
pageNum: number;
|
||||||
|
pageSize: number;
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Transformer<T extends TableData = TableData, Response = object> = (
|
||||||
|
response: Response
|
||||||
|
) => TransformedData<T>;
|
||||||
|
|
||||||
|
export type TableConfig<
|
||||||
|
A extends ApiFn = ApiFn,
|
||||||
|
T extends TableData = TableData,
|
||||||
|
C extends TableColumn = TableColumn
|
||||||
|
> = {
|
||||||
|
/** api function to get table data */
|
||||||
|
apiFn: A;
|
||||||
|
/** api params */
|
||||||
|
apiParams?: Parameters<A>[0];
|
||||||
|
/** transform api response to table data */
|
||||||
|
transformer: Transformer<T, Awaited<ReturnType<A>>>;
|
||||||
|
/** columns factory */
|
||||||
|
columns: () => C[];
|
||||||
|
/**
|
||||||
|
* get column checks
|
||||||
|
*
|
||||||
|
* @param columns
|
||||||
|
*/
|
||||||
|
getColumnChecks: (columns: C[]) => TableColumnCheck[];
|
||||||
|
/**
|
||||||
|
* get columns
|
||||||
|
*
|
||||||
|
* @param columns
|
||||||
|
*/
|
||||||
|
getColumns: (columns: C[], checks: TableColumnCheck[]) => C[];
|
||||||
|
/**
|
||||||
|
* callback when response fetched
|
||||||
|
*
|
||||||
|
* @param transformed transformed data
|
||||||
|
*/
|
||||||
|
onFetched?: (transformed: TransformedData<T>) => MaybePromise<void>;
|
||||||
|
/**
|
||||||
|
* whether to get data immediately
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
immediate?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function useTable<
|
||||||
|
A extends ApiFn = ApiFn,
|
||||||
|
T extends TableData = TableData,
|
||||||
|
C extends TableColumn = TableColumn
|
||||||
|
>(config: TableConfig<A, T, C>) {
|
||||||
|
const { loading, startLoading, endLoading } = useLoading();
|
||||||
|
const { bool: empty, setBool: setEmpty } = useBoolean();
|
||||||
|
|
||||||
|
const { apiFn, apiParams, transformer, immediate = true, getColumnChecks, getColumns } = config;
|
||||||
|
|
||||||
|
const searchParams: NonNullable<Parameters<A>[0]> = reactive({ ...apiParams });
|
||||||
|
|
||||||
|
const allColumns = ref(config.columns()) as Ref<C[]>;
|
||||||
|
|
||||||
|
const data: Ref<T[]> = ref([]);
|
||||||
|
|
||||||
|
const columnChecks: Ref<TableColumnCheck[]> = ref(getColumnChecks(config.columns()));
|
||||||
|
|
||||||
|
const columns = computed(() => getColumns(allColumns.value, columnChecks.value));
|
||||||
|
|
||||||
|
function reloadColumns() {
|
||||||
|
allColumns.value = config.columns();
|
||||||
|
|
||||||
|
const checkMap = new Map(columnChecks.value.map(col => [col.key, col.checked]));
|
||||||
|
|
||||||
|
const defaultChecks = getColumnChecks(allColumns.value);
|
||||||
|
|
||||||
|
columnChecks.value = defaultChecks.map(col => ({
|
||||||
|
...col,
|
||||||
|
checked: checkMap.get(col.key) ?? col.checked
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getData() {
|
||||||
|
startLoading();
|
||||||
|
|
||||||
|
const formattedParams = formatSearchParams(searchParams);
|
||||||
|
|
||||||
|
const response = await apiFn(formattedParams);
|
||||||
|
|
||||||
|
const transformed = transformer(response as Awaited<ReturnType<A>>);
|
||||||
|
|
||||||
|
data.value = transformed.data;
|
||||||
|
|
||||||
|
setEmpty(transformed.data.length === 0);
|
||||||
|
|
||||||
|
await config.onFetched?.(transformed);
|
||||||
|
|
||||||
|
endLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSearchParams(params: Record<string, unknown>) {
|
||||||
|
const formattedParams: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
if (value !== null && value !== undefined) {
|
||||||
|
formattedParams[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return formattedParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update search params
|
||||||
|
*
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
function updateSearchParams(params: Partial<Parameters<A>[0]>) {
|
||||||
|
Object.assign(searchParams, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** reset search params */
|
||||||
|
function resetSearchParams() {
|
||||||
|
Object.assign(searchParams, apiParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (immediate) {
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
empty,
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
columnChecks,
|
||||||
|
reloadColumns,
|
||||||
|
getData,
|
||||||
|
searchParams,
|
||||||
|
updateSearchParams,
|
||||||
|
resetSearchParams
|
||||||
|
};
|
||||||
|
}
|
@ -1,13 +1,12 @@
|
|||||||
<script setup lang="ts" generic="T extends Record<string, unknown>, K = never">
|
<script setup lang="ts" generic="T extends Record<string, unknown>, K = never">
|
||||||
import { VueDraggable } from 'vue-draggable-plus';
|
import { VueDraggable } from 'vue-draggable-plus';
|
||||||
import type { FilteredColumn } from '@/hooks/common/table';
|
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'TableColumnSetting'
|
name: 'TableColumnSetting'
|
||||||
});
|
});
|
||||||
|
|
||||||
const columns = defineModel<FilteredColumn[]>('columns', {
|
const columns = defineModel<NaiveUI.TableColumnCheck[]>('columns', {
|
||||||
required: true
|
required: true
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FilteredColumn } from '@/hooks/common/table';
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'TableHeaderOperation'
|
name: 'TableHeaderOperation'
|
||||||
});
|
});
|
||||||
@ -21,7 +19,7 @@ interface Emits {
|
|||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const columns = defineModel<FilteredColumn[]>('columns', {
|
const columns = defineModel<NaiveUI.TableColumnCheck[]>('columns', {
|
||||||
default: () => []
|
default: () => []
|
||||||
});
|
});
|
||||||
|
|
||||||
|
232
src/hooks/common/naive-table.ts
Normal file
232
src/hooks/common/naive-table.ts
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
import { computed, effectScope, onScopeDispose, reactive, ref, watch } from 'vue';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import type { PaginationProps } from 'naive-ui';
|
||||||
|
import { useBoolean, useTable } from '@sa/hooks';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
type TableData = NaiveUI.TableData;
|
||||||
|
|
||||||
|
export function useNaiveTable<T extends TableData = TableData, A extends NaiveUI.TableApiFn<T> = NaiveUI.TableApiFn<T>>(
|
||||||
|
config: NaiveUI.NaiveTableConfig<T, A>
|
||||||
|
) {
|
||||||
|
const scope = effectScope();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const { apiFn, apiParams, immediate } = config;
|
||||||
|
|
||||||
|
const SELECTION_KEY = '__selection__';
|
||||||
|
|
||||||
|
const {
|
||||||
|
loading,
|
||||||
|
empty,
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
columnChecks,
|
||||||
|
reloadColumns,
|
||||||
|
getData,
|
||||||
|
searchParams,
|
||||||
|
updateSearchParams,
|
||||||
|
resetSearchParams
|
||||||
|
} = useTable<A, T, NaiveUI.TableColumn<T>>({
|
||||||
|
apiFn,
|
||||||
|
apiParams,
|
||||||
|
columns: config.columns,
|
||||||
|
transformer: res => {
|
||||||
|
const { records = [], current = 1, size = 10, total = 0 } = res.data || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: records,
|
||||||
|
pageNum: current,
|
||||||
|
pageSize: size,
|
||||||
|
total
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getColumnChecks: cols => {
|
||||||
|
const checks: NaiveUI.TableColumnCheck[] = [];
|
||||||
|
|
||||||
|
cols.forEach(column => {
|
||||||
|
if (isTableColumnHasKey(column)) {
|
||||||
|
checks.push({
|
||||||
|
key: column.key as string,
|
||||||
|
title: column.title as string,
|
||||||
|
checked: true
|
||||||
|
});
|
||||||
|
} else if (column.type === 'selection') {
|
||||||
|
checks.push({
|
||||||
|
key: SELECTION_KEY,
|
||||||
|
title: $t('common.check'),
|
||||||
|
checked: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return checks;
|
||||||
|
},
|
||||||
|
getColumns: (cols, checks) => {
|
||||||
|
const columnMap = new Map<string, NaiveUI.TableColumn<T>>();
|
||||||
|
|
||||||
|
cols.forEach(column => {
|
||||||
|
if (isTableColumnHasKey(column)) {
|
||||||
|
columnMap.set(column.key as string, column);
|
||||||
|
} else if (column.type === 'selection') {
|
||||||
|
columnMap.set(SELECTION_KEY, column);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredColumns = checks
|
||||||
|
.filter(item => item.checked)
|
||||||
|
.map(check => columnMap.get(check.key) as NaiveUI.TableColumn<T>);
|
||||||
|
|
||||||
|
return filteredColumns;
|
||||||
|
},
|
||||||
|
onFetched: async transformed => {
|
||||||
|
const { pageNum, pageSize, total } = transformed;
|
||||||
|
|
||||||
|
updatePagination({
|
||||||
|
page: pageNum,
|
||||||
|
pageSize,
|
||||||
|
itemCount: total
|
||||||
|
});
|
||||||
|
},
|
||||||
|
immediate
|
||||||
|
});
|
||||||
|
|
||||||
|
const pagination: PaginationProps = reactive({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
showSizePicker: true,
|
||||||
|
pageSizes: [10, 15, 20, 25, 30],
|
||||||
|
onUpdatePage: async (page: number) => {
|
||||||
|
pagination.page = page;
|
||||||
|
|
||||||
|
updateSearchParams({
|
||||||
|
current: page,
|
||||||
|
size: pagination.pageSize!
|
||||||
|
});
|
||||||
|
|
||||||
|
getData();
|
||||||
|
},
|
||||||
|
onUpdatePageSize: async (pageSize: number) => {
|
||||||
|
pagination.pageSize = pageSize;
|
||||||
|
pagination.page = 1;
|
||||||
|
|
||||||
|
updateSearchParams({
|
||||||
|
current: pagination.page,
|
||||||
|
size: pageSize
|
||||||
|
});
|
||||||
|
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// this is for mobile, if the system does not support mobile, you can use `pagination` directly
|
||||||
|
const mobilePagination = computed(() => {
|
||||||
|
const p: PaginationProps = {
|
||||||
|
...pagination,
|
||||||
|
pageSlot: appStore.isMobile ? 3 : 9
|
||||||
|
};
|
||||||
|
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
|
||||||
|
function updatePagination(update: Partial<PaginationProps>) {
|
||||||
|
Object.assign(pagination, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.run(() => {
|
||||||
|
watch(
|
||||||
|
() => appStore.locale,
|
||||||
|
() => {
|
||||||
|
reloadColumns();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
onScopeDispose(() => {
|
||||||
|
scope.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
empty,
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
columnChecks,
|
||||||
|
reloadColumns,
|
||||||
|
pagination,
|
||||||
|
mobilePagination,
|
||||||
|
updatePagination,
|
||||||
|
getData,
|
||||||
|
searchParams,
|
||||||
|
updateSearchParams,
|
||||||
|
resetSearchParams
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useNaiveTableOperate<T extends TableData = TableData>(data: Ref<T[]>, getData: () => Promise<void>) {
|
||||||
|
const { bool: drawerVisible, setTrue: openDrawer, setFalse: closeDrawer } = useBoolean();
|
||||||
|
|
||||||
|
const operateType = ref<NaiveUI.TableOperateType>('add');
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
operateType.value = 'add';
|
||||||
|
openDrawer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** the editing row data */
|
||||||
|
const editingData: Ref<T | null> = ref(null);
|
||||||
|
|
||||||
|
function handleEdit(id: number) {
|
||||||
|
operateType.value = 'edit';
|
||||||
|
editingData.value = data.value.find(item => item.id === id) || null;
|
||||||
|
|
||||||
|
openDrawer();
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkedRowKeys = ref<string[]>([]);
|
||||||
|
|
||||||
|
/** the hook after the batch delete operation is completed */
|
||||||
|
async function onBatchDeleted() {
|
||||||
|
window.$message?.success($t('common.deleteSuccess'));
|
||||||
|
|
||||||
|
checkedRowKeys.value = [];
|
||||||
|
|
||||||
|
await getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** the hook after the delete operation is completed */
|
||||||
|
async function onDeleted() {
|
||||||
|
window.$message?.success($t('common.deleteSuccess'));
|
||||||
|
|
||||||
|
await getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
drawerVisible,
|
||||||
|
operateType,
|
||||||
|
handleAdd,
|
||||||
|
editingData,
|
||||||
|
handleEdit,
|
||||||
|
checkedRowKeys,
|
||||||
|
onBatchDeleted,
|
||||||
|
onDeleted,
|
||||||
|
closeDrawer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNaiveTableIndex(pagination: PaginationProps, index: number) {
|
||||||
|
const { page = 1, pageSize = 10 } = pagination;
|
||||||
|
|
||||||
|
return (page - 1) * pageSize + index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNaiveTableRowKey<T extends TableData>(row: T) {
|
||||||
|
return row.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTableColumnHasKey<T extends TableData>(
|
||||||
|
column: NaiveUI.TableColumn<T>
|
||||||
|
): column is NaiveUI.TableColumnWithKey<T> {
|
||||||
|
return Boolean((column as NaiveUI.TableColumnWithKey<T>).key);
|
||||||
|
}
|
40
src/typings/naive-ui.d.ts
vendored
40
src/typings/naive-ui.d.ts
vendored
@ -1,4 +1,44 @@
|
|||||||
declare namespace NaiveUI {
|
declare namespace NaiveUI {
|
||||||
type ThemeColor = 'default' | 'error' | 'primary' | 'info' | 'success' | 'warning';
|
type ThemeColor = 'default' | 'error' | 'primary' | 'info' | 'success' | 'warning';
|
||||||
type Align = 'stretch' | 'baseline' | 'start' | 'end' | 'center' | 'flex-end' | 'flex-start';
|
type Align = 'stretch' | 'baseline' | 'start' | 'end' | 'center' | 'flex-end' | 'flex-start';
|
||||||
|
|
||||||
|
type DataTableBaseColumn<T> = import('naive-ui').DataTableBaseColumn<T>;
|
||||||
|
type DataTableExpandColumn<T> = import('naive-ui').DataTableExpandColumn<T>;
|
||||||
|
type DataTableSelectionColumn<T> = import('naive-ui').DataTableSelectionColumn<T>;
|
||||||
|
type TableColumnGroup<T> = import('naive-ui/es/data-table/src/interface').TableColumnGroup<T>;
|
||||||
|
type PaginationProps = import('naive-ui').PaginationProps;
|
||||||
|
type TableColumnCheck = import('@sa/hooks').TableColumnCheck;
|
||||||
|
type FlatResponseData<T> = import('@sa/axios').FlatResponseData<T>;
|
||||||
|
|
||||||
|
type CustomColumnKey = 'index' | 'operate';
|
||||||
|
|
||||||
|
type SetTableColumnKey<C, T> = Omit<C, 'key'> & { key: keyof T | CustomColumnKey };
|
||||||
|
|
||||||
|
type TableData = Api.Common.CommonRecord<object>;
|
||||||
|
|
||||||
|
type TableColumnWithKey<T extends TableData = TableData> =
|
||||||
|
| SetTableColumnKey<DataTableBaseColumn<T>, T>
|
||||||
|
| SetTableColumnKey<TableColumnGroup<T>, T>;
|
||||||
|
|
||||||
|
type TableColumn<T extends TableData = TableData> =
|
||||||
|
| TableColumnWithKey<T>
|
||||||
|
| DataTableSelectionColumn<T>
|
||||||
|
| DataTableExpandColumn<T>;
|
||||||
|
|
||||||
|
type TableApiFn<T extends TableData = TableData> = (
|
||||||
|
params: Api.SystemManage.CommonSearchParams
|
||||||
|
) => Promise<FlatResponseData<Api.Common.PaginatingQueryRecord<T>>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the type of table operation
|
||||||
|
*
|
||||||
|
* - add: add table item
|
||||||
|
* - edit: edit table item
|
||||||
|
*/
|
||||||
|
type TableOperateType = 'add' | 'edit';
|
||||||
|
|
||||||
|
type NaiveTableConfig<T extends TableData = TableData, A extends TableApiFn<T> = TableApiFn<T>> = Pick<
|
||||||
|
import('@sa/hooks').TableConfig<A, T, TableColumn<T>>,
|
||||||
|
'apiFn' | 'apiParams' | 'columns' | 'immediate'
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
|
198
src/views/manage/user/index2.vue
Normal file
198
src/views/manage/user/index2.vue
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
||||||
|
import { fetchGetUserList } from '@/service/api';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
|
||||||
|
import {
|
||||||
|
getNaiveTableIndex,
|
||||||
|
getNaiveTableRowKey,
|
||||||
|
useNaiveTable,
|
||||||
|
useNaiveTableOperate
|
||||||
|
} from '@/hooks/common/naive-table';
|
||||||
|
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
||||||
|
import UserSearch from './modules/user-search.vue';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const { columns, columnChecks, data, getData, loading, pagination, mobilePagination, searchParams, resetSearchParams } =
|
||||||
|
useNaiveTable({
|
||||||
|
apiFn: fetchGetUserList,
|
||||||
|
apiParams: {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
||||||
|
// the value can not be undefined, otherwise the property in Form will not be reactive
|
||||||
|
status: null,
|
||||||
|
userName: null,
|
||||||
|
userGender: null,
|
||||||
|
nickName: null,
|
||||||
|
userPhone: null,
|
||||||
|
userEmail: null
|
||||||
|
},
|
||||||
|
columns: () => [
|
||||||
|
{
|
||||||
|
type: 'selection',
|
||||||
|
align: 'center',
|
||||||
|
width: 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'index',
|
||||||
|
title: $t('common.index'),
|
||||||
|
render: (_, index): number => getNaiveTableIndex(pagination, index),
|
||||||
|
align: 'center',
|
||||||
|
width: 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'userName',
|
||||||
|
title: $t('page.manage.user.userName'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'userGender',
|
||||||
|
title: $t('page.manage.user.userGender'),
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: row => {
|
||||||
|
if (row.userGender === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagMap: Record<Api.SystemManage.UserGender, NaiveUI.ThemeColor> = {
|
||||||
|
1: 'primary',
|
||||||
|
2: 'error'
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = $t(userGenderRecord[row.userGender]);
|
||||||
|
|
||||||
|
return <NTag type={tagMap[row.userGender]}>{label}</NTag>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'nickName',
|
||||||
|
title: $t('page.manage.user.nickName'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'userPhone',
|
||||||
|
title: $t('page.manage.user.userPhone'),
|
||||||
|
align: 'center',
|
||||||
|
width: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'userEmail',
|
||||||
|
title: $t('page.manage.user.userEmail'),
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'status',
|
||||||
|
title: $t('page.manage.user.userStatus'),
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: row => {
|
||||||
|
if (row.status === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagMap: Record<Api.Common.EnableStatus, NaiveUI.ThemeColor> = {
|
||||||
|
1: 'success',
|
||||||
|
2: 'warning'
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = $t(enableStatusRecord[row.status]);
|
||||||
|
|
||||||
|
return <NTag type={tagMap[row.status]}>{label}</NTag>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'operate',
|
||||||
|
title: $t('common.operate'),
|
||||||
|
align: 'center',
|
||||||
|
width: 130,
|
||||||
|
render: row => (
|
||||||
|
<div class="flex-center gap-8px">
|
||||||
|
<NButton type="primary" ghost size="small" onClick={() => edit(row.id)}>
|
||||||
|
{$t('common.edit')}
|
||||||
|
</NButton>
|
||||||
|
<NPopconfirm onPositiveClick={() => handleDelete(row.id)}>
|
||||||
|
{{
|
||||||
|
default: () => $t('common.confirmDelete'),
|
||||||
|
trigger: () => (
|
||||||
|
<NButton type="error" ghost size="small">
|
||||||
|
{$t('common.delete')}
|
||||||
|
</NButton>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NPopconfirm>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
|
||||||
|
useNaiveTableOperate(data, getData);
|
||||||
|
|
||||||
|
async function handleBatchDelete() {
|
||||||
|
// request
|
||||||
|
console.log(checkedRowKeys.value);
|
||||||
|
|
||||||
|
onBatchDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(id: number) {
|
||||||
|
// request
|
||||||
|
console.log(id);
|
||||||
|
|
||||||
|
onDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(id: number) {
|
||||||
|
handleEdit(id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="min-h-500px flex-vertical-stretch gap-16px overflow-hidden <sm:overflow-auto">
|
||||||
|
<UserSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
|
||||||
|
<NCard :title="$t('page.manage.user.title')" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
|
||||||
|
<template #header-extra>
|
||||||
|
<TableHeaderOperation
|
||||||
|
v-model:columns="columnChecks"
|
||||||
|
:disabled-delete="checkedRowKeys.length === 0"
|
||||||
|
:loading="loading"
|
||||||
|
@add="handleAdd"
|
||||||
|
@delete="handleBatchDelete"
|
||||||
|
@refresh="getData"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<NDataTable
|
||||||
|
v-model:checked-row-keys="checkedRowKeys"
|
||||||
|
:columns="columns"
|
||||||
|
:data="data"
|
||||||
|
size="small"
|
||||||
|
:flex-height="!appStore.isMobile"
|
||||||
|
:scroll-x="962"
|
||||||
|
:loading="loading"
|
||||||
|
remote
|
||||||
|
:row-key="getNaiveTableRowKey"
|
||||||
|
:pagination="mobilePagination"
|
||||||
|
class="sm:h-full"
|
||||||
|
/>
|
||||||
|
<UserOperateDrawer
|
||||||
|
v-model:visible="drawerVisible"
|
||||||
|
:operate-type="operateType"
|
||||||
|
:row-data="editingData"
|
||||||
|
@submitted="getData"
|
||||||
|
/>
|
||||||
|
</NCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
Loading…
Reference in New Issue
Block a user