This commit is contained in:
吴红兵
2025-12-02 10:37:49 +08:00
commit 1f645dad3e
1183 changed files with 147673 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
<template>
<el-dialog v-model="visible" :title="dataForm.id ? $t('common.editBtn') : $t('common.addBtn')" width="600">
<el-form ref="dicDialogFormRef" :model="dataForm" label-width="90px" :rules="dataRules" v-loading="loading">
<el-form-item :label="$t('dictItem.dictType')" prop="dictType">
<el-input v-model="dataForm.dictType" clearable disabled
:placeholder="$t('dictItem.inputDictTypeTip')"></el-input>
</el-form-item>
<el-form-item :label="$t('dictItem.label')" prop="label">
<el-input v-model="dataForm.label" :placeholder="$t('dictItem.inputLabelTip')" clearable></el-input>
</el-form-item>
<el-form-item :label="$t('dictItem.itemValue')" prop="value">
<el-input v-model="dataForm.value" :placeholder="$t('dictItem.inputItemValueTip')" clearable></el-input>
</el-form-item>
<el-form-item :label="$t('dictItem.description')" prop="description">
<el-input v-model="dataForm.description" :placeholder="$t('dictItem.inputDescriptionTip')" clearable></el-input>
</el-form-item>
<el-form-item :label="$t('dictItem.sortOrder')" prop="sortOrder">
<el-input-number v-model="dataForm.sortOrder" :placeholder="$t('dictItem.inputSortOrderTip')"
clearable></el-input-number>
</el-form-item>
<el-form-item :label="$t('dictItem.remarks')" prop="remarks">
<el-input type="textarea" maxlength="100" :rows="3" v-model="dataForm.remarks"
:placeholder="$t('dictItem.inputRemarksTip')"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="dict-item-form">
import {useI18n} from 'vue-i18n';
import {getItemObj, addItemObj, putItemObj, validateDictItemLabel} from '/@/api/admin/dict';
import {useMessage} from '/@/hooks/message';
import {rule} from "/@/utils/validate";
// 定义子组件向父组件传值/事件
const emit = defineEmits(['refresh']);
const {t} = useI18n();
// 定义变量内容
const dicDialogFormRef = ref();
const visible = ref(false);
const loading = ref(false);
const dataForm = reactive({
id: '',
dictId: '',
dictType: '',
value: '',
label: '',
description: '',
sortOrder: 0,
remarks: '',
});
const dataRules = reactive({
dictType: [{validator: rule.overLength, trigger: 'blur'}, {
required: true,
message: '请点选左侧字典项',
trigger: 'blur'
}],
value: [{validator: rule.overLength, trigger: 'blur'}, {required: true, message: '数据值不能为空', trigger: 'blur'}],
label: [
{validator: rule.overLength, trigger: 'blur'},
{required: true, message: '标签不能为空', trigger: 'blur'},
{
validator: (rule: any, value: any, callback: any) => {
validateDictItemLabel(rule, value, callback, dataForm.dictType, dataForm.id !== '');
},
trigger: 'blur',
},
],
description: [{validator: rule.overLength, trigger: 'blur'}, {
required: true,
message: '描述不能为空',
trigger: 'blur'
}],
sortOrder: [{validator: rule.overLength, trigger: 'blur'}, {
required: true,
message: '排序不能为空',
trigger: 'blur'
}],
});
// 打开弹窗
const openDialog = (row: any, dictForm: any) => {
visible.value = true;
dataForm.id = '';
nextTick(() => {
dicDialogFormRef.value?.resetFields();
if (dictForm) {
dataForm.dictId = dictForm.dictId;
dataForm.dictType = dictForm.dictType;
}
});
if (row?.id) {
getItemObj(row.id).then((res) => {
Object.assign(dataForm, res.data);
});
}
};
// 提交
const onSubmit = async () => {
const valid = await dicDialogFormRef.value.validate().catch(() => {
});
if (!valid) return false;
try {
loading.value = true;
dataForm.id ? await putItemObj(dataForm) : await addItemObj(dataForm);
useMessage().success(t(dataForm.id ? 'common.editSuccessText' : 'common.addSuccessText'));
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg);
} finally {
loading.value = false;
}
};
// 暴露变量
defineExpose({
openDialog,
});
</script>

View File

