Browse Source

将数据字典的管理合并到一个vue中.

master
庄锐波 5 months ago
parent
commit
71f6f8bdb9
  1. 3
      script/sql/zfjd-refactoring-v1/005-重新生成菜单.sql
  2. 518
      zdxt-web-client/zdxt-admin-plus-ui/src/views/system/dict/index-manager.vue

3
script/sql/zfjd-refactoring-v1/005-重新生成菜单.sql

@ -123,3 +123,6 @@ INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component,
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1623, '配置删除', 118, 6, '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, '2026-03-31 20:14:46', null, null, ''); INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1623, '配置删除', 118, 6, '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, '2026-03-31 20:14:46', null, null, '');
update sys_menu set system_code = 'authService' where create_time='2026-03-31 20:14:46'; update sys_menu set system_code = 'authService' where create_time='2026-03-31 20:14:46';
UPDATE sys_menu SET menu_name = '菜单管理', parent_id = 1, order_num = 3, path = 'menu', component = 'system/menu/index-groupby', query_param = '', is_frame = 1, is_cache = 0, url = '#', target = '', menu_type = 'C', visible = '0', status = '0', perms = 'system:menu:list', icon = 'tree-table', create_dept = '103', create_by = 1, create_time = '2026-03-31 20:14:46', update_by = null, update_time = null, remark = '菜单管理菜单', system_code = 'authService', type = null WHERE menu_id = 102;
UPDATE sys_menu SET menu_name = '字典管理', parent_id = 1, order_num = 6, path = 'dict', component = 'system/dict/index-manager', query_param = '', is_frame = 1, is_cache = 0, url = '#', target = '', menu_type = 'C', visible = '0', status = '0', perms = 'system:dict:list', icon = 'dict', create_dept = '103', create_by = 1, create_time = '2026-03-31 20:14:46', update_by = null, update_time = null, remark = '字典管理菜单', system_code = 'authService', type = null WHERE menu_id = 105;

518
zdxt-web-client/zdxt-admin-plus-ui/src/views/system/dict/index-manager.vue

