mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-21 02:56:38 +08:00
fix(projects): fix example code
This commit is contained in:
parent
54c9511bf8
commit
47e7ade11b
@ -241,7 +241,6 @@ const local: App.I18n.Schema = {
|
|||||||
function: 'System Function',
|
function: 'System Function',
|
||||||
alova: 'Alova Example',
|
alova: 'Alova Example',
|
||||||
alova_request: 'Alova Request',
|
alova_request: 'Alova Request',
|
||||||
alova_user: 'User List',
|
|
||||||
alova_scenes: 'Scenario Request',
|
alova_scenes: 'Scenario Request',
|
||||||
'pro-naive': 'Pro Naive Example',
|
'pro-naive': 'Pro Naive Example',
|
||||||
'pro-naive_form': 'Form',
|
'pro-naive_form': 'Form',
|
||||||
|
@ -238,7 +238,6 @@ const local: App.I18n.Schema = {
|
|||||||
function: '系统功能',
|
function: '系统功能',
|
||||||
alova: 'alova示例',
|
alova: 'alova示例',
|
||||||
alova_request: 'alova请求',
|
alova_request: 'alova请求',
|
||||||
alova_user: '用户列表',
|
|
||||||
alova_scenes: '场景化请求',
|
alova_scenes: '场景化请求',
|
||||||
'pro-naive': 'Pro Naive UI 示例',
|
'pro-naive': 'Pro Naive UI 示例',
|
||||||
'pro-naive_form': '表单',
|
'pro-naive_form': '表单',
|
||||||
|
@ -23,7 +23,6 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
about: () => import("@/views/about/index.vue"),
|
about: () => import("@/views/about/index.vue"),
|
||||||
alova_request: () => import("@/views/alova/request/index.vue"),
|
alova_request: () => import("@/views/alova/request/index.vue"),
|
||||||
alova_scenes: () => import("@/views/alova/scenes/index.vue"),
|
alova_scenes: () => import("@/views/alova/scenes/index.vue"),
|
||||||
alova_user: () => import("@/views/alova/user/index.vue"),
|
|
||||||
"function_hide-child_one": () => import("@/views/function/hide-child/one/index.vue"),
|
"function_hide-child_one": () => import("@/views/function/hide-child/one/index.vue"),
|
||||||
"function_hide-child_three": () => import("@/views/function/hide-child/three/index.vue"),
|
"function_hide-child_three": () => import("@/views/function/hide-child/three/index.vue"),
|
||||||
"function_hide-child_two": () => import("@/views/function/hide-child/two/index.vue"),
|
"function_hide-child_two": () => import("@/views/function/hide-child/two/index.vue"),
|
||||||
|
@ -81,17 +81,6 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
icon: 'cbi:scene-dynamic',
|
icon: 'cbi:scene-dynamic',
|
||||||
order: 3
|
order: 3
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'alova_user',
|
|
||||||
path: '/alova/user',
|
|
||||||
component: 'view.alova_user',
|
|
||||||
meta: {
|
|
||||||
title: 'alova_user',
|
|
||||||
i18nKey: 'route.alova_user',
|
|
||||||
icon: 'carbon:user-multiple',
|
|
||||||
order: 2
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -185,7 +185,6 @@ const routeMap: RouteMap = {
|
|||||||
"alova": "/alova",
|
"alova": "/alova",
|
||||||
"alova_request": "/alova/request",
|
"alova_request": "/alova/request",
|
||||||
"alova_scenes": "/alova/scenes",
|
"alova_scenes": "/alova/scenes",
|
||||||
"alova_user": "/alova/user",
|
|
||||||
"function": "/function",
|
"function": "/function",
|
||||||
"function_hide-child": "/function/hide-child",
|
"function_hide-child": "/function/hide-child",
|
||||||
"function_hide-child_one": "/function/hide-child/one",
|
"function_hide-child_one": "/function/hide-child/one",
|
||||||
|
2
src/typings/elegant-router.d.ts
vendored
2
src/typings/elegant-router.d.ts
vendored
@ -39,7 +39,6 @@ declare module "@elegant-router/types" {
|
|||||||
"alova": "/alova";
|
"alova": "/alova";
|
||||||
"alova_request": "/alova/request";
|
"alova_request": "/alova/request";
|
||||||
"alova_scenes": "/alova/scenes";
|
"alova_scenes": "/alova/scenes";
|
||||||
"alova_user": "/alova/user";
|
|
||||||
"function": "/function";
|
"function": "/function";
|
||||||
"function_hide-child": "/function/hide-child";
|
"function_hide-child": "/function/hide-child";
|
||||||
"function_hide-child_one": "/function/hide-child/one";
|
"function_hide-child_one": "/function/hide-child/one";
|
||||||
@ -183,7 +182,6 @@ declare module "@elegant-router/types" {
|
|||||||
| "about"
|
| "about"
|
||||||
| "alova_request"
|
| "alova_request"
|
||||||
| "alova_scenes"
|
| "alova_scenes"
|
||||||
| "alova_user"
|
|
||||||
| "function_hide-child_one"
|
| "function_hide-child_one"
|
||||||
| "function_hide-child_three"
|
| "function_hide-child_three"
|
||||||
| "function_hide-child_two"
|
| "function_hide-child_two"
|
||||||
|
@ -1,78 +0,0 @@
|
|||||||
import { computed, ref } from 'vue';
|
|
||||||
import type { DataTableBaseColumn, DataTableColumn } from 'naive-ui';
|
|
||||||
import type { TableColumnCheck } from '@sa/hooks';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import type { AlovaGenerics, Method } from '~/packages/alova/src';
|
|
||||||
|
|
||||||
function isTableColumnHasKey<T>(column: DataTableColumn<T>): column is DataTableBaseColumn<T> {
|
|
||||||
return Boolean((column as NaiveUI.TableColumnWithKey<T>).key);
|
|
||||||
}
|
|
||||||
|
|
||||||
type TableAlovaApiFn<T = any, R = Api.Common.CommonSearchParams> = (
|
|
||||||
params: R
|
|
||||||
) => Method<AlovaGenerics<Api.Common.PaginatingQueryRecord<T>>>;
|
|
||||||
|
|
||||||
// this hook is used to manage table columns
|
|
||||||
// if you choose alova, you can move this hook to the `src/hooks` to handle all list page in your project
|
|
||||||
export default function useCheckedColumns<A extends TableAlovaApiFn, T = Awaited<ReturnType<A>>['records'][number]>(
|
|
||||||
getColumns: () => DataTableColumn<T>[]
|
|
||||||
) {
|
|
||||||
const SELECTION_KEY = '__selection__';
|
|
||||||
|
|
||||||
const EXPAND_KEY = '__expand__';
|
|
||||||
|
|
||||||
const getColumnChecks = (cols: DataTableColumn<T>[]) => {
|
|
||||||
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
|
|
||||||
});
|
|
||||||
} else if (column.type === 'expand') {
|
|
||||||
checks.push({
|
|
||||||
key: EXPAND_KEY,
|
|
||||||
title: $t('common.expandColumn'),
|
|
||||||
checked: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return checks;
|
|
||||||
};
|
|
||||||
|
|
||||||
const columnChecks = ref<TableColumnCheck[]>(getColumnChecks(getColumns()));
|
|
||||||
|
|
||||||
const columns = computed(() => {
|
|
||||||
const cols = getColumns();
|
|
||||||
const columnMap = new Map<string, DataTableColumn<T>>();
|
|
||||||
|
|
||||||
cols.forEach(column => {
|
|
||||||
if (isTableColumnHasKey(column)) {
|
|
||||||
columnMap.set(column.key as string, column);
|
|
||||||
} else if (column.type === 'selection') {
|
|
||||||
columnMap.set(SELECTION_KEY, column);
|
|
||||||
} else if (column.type === 'expand') {
|
|
||||||
columnMap.set(EXPAND_KEY, column);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const filteredColumns = columnChecks.value
|
|
||||||
.filter(item => item.checked)
|
|
||||||
.map(check => columnMap.get(check.key) as NaiveUI.TableColumn<T>);
|
|
||||||
|
|
||||||
return filteredColumns;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
columnChecks,
|
|
||||||
columns
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
import type { Ref } from 'vue';
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import { useBoolean } from '@sa/hooks';
|
|
||||||
import { jsonClone } from '@sa/utils';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
type TableData = NaiveUI.TableData;
|
|
||||||
interface Operations<T> {
|
|
||||||
delete?: (row: T) => Promise<void>;
|
|
||||||
batchDelete?: (rows: T[]) => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this hook is used to handle the table operations
|
|
||||||
// if you choose alova, you can move this hook to the `src/hooks` to handle all list page in your project
|
|
||||||
export default function useTableOperate<T extends TableData = TableData>(data: Ref<T[]>, operations: Operations<T>) {
|
|
||||||
const { bool: drawerVisible, setTrue: openDrawer, setFalse: closeDrawer } = useBoolean();
|
|
||||||
const { bool: deleting, setTrue: deletify, setFalse: antiDelete } = useBoolean();
|
|
||||||
const { bool: batchDeleting, setTrue: batchDeletify, setFalse: antiBatchDelete } = useBoolean();
|
|
||||||
|
|
||||||
const operateType = ref<NaiveUI.TableOperateType>('add');
|
|
||||||
|
|
||||||
const getRowByDataId = (id: T['id']) => data.value.find(item => item.id === id) || null;
|
|
||||||
|
|
||||||
function handleAdd() {
|
|
||||||
operateType.value = 'add';
|
|
||||||
openDrawer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** the editing row data */
|
|
||||||
const editingData: Ref<T | null> = ref(null);
|
|
||||||
|
|
||||||
function handleEdit(id: T['id']) {
|
|
||||||
operateType.value = 'edit';
|
|
||||||
editingData.value = jsonClone(getRowByDataId(id));
|
|
||||||
|
|
||||||
openDrawer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** the checked row keys of table */
|
|
||||||
const checkedRowKeys = ref<T['id'][]>([]);
|
|
||||||
|
|
||||||
/** handler to batch delete rows */
|
|
||||||
async function handleBatchDelete() {
|
|
||||||
batchDeletify();
|
|
||||||
try {
|
|
||||||
const rows = checkedRowKeys.value.map(id => getRowByDataId(id)).filter(Boolean);
|
|
||||||
await operations.batchDelete?.(rows as T[]);
|
|
||||||
window.$message?.success($t('common.deleteSuccess'));
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
} finally {
|
|
||||||
antiBatchDelete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** handler to delete row */
|
|
||||||
async function handleDelete(id: T['id']) {
|
|
||||||
deletify();
|
|
||||||
const row = getRowByDataId(id);
|
|
||||||
if (!row) return;
|
|
||||||
try {
|
|
||||||
await operations.delete?.(row);
|
|
||||||
window.$message?.success($t('common.deleteSuccess'));
|
|
||||||
checkedRowKeys.value = [];
|
|
||||||
} finally {
|
|
||||||
antiDelete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
drawerVisible,
|
|
||||||
openDrawer,
|
|
||||||
closeDrawer,
|
|
||||||
operateType,
|
|
||||||
handleAdd,
|
|
||||||
editingData,
|
|
||||||
handleEdit,
|
|
||||||
checkedRowKeys,
|
|
||||||
deleting,
|
|
||||||
handleDelete,
|
|
||||||
batchDeleting,
|
|
||||||
handleBatchDelete
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,226 +0,0 @@
|
|||||||
<script setup lang="tsx">
|
|
||||||
import { reactive } from 'vue';
|
|
||||||
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
|
||||||
import { usePagination } from '@sa/alova/client';
|
|
||||||
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
|
|
||||||
import { useAppStore } from '@/store/modules/app';
|
|
||||||
import { batchDeleteUser, deleteUser, fetchGetUserList } from '@/service-alova/api';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
import useCheckedColumns from './hooks/use-checked-columns';
|
|
||||||
import useTableOperate from './hooks/use-table-operate';
|
|
||||||
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
|
||||||
import UserSearch from './modules/user-search.vue';
|
|
||||||
|
|
||||||
const appStore = useAppStore();
|
|
||||||
|
|
||||||
const searchParams = reactive({
|
|
||||||
// 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
|
|
||||||
});
|
|
||||||
const { loading, data, refresh, reload, page, pageSize, pageCount, send, remove } = usePagination(
|
|
||||||
(pageNum, size) =>
|
|
||||||
fetchGetUserList({
|
|
||||||
...searchParams,
|
|
||||||
current: pageNum,
|
|
||||||
size
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
data: ({ records }) => records,
|
|
||||||
total: ({ total }) => total,
|
|
||||||
|
|
||||||
// trigger reload when states in `searchParams` changed
|
|
||||||
watchingStates: [searchParams],
|
|
||||||
|
|
||||||
// debounce of `searchParams`
|
|
||||||
debounce: [1000]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const getDataByPage = (newPage = 1) => {
|
|
||||||
page.value = newPage;
|
|
||||||
send(page.value, pageSize.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
|
||||||
drawerVisible,
|
|
||||||
operateType,
|
|
||||||
editingData,
|
|
||||||
handleAdd,
|
|
||||||
handleEdit,
|
|
||||||
handleDelete,
|
|
||||||
handleBatchDelete,
|
|
||||||
checkedRowKeys,
|
|
||||||
deleting
|
|
||||||
// batchDeleting
|
|
||||||
// closeDrawer
|
|
||||||
} = useTableOperate(data, {
|
|
||||||
async delete(row) {
|
|
||||||
await deleteUser(row.id);
|
|
||||||
remove(row);
|
|
||||||
},
|
|
||||||
async batchDelete(rows) {
|
|
||||||
await batchDeleteUser(rows.map(({ id }) => id));
|
|
||||||
remove(...rows);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function edit(id: number) {
|
|
||||||
handleEdit(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { columnChecks, columns } = useCheckedColumns<typeof fetchGetUserList>(() => [
|
|
||||||
{
|
|
||||||
type: 'selection',
|
|
||||||
align: 'center',
|
|
||||||
width: 48
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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)}
|
|
||||||
positiveButtonProps={{
|
|
||||||
loading: deleting.value
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
default: () => $t('common.confirmDelete'),
|
|
||||||
trigger: () => (
|
|
||||||
<NButton type="error" ghost size="small">
|
|
||||||
{$t('common.delete')}
|
|
||||||
</NButton>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</NPopconfirm>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
|
||||||
<UserSearch v-model:model="searchParams" @search="getDataByPage" />
|
|
||||||
<NCard :title="$t('page.manage.user.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
|
||||||
<template #header-extra>
|
|
||||||
<TableHeaderOperation
|
|
||||||
v-model:columns="columnChecks"
|
|
||||||
:disabled-delete="checkedRowKeys.length === 0"
|
|
||||||
:loading="loading"
|
|
||||||
@add="handleAdd"
|
|
||||||
@delete="handleBatchDelete"
|
|
||||||
@refresh="refresh"
|
|
||||||
/>
|
|
||||||
</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="row => row.id"
|
|
||||||
:pagination="{
|
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
showSizePicker: true,
|
|
||||||
pageCount,
|
|
||||||
pageSizes: [10, 15, 20, 25, 30],
|
|
||||||
onUpdatePage(value) {
|
|
||||||
page = value;
|
|
||||||
},
|
|
||||||
onUpdatePageSize(value) {
|
|
||||||
pageSize = value;
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
class="sm:h-full"
|
|
||||||
/>
|
|
||||||
<UserOperateDrawer
|
|
||||||
v-model:visible="drawerVisible"
|
|
||||||
:operate-type="operateType"
|
|
||||||
:row-data="editingData"
|
|
||||||
@submitted="reload"
|
|
||||||
/>
|
|
||||||
</NCard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,169 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed, watch } from 'vue';
|
|
||||||
import { useForm, useWatcher } from '@sa/alova/client';
|
|
||||||
import { enableStatusOptions, userGenderOptions } from '@/constants/business';
|
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import type { UserModel } from '@/service-alova/api';
|
|
||||||
import { addUser, fetchGetAllRoles, updateUser } from '@/service-alova/api';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'UserOperateDrawer'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
/** the type of operation */
|
|
||||||
operateType: NaiveUI.TableOperateType;
|
|
||||||
/** the edit row data */
|
|
||||||
rowData?: Api.SystemManage.User | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'submitted'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
const { defaultRequiredRule } = useFormRules();
|
|
||||||
|
|
||||||
const title = computed(() => {
|
|
||||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
|
||||||
add: $t('page.manage.user.addUser'),
|
|
||||||
edit: $t('page.manage.user.editUser')
|
|
||||||
};
|
|
||||||
return titles[props.operateType];
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
loading: submiting,
|
|
||||||
reset,
|
|
||||||
send: submit,
|
|
||||||
form,
|
|
||||||
updateForm
|
|
||||||
} = useForm(formData => (props.operateType === 'add' ? addUser(formData) : updateUser(formData)), {
|
|
||||||
initialForm: {
|
|
||||||
userName: '',
|
|
||||||
userGender: null,
|
|
||||||
nickName: '',
|
|
||||||
userPhone: '',
|
|
||||||
userEmail: '',
|
|
||||||
userRoles: [],
|
|
||||||
status: null
|
|
||||||
} as UserModel,
|
|
||||||
resetAfterSubmiting: true
|
|
||||||
});
|
|
||||||
|
|
||||||
type RuleKey = Extract<keyof UserModel, 'userName' | 'status'>;
|
|
||||||
|
|
||||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
|
||||||
userName: defaultRequiredRule,
|
|
||||||
status: defaultRequiredRule
|
|
||||||
};
|
|
||||||
|
|
||||||
/** the enabled role options */
|
|
||||||
const { data: roleOptionsRaw, loading } = useWatcher(fetchGetAllRoles, [visible], {
|
|
||||||
initialData: [],
|
|
||||||
middleware(_, next) {
|
|
||||||
return visible.value ? next() : undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const roleOptions = computed<CommonType.Option<string>[]>(() => {
|
|
||||||
const options = roleOptionsRaw.value.map(item => ({
|
|
||||||
label: item.roleName,
|
|
||||||
value: item.roleCode
|
|
||||||
}));
|
|
||||||
|
|
||||||
// the mock data does not have the roleCode, so fill it
|
|
||||||
// if the real request, remove the following code
|
|
||||||
const userRoleOptions = form.value.userRoles.map(item => ({
|
|
||||||
label: item,
|
|
||||||
value: item
|
|
||||||
}));
|
|
||||||
// end
|
|
||||||
|
|
||||||
return [...userRoleOptions, ...options];
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleInitModel() {
|
|
||||||
if (props.operateType === 'edit' && props.rowData) {
|
|
||||||
updateForm(props.rowData);
|
|
||||||
} else if (props.operateType === 'add') {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDrawer() {
|
|
||||||
visible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await validate();
|
|
||||||
// request
|
|
||||||
await submit();
|
|
||||||
window.$message?.success($t('common.updateSuccess'));
|
|
||||||
closeDrawer();
|
|
||||||
emit('submitted');
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(visible, () => {
|
|
||||||
if (visible.value) {
|
|
||||||
restoreValidation();
|
|
||||||
handleInitModel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NDrawer v-model:show="visible" display-directive="show" :width="360">
|
|
||||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
|
||||||
<NForm ref="formRef" :model="form" :rules="rules">
|
|
||||||
<NFormItem :label="$t('page.manage.user.userName')" path="userName">
|
|
||||||
<NInput v-model:value="form.userName" :placeholder="$t('page.manage.user.form.userName')" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem :label="$t('page.manage.user.userGender')" path="userGender">
|
|
||||||
<NRadioGroup v-model:value="form.userGender">
|
|
||||||
<NRadio v-for="item in userGenderOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
|
||||||
</NRadioGroup>
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem :label="$t('page.manage.user.nickName')" path="nickName">
|
|
||||||
<NInput v-model:value="form.nickName" :placeholder="$t('page.manage.user.form.nickName')" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem :label="$t('page.manage.user.userPhone')" path="userPhone">
|
|
||||||
<NInput v-model:value="form.userPhone" :placeholder="$t('page.manage.user.form.userPhone')" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem :label="$t('page.manage.user.userEmail')" path="email">
|
|
||||||
<NInput v-model:value="form.userEmail" :placeholder="$t('page.manage.user.form.userEmail')" />
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem :label="$t('page.manage.user.userStatus')" path="status">
|
|
||||||
<NRadioGroup v-model:value="form.status">
|
|
||||||
<NRadio v-for="item in enableStatusOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
|
|
||||||
</NRadioGroup>
|
|
||||||
</NFormItem>
|
|
||||||
<NFormItem :label="$t('page.manage.user.userRole')" path="roles">
|
|
||||||
<NSelect
|
|
||||||
v-model:value="form.userRoles"
|
|
||||||
multiple
|
|
||||||
:loading="loading"
|
|
||||||
:options="roleOptions"
|
|
||||||
:placeholder="$t('page.manage.user.form.userRole')"
|
|
||||||
/>
|
|
||||||
</NFormItem>
|
|
||||||
</NForm>
|
|
||||||
<template #footer>
|
|
||||||
<NSpace :size="16">
|
|
||||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
|
||||||
<NButton type="primary" :loading="submiting" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</template>
|
|
||||||
</NDrawerContent>
|
|
||||||
</NDrawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,113 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import { enableStatusOptions, userGenderOptions } from '@/constants/business';
|
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
|
||||||
import { translateOptions } from '@/utils/common';
|
|
||||||
import { $t } from '@/locales';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'UserSearch'
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'search'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
|
||||||
|
|
||||||
const model = defineModel<Api.SystemManage.UserSearchParams>('model', { required: true });
|
|
||||||
|
|
||||||
const initialParams = { ...model.value };
|
|
||||||
|
|
||||||
type RuleKey = Extract<keyof Api.SystemManage.UserSearchParams, 'userEmail' | 'userPhone'>;
|
|
||||||
|
|
||||||
const rules = computed<Record<RuleKey, App.Global.FormRule>>(() => {
|
|
||||||
const { patternRules } = useFormRules(); // inside computed to make locale reactive
|
|
||||||
|
|
||||||
return {
|
|
||||||
userEmail: patternRules.email,
|
|
||||||
userPhone: patternRules.phone
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
await restoreValidation();
|
|
||||||
Object.assign(model.value, initialParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function search() {
|
|
||||||
await validate();
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
|
||||||
<NCollapse :default-expanded-names="['user-search']">
|
|
||||||
<NCollapseItem :title="$t('common.search')" name="user-search">
|
|
||||||
<NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="80">
|
|
||||||
<NGrid responsive="screen" item-responsive>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.user.userName')" path="userName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.userName" :placeholder="$t('page.manage.user.form.userName')" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi
|
|
||||||
span="24 s:12 m:6"
|
|
||||||
:label="$t('page.manage.user.userGender')"
|
|
||||||
path="userGender"
|
|
||||||
class="pr-24px"
|
|
||||||
>
|
|
||||||
<NSelect
|
|
||||||
v-model:value="model.userGender"
|
|
||||||
:placeholder="$t('page.manage.user.form.userGender')"
|
|
||||||
:options="translateOptions(userGenderOptions)"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.user.nickName')" path="nickName" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.nickName" :placeholder="$t('page.manage.user.form.nickName')" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.user.userPhone')" path="userPhone" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.userPhone" :placeholder="$t('page.manage.user.form.userPhone')" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 s:12 m:6" :label="$t('page.manage.user.userEmail')" path="userEmail" class="pr-24px">
|
|
||||||
<NInput v-model:value="model.userEmail" :placeholder="$t('page.manage.user.form.userEmail')" />
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi
|
|
||||||
span="24 s:12 m:6"
|
|
||||||
:label="$t('page.manage.user.userStatus')"
|
|
||||||
path="userStatus"
|
|
||||||
class="pr-24px"
|
|
||||||
>
|
|
||||||
<NSelect
|
|
||||||
v-model:value="model.status"
|
|
||||||
:placeholder="$t('page.manage.user.form.userStatus')"
|
|
||||||
:options="translateOptions(enableStatusOptions)"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</NFormItemGi>
|
|
||||||
<NFormItemGi span="24 m:12" class="pr-24px">
|
|
||||||
<NSpace class="w-full" justify="end">
|
|
||||||
<NButton @click="reset">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.reset') }}
|
|
||||||
</NButton>
|
|
||||||
<NButton type="primary" ghost @click="search">
|
|
||||||
<template #icon>
|
|
||||||
<icon-ic-round-search class="text-icon" />
|
|
||||||
</template>
|
|
||||||
{{ $t('common.search') }}
|
|
||||||
</NButton>
|
|
||||||
</NSpace>
|
|
||||||
</NFormItemGi>
|
|
||||||
</NGrid>
|
|
||||||
</NForm>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
</NCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -7,7 +7,7 @@ import { yesOrNoRecord } from '@/constants/common';
|
|||||||
import { enableStatusRecord, menuTypeRecord } from '@/constants/business';
|
import { enableStatusRecord, menuTypeRecord } from '@/constants/business';
|
||||||
import { fetchGetAllPages, fetchGetMenuList } from '@/service/api';
|
import { fetchGetAllPages, fetchGetMenuList } from '@/service/api';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import SvgIcon from '@/components/custom/svg-icon.vue';
|
import SvgIcon from '@/components/custom/svg-icon.vue';
|
||||||
import MenuOperateModal, { type OperateType } from './modules/menu-operate-modal.vue';
|
import MenuOperateModal, { type OperateType } from './modules/menu-operate-modal.vue';
|
||||||
@ -18,8 +18,9 @@ const { bool: visible, setTrue: openModal } = useBoolean();
|
|||||||
|
|
||||||
const wrapperRef = ref<HTMLElement | null>(null);
|
const wrapperRef = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
const { columns, columnChecks, data, loading, pagination, getData, getDataByPage } = useTable({
|
const { columns, columnChecks, data, loading, pagination, getData, getDataByPage } = useNaivePaginatedTable({
|
||||||
apiFn: fetchGetMenuList,
|
api: () => fetchGetMenuList(),
|
||||||
|
transform: response => defaultTransform(response),
|
||||||
columns: () => [
|
columns: () => [
|
||||||
{
|
{
|
||||||
type: 'selection',
|
type: 'selection',
|
||||||
@ -170,7 +171,7 @@ const { columns, columnChecks, data, loading, pagination, getData, getDataByPage
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, getData);
|
const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, 'id', getData);
|
||||||
|
|
||||||
const operateType = ref<OperateType>('add');
|
const operateType = ref<OperateType>('add');
|
||||||
|
|
||||||
|
@ -1,35 +1,30 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
|
import { reactive } from 'vue';
|
||||||
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
||||||
import { enableStatusRecord } from '@/constants/business';
|
import { enableStatusRecord } from '@/constants/business';
|
||||||
import { fetchGetRoleList } from '@/service/api';
|
import { fetchGetRoleList } from '@/service/api';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import RoleOperateDrawer from './modules/role-operate-drawer.vue';
|
import RoleOperateDrawer from './modules/role-operate-drawer.vue';
|
||||||
import RoleSearch from './modules/role-search.vue';
|
import RoleSearch from './modules/role-search.vue';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const {
|
const searchParams: Api.SystemManage.RoleSearchParams = reactive({
|
||||||
columns,
|
current: 1,
|
||||||
columnChecks,
|
size: 10,
|
||||||
data,
|
roleName: null,
|
||||||
loading,
|
roleCode: null,
|
||||||
getData,
|
status: null
|
||||||
getDataByPage,
|
});
|
||||||
mobilePagination,
|
|
||||||
searchParams,
|
const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagination } = useNaivePaginatedTable({
|
||||||
resetSearchParams
|
api: () => fetchGetRoleList(searchParams),
|
||||||
} = useTable({
|
transform: response => defaultTransform(response),
|
||||||
apiFn: fetchGetRoleList,
|
onPaginationParamsChange: params => {
|
||||||
apiParams: {
|
searchParams.current = params.page;
|
||||||
current: 1,
|
searchParams.size = params.pageSize;
|
||||||
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,
|
|
||||||
roleName: null,
|
|
||||||
roleCode: null
|
|
||||||
},
|
},
|
||||||
columns: () => [
|
columns: () => [
|
||||||
{
|
{
|
||||||
@ -41,7 +36,8 @@ const {
|
|||||||
key: 'index',
|
key: 'index',
|
||||||
title: $t('common.index'),
|
title: $t('common.index'),
|
||||||
width: 64,
|
width: 64,
|
||||||
align: 'center'
|
align: 'center',
|
||||||
|
render: (_, index) => index + 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'roleName',
|
key: 'roleName',
|
||||||
@ -116,7 +112,7 @@ const {
|
|||||||
onBatchDeleted,
|
onBatchDeleted,
|
||||||
onDeleted
|
onDeleted
|
||||||
// closeDrawer
|
// closeDrawer
|
||||||
} = useTableOperate(data, getData);
|
} = useTableOperate(data, 'id', getData);
|
||||||
|
|
||||||
async function handleBatchDelete() {
|
async function handleBatchDelete() {
|
||||||
// request
|
// request
|
||||||
@ -139,7 +135,7 @@ function edit(id: number) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||||
<RoleSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
|
<RoleSearch v-model:model="searchParams" @search="getDataByPage" />
|
||||||
<NCard :title="$t('page.manage.role.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
<NCard :title="$t('page.manage.role.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
<TableHeaderOperation
|
<TableHeaderOperation
|
||||||
|
@ -8,7 +8,6 @@ defineOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
(e: 'search'): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,8 +15,14 @@ const emit = defineEmits<Emits>();
|
|||||||
|
|
||||||
const model = defineModel<Api.SystemManage.RoleSearchParams>('model', { required: true });
|
const model = defineModel<Api.SystemManage.RoleSearchParams>('model', { required: true });
|
||||||
|
|
||||||
function reset() {
|
function resetModel() {
|
||||||
emit('reset');
|
model.value = {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
roleName: null,
|
||||||
|
roleCode: null,
|
||||||
|
status: null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function search() {
|
function search() {
|
||||||
@ -47,7 +52,7 @@ function search() {
|
|||||||
</NFormItemGi>
|
</NFormItemGi>
|
||||||
<NFormItemGi span="24 s:12 m:6">
|
<NFormItemGi span="24 s:12 m:6">
|
||||||
<NSpace class="w-full" justify="end">
|
<NSpace class="w-full" justify="end">
|
||||||
<NButton @click="reset">
|
<NButton @click="resetModel">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
<icon-ic-round-refresh class="text-icon" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,39 +1,33 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
|
import { reactive } from 'vue';
|
||||||
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
||||||
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
|
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
|
||||||
import { fetchGetUserList } from '@/service/api';
|
import { fetchGetUserList } from '@/service/api';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
||||||
import UserSearch from './modules/user-search.vue';
|
import UserSearch from './modules/user-search.vue';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const {
|
const searchParams: Api.SystemManage.UserSearchParams = reactive({
|
||||||
columns,
|
current: 1,
|
||||||
columnChecks,
|
size: 10,
|
||||||
data,
|
status: null,
|
||||||
getData,
|
userName: null,
|
||||||
getDataByPage,
|
userGender: null,
|
||||||
loading,
|
nickName: null,
|
||||||
mobilePagination,
|
userPhone: null,
|
||||||
searchParams,
|
userEmail: null
|
||||||
resetSearchParams
|
});
|
||||||
} = useTable({
|
|
||||||
apiFn: fetchGetUserList,
|
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination } = useNaivePaginatedTable({
|
||||||
showTotal: true,
|
api: () => fetchGetUserList(searchParams),
|
||||||
apiParams: {
|
transform: response => defaultTransform(response),
|
||||||
current: 1,
|
onPaginationParamsChange: params => {
|
||||||
size: 10,
|
searchParams.current = params.page;
|
||||||
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
searchParams.size = params.pageSize;
|
||||||
// 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: () => [
|
columns: () => [
|
||||||
{
|
{
|
||||||
@ -45,7 +39,8 @@ const {
|
|||||||
key: 'index',
|
key: 'index',
|
||||||
title: $t('common.index'),
|
title: $t('common.index'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 64
|
width: 64,
|
||||||
|
render: (_, index) => index + 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'userName',
|
key: 'userName',
|
||||||
@ -147,7 +142,7 @@ const {
|
|||||||
onBatchDeleted,
|
onBatchDeleted,
|
||||||
onDeleted
|
onDeleted
|
||||||
// closeDrawer
|
// closeDrawer
|
||||||
} = useTableOperate(data, getData);
|
} = useTableOperate(data, 'id', getData);
|
||||||
|
|
||||||
async function handleBatchDelete() {
|
async function handleBatchDelete() {
|
||||||
// request
|
// request
|
||||||
@ -170,7 +165,7 @@ function edit(id: number) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||||
<UserSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
|
<UserSearch v-model:model="searchParams" @search="getDataByPage" />
|
||||||
<NCard :title="$t('page.manage.user.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
<NCard :title="$t('page.manage.user.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
<TableHeaderOperation
|
<TableHeaderOperation
|
||||||
|
@ -10,7 +10,6 @@ defineOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'reset'): void;
|
|
||||||
(e: 'search'): void;
|
(e: 'search'): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,9 +30,22 @@ const rules = computed<Record<RuleKey, App.Global.FormRule>>(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function resetModel() {
|
||||||
|
model.value = {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
status: null,
|
||||||
|
userName: null,
|
||||||
|
userGender: null,
|
||||||
|
nickName: null,
|
||||||
|
userPhone: null,
|
||||||
|
userEmail: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function reset() {
|
async function reset() {
|
||||||
await restoreValidation();
|
await restoreValidation();
|
||||||
emit('reset');
|
resetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function search() {
|
async function search() {
|
||||||
|
@ -1,28 +1,36 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
|
import { reactive } from 'vue';
|
||||||
import { NButton, NTag } from 'naive-ui';
|
import { NButton, NTag } from 'naive-ui';
|
||||||
import { utils, writeFile } from 'xlsx';
|
import { utils, writeFile } from 'xlsx';
|
||||||
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
|
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
|
||||||
import { fetchGetUserList } from '@/service/api';
|
import { fetchGetUserList } from '@/service/api';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useTable } from '@/hooks/common/table';
|
import { isTableColumnHasKey, useNaiveTable } from '@/hooks/common/table';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const { columns, data, loading } = useTable({
|
const searchParams: Api.SystemManage.UserSearchParams = reactive({
|
||||||
apiFn: fetchGetUserList,
|
current: 1,
|
||||||
showTotal: true,
|
size: 999,
|
||||||
apiParams: {
|
status: null,
|
||||||
current: 1,
|
userName: null,
|
||||||
size: 999,
|
userGender: null,
|
||||||
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
nickName: null,
|
||||||
// the value can not be undefined, otherwise the property in Form will not be reactive
|
userPhone: null,
|
||||||
status: null,
|
userEmail: null
|
||||||
userName: null,
|
});
|
||||||
userGender: null,
|
|
||||||
nickName: null,
|
const { columns, data, loading } = useNaiveTable({
|
||||||
userPhone: null,
|
api: () => fetchGetUserList(searchParams),
|
||||||
userEmail: null
|
transform: response => {
|
||||||
|
const { data: list, error } = response;
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
return list.records;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
},
|
},
|
||||||
columns: () => [
|
columns: () => [
|
||||||
{
|
{
|
||||||
@ -34,7 +42,8 @@ const { columns, data, loading } = useTable({
|
|||||||
key: 'index',
|
key: 'index',
|
||||||
title: $t('common.index'),
|
title: $t('common.index'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 64
|
width: 64,
|
||||||
|
render: (_, index) => index + 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'userName',
|
key: 'userName',
|
||||||
@ -125,20 +134,13 @@ function exportExcel() {
|
|||||||
writeFile(workBook, '用户数据.xlsx');
|
writeFile(workBook, '用户数据.xlsx');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTableValue(
|
function getTableValue(col: NaiveUI.TableColumn<Api.SystemManage.User>, item: Api.SystemManage.User) {
|
||||||
col: NaiveUI.TableColumn<NaiveUI.TableDataWithIndex<Api.SystemManage.User>>,
|
|
||||||
item: NaiveUI.TableDataWithIndex<Api.SystemManage.User>
|
|
||||||
) {
|
|
||||||
if (!isTableColumnHasKey(col)) {
|
if (!isTableColumnHasKey(col)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { key } = col;
|
const { key } = col;
|
||||||
|
|
||||||
if (key === 'operate') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'userRoles') {
|
if (key === 'userRoles') {
|
||||||
return item.userRoles.map(role => role).join(',');
|
return item.userRoles.map(role => role).join(',');
|
||||||
}
|
}
|
||||||
@ -151,11 +153,8 @@ function getTableValue(
|
|||||||
return (item.userGender && $t(userGenderRecord[item.userGender])) || null;
|
return (item.userGender && $t(userGenderRecord[item.userGender])) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return item[key];
|
// @ts-expect-error the key is not in the type of Api.SystemManage.User
|
||||||
}
|
return item[key] || null;
|
||||||
|
|
||||||
function isTableColumnHasKey<T>(column: NaiveUI.TableColumn<T>): column is NaiveUI.TableColumnWithKey<T> {
|
|
||||||
return Boolean((column as NaiveUI.TableColumnWithKey<T>).key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isTableColumnHasTitle<T>(column: NaiveUI.TableColumn<T>): column is NaiveUI.TableColumnWithKey<T> & {
|
function isTableColumnHasTitle<T>(column: NaiveUI.TableColumn<T>): column is NaiveUI.TableColumnWithKey<T> & {
|
||||||
|
Loading…
Reference in New Issue
Block a user