@@ -0,0 +1,86 @@
<template>
<div class="layout-padding-auto layout-padding-view">
<div class="mb8">
<el-button icon="folder-add" type="primary" class="ml10" @click="dictformRef.openDialog(null, state.queryForm)">
{{ $t('common.addBtn') }}
</el-button>
<right-toolbar :search="false" class="ml10" style="float: right; margin-right: 20px" @queryTable="getDataList"></right-toolbar>
</div>
<el-table
:data="state.dataList"
v-loading="state.loading"
style="width: 100%"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
>
<el-table-column prop="dictType" :label="$t('dictItem.dictType')" show-overflow-tooltip></el-table-column>
<el-table-column prop="value" :label="$t('dictItem.itemValue')" show-overflow-tooltip></el-table-column>
<el-table-column prop="label" :label="$t('dictItem.label')" show-overflow-tooltip></el-table-column>
<el-table-column prop="description" :label="$t('dictItem.description')" show-overflow-tooltip></el-table-column>
<el-table-column prop="sortOrder" :label="$t('dictItem.sortOrder')" show-overflow-tooltip></el-table-column>
<el-table-column prop="remarks" :label="$t('dictItem.remarks')" show-overflow-tooltip></el-table-column>
<el-table-column prop="createTime" :label="$t('dictItem.createTime')" show-overflow-tooltip></el-table-column>
<el-table-column :label="$t('common.action')" width="150">
<template #default="scope">
<el-button icon="edit-pen" text type="primary" @click="dictformRef.openDialog(scope.row)"> {{ $t('common.editBtn') }} </el-button>
<el-button icon="delete" text type="primary" @click="handleDelete(scope.row)">
{{ $t('common.delBtn') }}
</el-button>
</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination"> </pagination>
<dict-form ref="dictformRef" @refresh="getDataList"></dict-form>
</div>
</template>
<script setup lang="ts" name="dict-item">
import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchItemList, delItemObj } from '/@/api/admin/dict';
import { useMessage, useMessageBox } from '/@/hooks/message';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const visible = ref(false);
const DictForm = defineAsyncComponent(() => import('./form.vue'));
const dictformRef = ref();
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {
dictId: '',
dictType: '',
},
createdIsNeed: false,
pageList: fetchItemList,
});
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm(t('common.delConfirmText'));
} catch {
return;
}
try {
await delItemObj(row.id);
getDataList();
useMessage().success(t('common.delSuccessText'));
} catch (err: any) {
useMessage().error(err.msg);
}
};
const open = (row: any) => {
state.queryForm.dictId = row.id;
state.queryForm.dictType = row.dictType;
visible.value = true;
getDataList();
};
// 暴露变量
defineExpose({
open,
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,114 @@
<template>
<el-dialog :title="dataForm.id ? $t('common.editBtn') : $t('common.addBtn')" v-model="visible" width="600">
<el-form :model="dataForm" :rules="dataRules" label-width="100px" ref="dicDialogFormRef" v-loading="loading">
<el-form-item :label="$t('sysdict.systemFlag')" prop="systemFlag">
<el-radio-group v-model="dataForm.systemFlag">
<el-radio border :key="index" :label="item.value" v-for="(item, index) in dict_type">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('sysdict.dictType')" prop="dictType">
<el-input :placeholder="$t('sysdict.inputDictTypeTip')" :disabled="dataForm.id !== ''" clearable v-model="dataForm.dictType"></el-input>
</el-form-item>
<el-form-item :label="$t('sysdict.description')" prop="description">
<el-input :placeholder="$t('sysdict.inputDescriptionTip')" clearable v-model="dataForm.description"></el-input>
</el-form-item>
<el-form-item :label="$t('sysdict.remarks')" prop="remarks">
<el-input type="textarea" maxlength="100" :rows="3" :placeholder="$t('sysdict.inputRemarksTip')" v-model="dataForm.remarks"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
<el-button @click="onSubmit" type="primary" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" name="systemDicDialog" setup>
import { useI18n } from 'vue-i18n';
import { addObj, getObj, putObj, validateDictType } from '/@/api/admin/dict';
import { useDict } from '/@/hooks/dict';
import { useMessage } from '/@/hooks/message';
import { rule } from '/@/utils/validate';
// 定义子组件向父组件传值/事件
const emit = defineEmits(['refresh']);
const { dict_type } = useDict('dict_type');
const { t } = useI18n();
// 定义变量内容
const dicDialogFormRef = ref();
const visible = ref(false);
const loading = ref(false);
const dataForm = reactive({
id: '',
dictType: '',
description: '',
systemFlag: '0',
remarks: '',
});
const dataRules = reactive({
dictType: [
{ validator: rule.overLength, trigger: 'blur' },
{ required: true, message: '类型不能为空', trigger: 'blur' },
{ validator: rule.validatorNameCn, trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
validateDictType(rule, value, callback, dataForm.id !== '');
},
trigger: 'blur',
},
],
systemFlag: [{ required: true, message: '字典类型不能为空', trigger: 'blur' }],
description: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '描述不能为空', trigger: 'blur' }],
});
// 打开弹窗
const openDialog = (id: string) => {
visible.value = true;
dataForm.id = '';
nextTick(() => {
dicDialogFormRef.value?.resetFields();
});
if (id) {
getObj(id).then((res) => {
Object.assign(dataForm, res.data);
});
}
};
// 提交
const onSubmit = async () => {
// 立即设置 loading防止重复点击
if (loading.value) return;
loading.value = true;
try {
const valid = await dicDialogFormRef.value.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
const result = dataForm.id ? await putObj(dataForm) : await addObj(dataForm);
useMessage().success(t(dataForm.id ? 'common.editSuccessText' : 'common.addSuccessText'));
visible.value = false;
emit('refresh', result.data);
} catch (err: any) {
useMessage().error(err.msg);
} finally {
loading.value = false;
}
};
// 暴露变量
defineExpose({
openDialog,
});
</script>

