|
@@ -1,203 +1,265 @@
|
|
|
<script setup lang="tsx">
|
|
|
-import { reactive } from 'vue';
|
|
|
import { NButton, NPopconfirm, NTag } from 'naive-ui';
|
|
|
-import { enableStatusRecord, userGenderRecord } from '@/constants/business';
|
|
|
-import { fetchGetUserList } from '@/service/api';
|
|
|
-import { useAppStore } from '@/store/modules/app';
|
|
|
-import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
|
|
|
+import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
|
|
|
+import { enableStatusRecord } from '@/constants/business';
|
|
|
+import {
|
|
|
+ fetchAddUser,
|
|
|
+ fetchDeleteUser,
|
|
|
+ fetchDetaileUser,
|
|
|
+ fetchEditUser,
|
|
|
+ fetchGetRoleAllList,
|
|
|
+ fetchGetUserList
|
|
|
+} from '@/service/api';
|
|
|
+import { useTable } from '@/components/zt/Table/hooks/useTable';
|
|
|
import { $t } from '@/locales';
|
|
|
-import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
|
|
-import UserSearch from './modules/user-search.vue';
|
|
|
+import { useModalFrom } from '@/components/zt/ModalForm/hooks/useModalForm';
|
|
|
|
|
|
-const appStore = useAppStore();
|
|
|
-
|
|
|
-const searchParams: Api.SystemManage.UserSearchParams = reactive({
|
|
|
- current: 1,
|
|
|
- size: 10,
|
|
|
- status: null,
|
|
|
- userName: null,
|
|
|
- userGender: null,
|
|
|
- nickName: null,
|
|
|
- userPhone: null,
|
|
|
- userEmail: null
|
|
|
-});
|
|
|
-
|
|
|
-const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination } = useNaivePaginatedTable({
|
|
|
- api: () => fetchGetUserList(searchParams),
|
|
|
- transform: response => defaultTransform(response),
|
|
|
- onPaginationParamsChange: params => {
|
|
|
- searchParams.current = params.page;
|
|
|
- searchParams.size = params.pageSize;
|
|
|
+const columns: NaiveUI.TableColumn<InternalRowData>[] = [
|
|
|
+ {
|
|
|
+ type: 'selection',
|
|
|
+ align: 'center',
|
|
|
+ width: 48
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'index',
|
|
|
+ title: $t('common.index'),
|
|
|
+ align: 'center',
|
|
|
+ width: 64,
|
|
|
+ render: (_, index) => index + 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'username',
|
|
|
+ title: $t('page.manage.user.userName'),
|
|
|
+ align: 'center',
|
|
|
+ minWidth: 100
|
|
|
},
|
|
|
- columns: () => [
|
|
|
- {
|
|
|
- type: 'selection',
|
|
|
- align: 'center',
|
|
|
- width: 48
|
|
|
- },
|
|
|
- {
|
|
|
- key: 'index',
|
|
|
- title: $t('common.index'),
|
|
|
- align: 'center',
|
|
|
- width: 64,
|
|
|
- render: (_, index) => index + 1
|
|
|
- },
|
|
|
- {
|
|
|
- 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: 'mobile',
|
|
|
+ title: $t('page.manage.user.userPhone'),
|
|
|
+ align: 'center',
|
|
|
+ width: 120
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'email',
|
|
|
+ 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;
|
|
|
}
|
|
|
- },
|
|
|
- {
|
|
|
- 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]);
|
|
|
+ const tagMap: Record<Api.Common.EnableStatus, NaiveUI.ThemeColor> = {
|
|
|
+ 1: 'success',
|
|
|
+ 0: 'warning'
|
|
|
+ };
|
|
|
|
|
|
- return <NTag type={tagMap[row.status]}>{label}</NTag>;
|
|
|
- }
|
|
|
- },
|
|
|
+ const status = row.status as Api.Common.EnableStatus;
|
|
|
+ const label = $t(enableStatusRecord[status]);
|
|
|
+ return <NTag type={tagMap[status]}>{label}</NTag>;
|
|
|
+ }
|
|
|
+ }
|
|
|
+];
|
|
|
+const [registerTable, { refresh, setTableLoading }] = useTable({
|
|
|
+ schemas: [
|
|
|
{
|
|
|
- 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>
|
|
|
- )
|
|
|
+ field: 'username',
|
|
|
+ label: '用户名',
|
|
|
+ component: 'NInput'
|
|
|
}
|
|
|
- ]
|
|
|
+ ],
|
|
|
+ inline: false,
|
|
|
+ size: 'small',
|
|
|
+ labelPlacement: 'left',
|
|
|
+ isFull: false
|
|
|
});
|
|
|
|
|
|
-const {
|
|
|
- drawerVisible,
|
|
|
- operateType,
|
|
|
- editingData,
|
|
|
- handleAdd,
|
|
|
- handleEdit,
|
|
|
- checkedRowKeys,
|
|
|
- onBatchDeleted,
|
|
|
- onDeleted
|
|
|
- // closeDrawer
|
|
|
-} = useTableOperate(data, 'id', getData);
|
|
|
-
|
|
|
-async function handleBatchDelete() {
|
|
|
- // request
|
|
|
- console.log(checkedRowKeys.value);
|
|
|
-
|
|
|
- onBatchDeleted();
|
|
|
+async function handleDelete(row: Recordable) {
|
|
|
+ setTableLoading(true);
|
|
|
+ await fetchDeleteUser(row.userId);
|
|
|
+ refresh();
|
|
|
}
|
|
|
+const [registerModalForm, { openModal, closeModal, getFieldsValue, setFieldsValue, updateSchema }] = useModalFrom({
|
|
|
+ modalConfig: {
|
|
|
+ title: '用户 ',
|
|
|
+ width: 800
|
|
|
+ },
|
|
|
+ formConfig: {
|
|
|
+ schemas: [
|
|
|
+ {
|
|
|
+ field: 'userId',
|
|
|
+ label: '',
|
|
|
+ component: 'NInput',
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'username',
|
|
|
+ label: '用户名',
|
|
|
+ component: 'NInput',
|
|
|
+ required: true,
|
|
|
+ rules: [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ message: '请输入用户名',
|
|
|
+ trigger: ['blur', 'input'],
|
|
|
+ validator: (rule, value) => {
|
|
|
+ return new Promise<void>((resolve, reject) => {
|
|
|
+ if (value.length < 2) {
|
|
|
+ console.log(rule);
|
|
|
|
|
|
-function handleDelete(id: number) {
|
|
|
- // request
|
|
|
- console.log(id);
|
|
|
-
|
|
|
- onDeleted();
|
|
|
+ reject(new Error('用户名不能低于2位'));
|
|
|
+ }
|
|
|
+ if (value.length > 20) {
|
|
|
+ reject(new Error('用户名不能高于20位'));
|
|
|
+ }
|
|
|
+ resolve();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'password',
|
|
|
+ label: '密码',
|
|
|
+ component: 'NInput',
|
|
|
+ required: true,
|
|
|
+ componentProps: {
|
|
|
+ type: 'password',
|
|
|
+ showPasswordOn: 'click'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'comfirmPassword',
|
|
|
+ label: '确认密码',
|
|
|
+ component: 'NInput',
|
|
|
+ required: true,
|
|
|
+ componentProps: {
|
|
|
+ type: 'password',
|
|
|
+ showPasswordOn: 'click'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'email',
|
|
|
+ label: '邮箱',
|
|
|
+ component: 'NInput',
|
|
|
+ required: true,
|
|
|
+ rules: [
|
|
|
+ {
|
|
|
+ pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
|
|
|
+ type: 'email',
|
|
|
+ message: '请输入正确的邮箱地址',
|
|
|
+ trigger: ['blur', 'input']
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'mobile',
|
|
|
+ label: '手机号',
|
|
|
+ component: 'NInput',
|
|
|
+ required: true,
|
|
|
+ componentProps: {
|
|
|
+ maxlength: 11
|
|
|
+ },
|
|
|
+ rules: [
|
|
|
+ {
|
|
|
+ pattern: /^1[3456789]\d{9}$/,
|
|
|
+ message: '请输入正确的手机号',
|
|
|
+ trigger: ['blur', 'input']
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'roleIdList',
|
|
|
+ label: '角色',
|
|
|
+ component: 'ApiSelect',
|
|
|
+ required: true,
|
|
|
+ componentProps: {
|
|
|
+ api: () => fetchGetRoleAllList(),
|
|
|
+ labelFeild: 'roleName',
|
|
|
+ valueFeild: 'roleId',
|
|
|
+ multiple: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'status',
|
|
|
+ label: '状态',
|
|
|
+ component: 'NRadioGroup',
|
|
|
+ required: true,
|
|
|
+ componentProps: {
|
|
|
+ options: [
|
|
|
+ {
|
|
|
+ label: '启用',
|
|
|
+ value: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '禁用',
|
|
|
+ value: 0
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ defaultValue: 1
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ labelWidth: 120,
|
|
|
+ gridProps: {
|
|
|
+ cols: '1'
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+async function handleSubmit() {
|
|
|
+ const form = await getFieldsValue();
|
|
|
+ if (form.userId) {
|
|
|
+ await fetchEditUser(form as Api.SystemManage.UserModel);
|
|
|
+ } else {
|
|
|
+ await fetchAddUser(form as Api.SystemManage.UserModel);
|
|
|
+ }
|
|
|
+ closeModal();
|
|
|
+ refresh();
|
|
|
}
|
|
|
|
|
|
-function edit(id: number) {
|
|
|
- handleEdit(id);
|
|
|
+async function edit(row: Recordable) {
|
|
|
+ const res = await fetchDetaileUser(row.userId);
|
|
|
+ openModal(row);
|
|
|
+ setFieldsValue({ ...res.data, userId: row.userId });
|
|
|
+ updateSchema([
|
|
|
+ { field: 'password', required: false },
|
|
|
+ { field: 'comfirmPassword', required: false }
|
|
|
+ ]);
|
|
|
}
|
|
|
</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="getData"
|
|
|
- />
|
|
|
+ <LayoutTable>
|
|
|
+ <ZTable
|
|
|
+ :table-config="{
|
|
|
+ api: fetchGetUserList,
|
|
|
+ columns: columns,
|
|
|
+ keyField: 'userId',
|
|
|
+ title: '用户列表',
|
|
|
+ showAddButton: true
|
|
|
+ }"
|
|
|
+ @register="registerTable"
|
|
|
+ @add="openModal"
|
|
|
+ >
|
|
|
+ <template #op="{ row }">
|
|
|
+ <NButton v-if="row.userId != 1" size="small" ghost type="primary" @click="edit(row)">编辑</NButton>
|
|
|
+ <NPopconfirm v-if="row.userId != 1" @positive-click="handleDelete(row)">
|
|
|
+ <template #trigger>
|
|
|
+ <NButton size="small" type="error" ghost>删除</NButton>
|
|
|
+ </template>
|
|
|
+ 确定删除吗?
|
|
|
+ </NPopconfirm>
|
|
|
</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="mobilePagination"
|
|
|
- class="sm:h-full"
|
|
|
- />
|
|
|
- <UserOperateDrawer
|
|
|
- v-model:visible="drawerVisible"
|
|
|
- :operate-type="operateType"
|
|
|
- :row-data="editingData"
|
|
|
- @submitted="getDataByPage"
|
|
|
- />
|
|
|
- </NCard>
|
|
|
- </div>
|
|
|
+ </ZTable>
|
|
|
+ <BasicModelForm @register-modal-form="registerModalForm" @submit-form="handleSubmit"></BasicModelForm>
|
|
|
+ </LayoutTable>
|
|
|
</template>
|
|
|
|
|
|
<style scoped></style>
|