@ -0,0 +1,518 @@
<template>
<div class="p-2">
<el-row :gutter="10">
<!-- 左侧: 字典类型管理 -->
<el-col :span="10">
<el-card shadow="hover" class="dict-card">
<template #header>
<div class="card-header">
<span class="card-header-title">字典类型</span>
<div class="card-header-actions">
<el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" size="small" @click="handleTypeAdd">新增</el-button>
<el-button v-hasPermi="['system:dict:remove']" type="danger" plain icon="Refresh" size="small" @click="handleRefreshCache">刷新缓存</el-button>
</div>
</div>
</template>
<!-- 搜索区域 -->
<el-form ref="typeQueryFormRef" :model="typeQueryParams" :inline="true" class="search-form">
<el-form-item label="字典名称" prop="dictName">
<el-input v-model="typeQueryParams.dictName" placeholder="请输入字典名称" clearable size="small" @keyup.enter="handleTypeQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" size="small" @click="handleTypeQuery">搜索</el-button>
<el-button icon="Refresh" size="small" @click="resetTypeQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 字典类型表格 -->
<el-table
v-loading="typeLoading"
border
:data="typeList"
highlight-current-row
@current-change="handleTypeCurrentChange"
@selection-change="handleTypeSelectionChange"
size="small"
>
<el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />
<el-table-column label="字典类型" align="center" prop="dictType" :show-overflow-tooltip="true" />
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['system:dict:edit']" link type="primary" icon="Edit" @click="handleTypeUpdate(scope.row)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['system:dict:remove']" link type="primary" icon="Delete" @click="handleTypeDelete(scope.row)" />
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination
v-show="typeTotal > 0"
v-model:page="typeQueryParams.pageNum"
v-model:limit="typeQueryParams.pageSize"
:total="typeTotal"
:page-sizes="[10, 20, 50]"
layout="total, prev, pager, next"
small
@pagination="getTypeList"
/>
</el-card>
</el-col>
<!-- 右侧: 字典数据管理 -->
<el-col :span="14">
<el-card shadow="hover" class="dict-card">
<template #header>
<div class="card-header">
<span class="card-header-title">
字典数据
<el-tag v-if="currentDictType" type="primary" size="small" class="ml-2">{{ currentDictTypeName }}</el-tag>
</span>
<div class="card-header-actions">
<el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" size="small" :disabled="!currentDictType" @click="handleDataAdd">新增</el-button>
<el-button v-hasPermi="['system:dict:export']" type="warning" plain icon="Download" size="small" :disabled="!currentDictType" @click="handleDataExport">导出</el-button>
</div>
</div>
</template>
<!-- 搜索区域 -->
<el-form ref="dataQueryFormRef" :model="dataQueryParams" :inline="true" class="search-form">
<el-form-item label="字典标签" prop="dictLabel">
<el-input v-model="dataQueryParams.dictLabel" placeholder="请输入字典标签" clearable size="small" @keyup.enter="handleDataQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" size="small" @click="handleDataQuery">搜索</el-button>
<el-button icon="Refresh" size="small" @click="resetDataQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 字典数据表格 -->
<el-table v-loading="dataLoading" border :data="dataList" size="small">
<el-table-column label="字典标签" align="center" prop="dictLabel">
<template #default="scope">
<span v-if="(scope.row.listClass === '' || scope.row.listClass === 'default') && (scope.row.cssClass === '' || scope.row.cssClass == null)">
{{ scope.row.dictLabel }}
</span>
<el-tag
v-else
:type="scope.row.listClass === 'primary' || scope.row.listClass === 'default' ? 'primary' : scope.row.listClass"
:class="scope.row.cssClass"
>
{{ scope.row.dictLabel }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="字典键值" align="center" prop="dictValue" />
<el-table-column label="字典排序" align="center" prop="dictSort" width="80" />
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['system:dict:edit']" link type="primary" icon="Edit" @click="handleDataUpdate(scope.row)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['system:dict:remove']" link type="primary" icon="Delete" @click="handleDataDelete(scope.row)" />
</el-tooltip>
</template>
</el-table-column>
</el-table>
<!-- 无选中提示 -->
<el-empty v-if="!currentDictType && !dataLoading" description="请在左侧选择一个字典类型" :image-size="80" />
<pagination
v-show="dataTotal > 0"
v-model:page="dataQueryParams.pageNum"
v-model:limit="dataQueryParams.pageSize"
:total="dataTotal"
:page-sizes="[10, 20, 50]"
layout="total, prev, pager, next"
small
@pagination="getDataList"
/>
</el-card>
</el-col>
</el-row>
<!-- 添加或修改字典类型对话框 -->
<el-dialog v-model="typeDialog.visible" :title="typeDialog.title" width="500px" append-to-body>
<el-form ref="typeFormRef" :model="typeForm" :rules="typeRules" label-width="80px">
<el-form-item label="字典名称" prop="dictName">
<el-input v-model="typeForm.dictName" placeholder="请输入字典名称" />
</el-form-item>
<el-form-item label="字典类型" prop="dictType">
<el-input v-model="typeForm.dictType" placeholder="请输入字典类型" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="typeForm.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitTypeForm"> </el-button>
<el-button @click="cancelType"> </el-button>
</div>
</template>
</el-dialog>
<!-- 添加或修改字典数据对话框 -->
<el-dialog v-model="dataDialog.visible" :title="dataDialog.title" width="500px" append-to-body>
<el-form ref="dataFormRef" :model="dataForm" :rules="dataRules" label-width="80px">
<el-form-item label="字典类型">
<el-input v-model="dataForm.dictType" :disabled="true" />
</el-form-item>
<el-form-item label="数据标签" prop="dictLabel">
<el-input v-model="dataForm.dictLabel" placeholder="请输入数据标签" />
</el-form-item>
<el-form-item label="数据键值" prop="dictValue">
<el-input v-model="dataForm.dictValue" placeholder="请输入数据键值" />
</el-form-item>
<el-form-item label="样式属性" prop="cssClass">
<el-input v-model="dataForm.cssClass" placeholder="请输入样式属性" />
</el-form-item>
<el-form-item label="显示排序" prop="dictSort">
<el-input-number v-model="dataForm.dictSort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="回显样式" prop="listClass">
<el-select v-model="dataForm.listClass">
<el-option
v-for="item in listClassOptions"
:key="item.value"
:label="item.label + '(' + item.value + ')'"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="dataForm.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitDataForm"> </el-button>
<el-button @click="cancelData"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="DictManager" lang="ts">
import { useDictStore } from '@/store/modules/dict';
import { listType, getType, delType, addType, updateType, refreshCache } from '@/api/system/dict/type';
import { listData, getData, delData, addData, updateData } from '@/api/system/dict/data';
import { DictTypeForm, DictTypeQuery, DictTypeVO } from '@/api/system/dict/type/types';
import { DictDataForm, DictDataQuery, DictDataVO } from '@/api/system/dict/data/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// ==================== ====================
const typeList = ref<DictTypeVO[]>([]);
const typeLoading = ref(true);
const typeTotal = ref(0);
const currentDictType = ref('');
const currentDictTypeName = ref('');
const typeQueryFormRef = ref<ElFormInstance>();
const typeFormRef = ref<ElFormInstance>();
const typeDialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initTypeForm: DictTypeForm = {
dictId: undefined,
dictName: '',
dictType: '',
remark: ''
};
const typeQueryParams = reactive<DictTypeQuery>({
pageNum: 1,
pageSize: 20,
dictName: '',
dictType: ''
});
const typeForm = ref<DictTypeForm>({ ...initTypeForm });
const typeRules = {
dictName: [{ required: true, message: '字典名称不能为空', trigger: 'blur' }],
dictType: [{ required: true, message: '字典类型不能为空', trigger: 'blur' }]
};
/** 查询字典类型列表 */
const getTypeList = async () => {
typeLoading.value = true;
const res = await listType(typeQueryParams);
typeList.value = res.rows;
typeTotal.value = res.total;
typeLoading.value = false;
};
/** 字典类型行点击 - 切换右侧数据 */
const handleTypeCurrentChange = (row: DictTypeVO | null) => {
if (row) {
currentDictType.value = row.dictType;
currentDictTypeName.value = row.dictName;
dataQueryParams.dictType = row.dictType;
dataQueryParams.pageNum = 1;
dataQueryParams.dictLabel = '';
getDataList();
}
};
/** 字典类型搜索 */
const handleTypeQuery = () => {
typeQueryParams.pageNum = 1;
getTypeList();
};
/** 字典类型重置 */
const resetTypeQuery = () => {
typeQueryFormRef.value?.resetFields();
handleTypeQuery();
};
/** 字典类型新增 */
const handleTypeAdd = () => {
typeForm.value = { ...initTypeForm };
typeFormRef.value?.resetFields();
typeDialog.visible = true;
typeDialog.title = '添加字典类型';
};
/** 字典类型修改 */
const handleTypeUpdate = async (row: DictTypeVO) => {
typeForm.value = { ...initTypeForm };
typeFormRef.value?.resetFields();
const res = await getType(row.dictId);
typeForm.value = { ...res.data };
typeDialog.visible = true;
typeDialog.title = '修改字典类型';
};
/** 字典类型提交 */
const submitTypeForm = () => {
typeFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
typeForm.value.dictId ? await updateType(typeForm.value) : await addType(typeForm.value);
proxy?.$modal.msgSuccess('操作成功');
typeDialog.visible = false;
getTypeList();
}
});
};
/** 字典类型取消 */
const cancelType = () => {
typeDialog.visible = false;
typeForm.value = { ...initTypeForm };
typeFormRef.value?.resetFields();
};
/** 字典类型删除 */
const handleTypeDelete = async (row: DictTypeVO) => {
await proxy?.$modal.confirm('是否确认删除字典名称为"' + row.dictName + '"的数据项?');
await delType(row.dictId);
getTypeList();
//
if (currentDictType.value === row.dictType) {
currentDictType.value = '';
currentDictTypeName.value = '';
dataList.value = [];
dataTotal.value = 0;
}
proxy?.$modal.msgSuccess('删除成功');
};
/** 字典类型多选(预留) */
const handleTypeSelectionChange = (_selection: DictTypeVO[]) => {};
/** 刷新缓存 */
const handleRefreshCache = async () => {
await refreshCache();
proxy?.$modal.msgSuccess('刷新成功');
useDictStore().cleanDict();
};
// ==================== ====================
const dataList = ref<DictDataVO[]>([]);
const dataLoading = ref(false);
const dataTotal = ref(0);
const dataQueryFormRef = ref<ElFormInstance>();
const dataFormRef = ref<ElFormInstance>();
const dataDialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initDataForm: DictDataForm = {
dictCode: undefined,
dictLabel: '',
dictValue: '',
cssClass: '',
listClass: 'primary',
dictSort: 0,
remark: ''
};
const dataQueryParams = reactive<DictDataQuery>({
pageNum: 1,
pageSize: 20,
dictName: '',
dictType: '',
dictLabel: ''
});
const dataForm = ref<DictDataForm>({ ...initDataForm });
const dataRules = {
dictLabel: [{ required: true, message: '数据标签不能为空', trigger: 'blur' }],
dictValue: [{ required: true, message: '数据键值不能为空', trigger: 'blur' }],
dictSort: [{ required: true, message: '数据顺序不能为空', trigger: 'blur' }]
};
//
const listClassOptions = ref<Array<{ value: string; label: string }>>([
{ value: 'default', label: '默认' },
{ value: 'primary', label: '主要' },
{ value: 'success', label: '成功' },
{ value: 'info', label: '信息' },
{ value: 'warning', label: '警告' },
{ value: 'danger', label: '危险' }
]);
/** 查询字典数据列表 */
const getDataList = async () => {
if (!currentDictType.value) return;
dataLoading.value = true;
const res = await listData(dataQueryParams);
dataList.value = res.rows;
dataTotal.value = res.total;
dataLoading.value = false;
};
/** 字典数据搜索 */
const handleDataQuery = () => {
dataQueryParams.pageNum = 1;
getDataList();
};
/** 字典数据重置 */
const resetDataQuery = () => {
dataQueryFormRef.value?.resetFields();
dataQueryParams.dictType = currentDictType.value;
handleDataQuery();
};
/** 字典数据新增 */
const handleDataAdd = () => {
dataForm.value = { ...initDataForm };
dataFormRef.value?.resetFields();
dataForm.value.dictType = currentDictType.value;
dataDialog.visible = true;
dataDialog.title = '添加字典数据';
};
/** 字典数据修改 */
const handleDataUpdate = async (row: DictDataVO) => {
dataForm.value = { ...initDataForm };
dataFormRef.value?.resetFields();
const res = await getData(row.dictCode);
dataForm.value = { ...res.data };
dataDialog.visible = true;
dataDialog.title = '修改字典数据';
};
/** 字典数据提交 */
const submitDataForm = () => {
dataFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
dataForm.value.dictCode ? await updateData(dataForm.value) : await addData(dataForm.value);
useDictStore().removeDict(currentDictType.value);
proxy?.$modal.msgSuccess('操作成功');
dataDialog.visible = false;
getDataList();
}
});
};
/** 字典数据取消 */
const cancelData = () => {
dataDialog.visible = false;
dataForm.value = { ...initDataForm };
dataFormRef.value?.resetFields();
};
/** 字典数据删除 */
const handleDataDelete = async (row: DictDataVO) => {
await proxy?.$modal.confirm('是否确认删除字典标签为"' + row.dictLabel + '"的数据项?');
await delData(row.dictCode);
await getDataList();
proxy?.$modal.msgSuccess('删除成功');
useDictStore().removeDict(currentDictType.value);
};
/** 字典数据导出 */
const handleDataExport = () => {
proxy?.download(
'system/dict/data/export',
{
...dataQueryParams
},
`dict_data_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getTypeList();
});
</script>
<style scoped lang="scss">
.dict-card {
min-height: calc(100vh - 120px);
:deep(.el-card__header) {
padding: 12px 16px;
}
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-header-title {
font-weight: bold;
font-size: 14px;
display: flex;
align-items: center;
}
.card-header-actions {
display: flex;
gap: 8px;
}
.search-form {
margin-bottom: 10px;
:deep(.el-form-item) {
margin-bottom: 8px;
}
}
.ml-2 {
margin-left: 8px;
}
</style>
Loading…
Cancel
Save