View File

@@ -0,0 +1,63 @@
export default {
sysdict: {
index: '#',
importsysDictTip: 'import SysDict',
id: 'id',
dictType: 'dictType',
description: 'description',
createBy: 'createBy',
updateBy: 'updateBy',
createTime: 'createTime',
updateTime: 'updateTime',
remarks: 'remarks',
systemFlag: 'systemFlag',
inputDictTypeTip: 'input dictType',
inputDescriptionTip: 'input description',
inputCreateByTip: 'input createBy',
inputUpdateByTip: 'input updateBy',
inputCreateTimeTip: 'input createTime',
inputUpdateTimeTip: 'input updateTime',
inputRemarksTip: 'input remarks',
inputSystemFlagTip: 'input systemFlag',
inputDelFlagTip: 'input delFlag',
inputTenantIdTip: 'input tenantId',
dictItem: 'dict item',
deleteDisabledTip: 'system data cannot be deleted ',
},
dictItem: {
index: '#',
name: 'dict item',
importsysDictItemTip: 'import SysDictItem',
id: 'id',
dictId: 'dictId',
itemValue: 'itemValue',
label: 'label',
dictType: 'dictType',
description: 'description',
sortOrder: 'sortOrder',
createBy: 'createBy',
updateBy: 'updateBy',
createTime: 'createTime',
updateTime: 'updateTime',
remarks: 'remarks',
delFlag: 'delFlag',
tenantId: 'tenantId',
inputIdTip: 'input id',
inputDictIdTip: 'input dictId',
inputItemValueTip: 'input itemValue',
inputLabelTip: 'input label',
inputDictTypeTip: 'input dictType',
inputDescriptionTip: 'input description',
inputSortOrderTip: 'input sortOrder',
inputCreateByTip: 'input createBy',
inputUpdateByTip: 'input updateBy',
inputCreateTimeTip: 'input createTime',
inputUpdateTimeTip: 'input updateTime',
inputRemarksTip: 'input remarks',
inputDelFlagTip: 'input delFlag',
inputTenantIdTip: 'input tenantId',
emptyItemsError: 'dict items cannot be empty',
labelRequired: 'label cannot be empty',
valueRequired: 'value cannot be empty',
},
};

View File

@@ -0,0 +1,66 @@
export default {
sysdict: {
index: '#',
importsysDictTip: '导入字典表',
id: '编号',
dictType: '字典标识',
description: '字典名称',
createBy: '创建人',
updateBy: '修改人',
createTime: '创建时间',
updateTime: '更新时间',
remarks: '备注',
systemFlag: '配置类型',
delFlag: ' delFlag',
tenantId: '所属租户',
inputIdTip: '请输入编号',
inputDictTypeTip: '请输入字典类型',
inputDescriptionTip: '请输入描述',
inputCreateByTip: '请输入创建人',
inputUpdateByTip: '请输入修改人',
inputCreateTimeTip: '请输入创建时间',
inputUpdateTimeTip: '请输入更新时间',
inputRemarksTip: '请输入备注',
inputSystemFlagTip: '请输入字典类型',
inputDelFlagTip: '请输入 delFlag',
inputTenantIdTip: '请输入所属租户',
dictItem: '字典项',
deleteDisabledTip: '系统内置数据不能删除',
},
dictItem: {
index: '#',
name: '字典项',
importsysDictItemTip: '导入字典项',
id: '编号',
dictId: ' dictId',
itemValue: '数据值',
label: '标签名',
dictType: '类型',
description: '描述',
sortOrder: '排序',
createBy: '创建人',
updateBy: '修改人',
createTime: '创建时间',
updateTime: '更新时间',
remarks: '备注',
delFlag: ' delFlag',
tenantId: '所属租户',
inputIdTip: '请输入编号',
inputDictIdTip: '请输入 dictId',
inputItemValueTip: '请输入数据值',
inputLabelTip: '请输入标签名',
inputDictTypeTip: '请输入类型',
inputDescriptionTip: '请输入描述',
inputSortOrderTip: '请输入排序',
inputCreateByTip: '请输入创建人',
inputUpdateByTip: '请输入修改人',
inputCreateTimeTip: '请输入创建时间',
inputUpdateTimeTip: '请输入更新时间',
inputRemarksTip: '请输入备注',
inputDelFlagTip: '请输入 delFlag',
inputTenantIdTip: '请输入所属租户',
emptyItemsError: '字典项不能为空',
labelRequired: '标签名不能为空',
valueRequired: '数据值不能为空',
},
};

