tansci/antdv-next-admin/src/views/examples/table/index.vue
李龙龙 a5176016b3 feat: 优化字典管理页面布局,修复部门人员操作列,完善LDAP集成
- 字典管理页面改为左右分栏 a-menu 布局,对齐系统配置页面风格
- 左侧菜单项显示字典名称、编码(hover展开)及数据数量徽标
- 修复部门人员操作列因 ProTable dataIndex='action' 拦截导致按钮不显示
- 字典类型编辑/删除移至右侧工具栏,操作列增加序号列
- 新增 LDAP 配置管理与同步功能(UnboundID LDAP SDK)
- 清理废弃的 fantastic-admin 目录
2026-05-18 18:00:41 +08:00

322 lines
7.7 KiB
Vue

<template>
<div class="page-container">
<ProTable
:columns="columns"
:request="fetchData"
:toolbar="{
title: $t('exampleTable.userList'),
subTitle: $t('exampleTable.subTitle'),
actions: ['refresh', 'columnSetting'],
}"
:search="{
labelWidth: 80,
defaultCollapsed: true,
}"
:header-filter="{
defaultMode: 'server',
requestPayload: 'flat',
}"
row-key="id"
>
<template #toolbar-actions>
<a-button type="primary" class="create-user-btn" @click="handleCreate">
<PlusOutlined /> {{ $t("exampleTable.createUser") }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'status'">
<a-switch
:checked="record.status === 'active'"
@change="handleStatusChange(record, $event as boolean)"
>
<template #checkedChildren>{{ $t("user.active") }}</template>
<template #unCheckedChildren>{{ $t("user.inactive") }}</template>
</a-switch>
</template>
<template v-else-if="column.dataIndex === 'gender'">
<a-tag :color="genderValueEnum[record.gender]?.color">
{{ genderValueEnum[record.gender]?.text || record.gender }}
</a-tag>
</template>
</template>
</ProTable>
<!-- Create/Edit Modal -->
<a-modal
v-model:open="modalVisible"
:title="
editingId ? $t('exampleTable.editUser') : $t('exampleTable.createUser')
"
width="600px"
@ok="handleSubmit"
>
<ProForm
ref="formRef"
:form-items="formItems"
:initial-values="formData"
:grid="{ cols: 2, gutter: 16 }"
/>
</a-modal>
</div>
</template>
<script setup lang="ts">
import type { ProTableColumn, ProFormItem } from "@/types/pro";
import type { User } from "@/types/auth";
import type { PageParams } from "@/types/api";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@antdv-next/icons";
import { message } from "antdv-next";
import { ref, computed } from "vue";
import { getUserList, createUser, updateUser, deleteUser } from "@/api/user";
import ProForm from "@/components/Pro/ProForm/index.vue";
import ProTable from "@/components/Pro/ProTable/index.vue";
import { $t } from "@/locales";
import { commonRules } from "@/utils/formRules";
const modalVisible = ref(false);
const editingId = ref<string | null>(null);
const formRef = ref();
const formData = ref({});
const genderOptions = computed(() => [
{ label: $t("user.male"), value: "male" },
{ label: $t("user.female"), value: "female" },
]);
const genderValueEnum = computed<
Record<string, { text: string; color?: string }>
>(() => ({
male: { text: $t("user.male"), color: "blue" },
female: { text: $t("user.female"), color: "pink" },
}));
// Table columns configuration
const columns = computed<ProTableColumn[]>(() => [
{
title: $t("user.username"),
dataIndex: "username",
width: 150,
fixed: "left",
headerFilter: {
type: "keyword",
mode: "server",
icon: "search",
placeholder: `搜索${$t("user.username")}`,
matchAllKeywords: true,
},
},
{
title: $t("user.email"),
dataIndex: "email",
search: true,
searchType: "input",
copyable: true,
},
{
title: $t("user.realName"),
dataIndex: "realName",
search: true,
searchType: "input",
},
{
title: $t("user.phone"),
dataIndex: "phone",
},
{
title: $t("user.gender"),
dataIndex: "gender",
search: true,
searchType: "select",
searchOptions: genderOptions.value,
headerFilter: {
type: "select",
mode: "server",
icon: "filter",
multiple: true,
options: genderOptions.value,
},
valueType: "tag",
valueEnum: genderValueEnum.value,
},
{
title: $t("common.status"),
dataIndex: "status",
width: 150,
headerFilter: {
type: "select",
mode: "server",
icon: "filter",
multiple: false,
options: [
{ label: $t("user.active"), value: "active" },
{ label: $t("user.inactive"), value: "inactive" },
],
},
},
{
title: $t("common.createTime"),
dataIndex: "createdAt",
valueType: "dateTime",
search: true,
searchType: "dateRange",
sorter: true,
},
{
title: $t("common.actions"),
dataIndex: "action",
width: 200,
fixed: "right",
actions: [
{
label: $t("common.edit"),
icon: EditOutlined,
onClick: (record) => handleEdit(record as unknown as User),
},
{
label: $t("common.delete"),
icon: DeleteOutlined,
danger: true,
confirm: $t("user.confirmDelete"),
onClick: (record) => handleDelete(record as unknown as User),
},
],
},
]);
// Form items configuration
const formItems = computed<ProFormItem[]>(() => [
{
name: "username",
label: $t("user.username"),
type: "input",
required: true,
rules: [
commonRules.required(),
commonRules.length(3, 20),
commonRules.username(),
],
},
{
name: "email",
label: $t("user.email"),
type: "input",
required: true,
rules: [commonRules.required(), commonRules.email()],
},
{
name: "realName",
label: $t("user.realName"),
type: "input",
required: true,
},
{
name: "phone",
label: $t("user.phone"),
type: "input",
rules: [commonRules.phone()],
},
{
name: "gender",
label: $t("user.gender"),
type: "radio",
required: true,
options: genderOptions.value,
},
{
name: "status",
label: $t("common.status"),
type: "radio",
required: true,
initialValue: "active",
options: [
{ label: $t("user.active"), value: "active" },
{ label: $t("user.inactive"), value: "inactive" },
],
},
{
name: "bio",
label: $t("user.bio"),
type: "textarea",
colSpan: 2,
props: {
rows: 4,
maxLength: 200,
showCount: true,
},
},
]);
// Methods
const fetchData = async (params: PageParams) => {
const res = await getUserList(params);
return {
data: res.data.list,
total: res.data.total,
success: true,
};
};
const handleCreate = () => {
editingId.value = null;
formData.value = {};
modalVisible.value = true;
};
const handleEdit = (record: User) => {
editingId.value = record.id;
formData.value = { ...record };
modalVisible.value = true;
};
const handleDelete = async (record: User) => {
await deleteUser(record.id);
message.success($t("exampleTable.deleteSuccess"));
};
const handleSubmit = async () => {
const valid = await formRef.value?.validate();
if (!valid) return;
const values = formRef.value?.getFieldsValue();
try {
if (editingId.value) {
await updateUser({ ...values, id: editingId.value });
message.success($t("exampleTable.updateSuccess"));
} else {
await createUser(values);
message.success($t("exampleTable.createSuccess"));
}
modalVisible.value = false;
} catch (error: unknown) {
message.error((error as Error).message || $t("exampleTable.createSuccess"));
}
};
const handleStatusChange = async (record: User, checked: boolean) => {
try {
const newStatus = checked ? "active" : "inactive";
await updateUser({ ...record, id: record.id, status: newStatus });
record.status = newStatus;
message.success($t("exampleTable.updateSuccess"));
} catch (error: unknown) {
message.error((error as Error).message || $t("common.error"));
}
};
</script>
<style scoped lang="scss">
.create-user-btn {
box-shadow: 0 4px 14px rgba(24, 119, 255, 0.3);
border: none;
&:hover {
box-shadow: 0 8px 18px rgba(24, 119, 255, 0.36);
transform: translateY(-1px);
}
}
</style>