View File

@@ -0,0 +1,155 @@
<template>
<div class="layout-padding">
<splitpanes>
<pane class="ml10">
<splitpanes>
<pane size="30">
<div class="layout-padding-auto layout-padding-view">
<el-row>
<div class="mb8" style="width: 100%">
<el-button @click="dicDialogRef.openDialog()" class="ml10" icon="folder-add" type="primary">
{{ $t('common.addBtn') }}
</el-button>
<el-button plain @click="handleRefreshCache()" class="ml10" icon="refresh-left" type="primary">
{{ $t('common.refreshCacheBtn') }}
</el-button>
</div>
</el-row>
<el-scrollbar>
<query-tree ref="dictTreeRef" :query="state.queryList" @node-click="handleNodeClick" placeholder="请输入字典项或名称">
<template #default="{ data }">
<span class="custom-tree-node">
<span class="label">{{ data.description }}</span>
<span class="code">{{ data.dictType }}</span>
<span class="do">
<el-button-group>
<el-button icon="edit" size="small" @click.stop="dicDialogRef.openDialog(data.id)"></el-button>
<el-tooltip :content="$t('sysdict.deleteDisabledTip')" :disabled="data.systemFlag === '0'" placement="top">
<span style="margin-left: 12px">
<el-button
:disabled="data.systemFlag !== '0'"
icon="delete"
size="small"
@click.stop="handleDelete([data.id])"
></el-button>
</span>
</el-tooltip>
</el-button-group>
</span>
</span>
</template>
</query-tree>
</el-scrollbar>
<el-footer style="height: 40px; line-height: 40px">
<el-button type="primary" size="small" icon="Download" style="width: 100%" @click="exportExcel">{{
$t('common.exportBtn')
}}</el-button>
</el-footer>
</div>
</pane>
<pane class="ml8">
<DicDialog @refresh="handleRefreshTree" ref="dicDialogRef" />
<dict-item-dialog ref="dictItemDialogRef"></dict-item-dialog>
</pane>
</splitpanes>
</pane>
</splitpanes>
</div>
</template>
<script lang="ts" name="systemDic" setup>
import { delObj, fetchList, refreshCache } from '/@/api/admin/dict';
import { useMessage, useMessageBox } from '/@/hooks/message';
import { useI18n } from 'vue-i18n';
import { downBlobFile } from '/@/utils/other';
// 引入组件
const DicDialog = defineAsyncComponent(() => import('./form.vue'));
const DictItemDialog = defineAsyncComponent(() => import('./dictItem/index.vue'));
const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue'));
const { t } = useI18n();
// 定义变量内容
const dicDialogRef = ref();
const dictTreeRef = ref();
const dictItemDialogRef = ref();
const state = reactive({
queryForm: {},
queryList: (name?: string) => {
return fetchList({
name: name,
});
},
});
// 导出EXCEL
const exportExcel = () => {
downBlobFile('/admin/dict/export', state.queryForm, 'dict.xlsx');
};
//刷新缓存
const handleRefreshCache = () => {
refreshCache().then(() => {
useMessage().success('同步成功');
});
};
// 点击树
const handleNodeClick = (data: any) => {
dictItemDialogRef.value.open(data);
};
// 刷新树
const handleRefreshTree = async (data: any) => {
await dictTreeRef.value.getdeptTree();
// 选择当前编辑、新增的节点
handleNodeClick(data);
};
// 删除操作
const handleDelete = async (ids: string[]) => {
try {
await useMessageBox().confirm(t('common.delConfirmText'));
} catch {
return;
}
try {
await delObj(ids);
useMessage().success(t('common.delSuccessText'));
dictTreeRef.value.getdeptTree();
} catch (err: any) {
useMessage().error(err.msg);
}
};
</script>
<style scoped>
.menu:deep(.el-tree-node__label) {
display: flex;
flex: 1;
height: 100%;
}
.custom-tree-node {
display: flex;
flex: 1;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 24px;
height: 100%;
}
.custom-tree-node .code {
font-size: 12px;
color: #999;
}
.custom-tree-node .do {
display: none;
}
.custom-tree-node:hover .code {
display: none;
}
.custom-tree-node:hover .do {
display: inline-block;
}
</style>