Files
Web_PanelSelection_Vue_Prod/src/pages/dicmanage/index.vue
2025-11-18 08:33:12 +08:00

499 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="container">
<div class="header">
<div>字典名称</div>
<el-select v-model="currentVarName"
placeholder="请选择字典项"
@change="handleDictChange">
<el-option v-for="item in dictList"
:key="item.varName"
:label="item.varName"
:value="item.varName" />
</el-select>
</div>
<div>字典修改</div>
<!-- 区域字典特殊表格 -->
<el-table v-if="isRegionDict"
:data="regionData"
stripe
style="width: 777px">
<el-table-column prop="key" label="大区名称">
<template #default="{ row }">
<el-input v-model="row.key"
placeholder="请输入大区名称" />
</template>
</el-table-column>
<el-table-column prop="value" label="包含省份(点击修改省份/地区)" width="521">
<template #default="{ row }">
<el-button type="primary" @click="showProvinceDialog(row)"
link style="width: 100%; padding: 0; height: auto; min-height: 32px; white-space: normal; word-break: break-all; line-height: 1.5; display: inline-flex; align-items: center;">
<span style="text-align: left;">
{{ row.value.join('') || '点击选择省份' }}
</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="63">
<template #default="{ $index }">
<el-button type="danger"
:icon="Delete"
circle
@click="deleteRegion($index)" />
</template>
</el-table-column>
</el-table>
<!-- 普通字典表格 -->
<el-table v-else
:data="currentValues"
stripe
style="width: 777px">
<el-table-column prop="value" label="字典值">
<template #default="{ $index }">
<el-input v-model="currentValues[$index]" placeholder="请输入" />
</template>
</el-table-column>
<el-table-column label="操作" width="63">
<template #default="{ $index }">
<el-button type="danger"
:icon="Delete"
circle
@click="handleDelete($index)" />
</template>
</el-table-column>
</el-table>
<!-- 操作按钮区域 -->
<div class="footer">
<template v-if="isRegionDict">
<el-button type="primary"
:icon="Plus"
plain
round
@click="addNewRegion">
添加大区
</el-button>
</template>
<template v-else>
<el-button type="primary"
:icon="Plus"
plain
round
@click="handleAdd">
添加值
</el-button>
</template>
<el-button type="success"
:icon="FolderChecked"
@click="handleSave">
保存
</el-button>
</div>
<!-- 省份选择对话框 -->
<el-dialog v-model="provinceDialogVisible"
title="选择省份/地区"
width="50%">
<el-checkbox-group v-model="selectedProvinces">
<el-row :gutter="20">
<el-col v-for="province in allProvinces"
:key="province"
:span="8"
style="margin-bottom: 15px;">
<el-checkbox :label="province"
:value="province" />
</el-col>
</el-row>
</el-checkbox-group>
<template #footer>
<el-button @click="provinceDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmProvinceSelection">
确定
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, inject, computed } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Plus, Delete, Edit, UploadFilled, RefreshLeft, Upload, EditPen, FolderChecked } from '@element-plus/icons-vue';
const $http = inject('$http')
const config = inject('config');
// 初始字典数据
const dictList = reactive([]);
// 新增响应式数据
const provinceDialogVisible = ref(false);
const selectedProvinces = ref([]);
const currentEditingRegion = ref(null);
const allProvinces = computed(() => {
const provinceDict = dictList.find(d => d.varName == '省份地区');
return provinceDict?.varValue || [];
});
// 获取字典
const getDic = async () => {
try {
const response = await $http.post('ConfigPY/GetConfigString');
const serverData = response.data.response || [];
//console.log(response);
// 清空原有数据
dictList.length = 0;
// 转换数据格式
serverData.forEach(item => {
dictList.push({
varName: item.varName,
varValue: tryParseJson(item.varValue) || []
});
});
//console.log(dictList);
// 默认选中第一个(如果存在)
if (dictList.length > 0) {
currentVarName.value = dictList[0].varName;
}
} catch (error) {
console.error('获取字典失败:', error);
ElMessage.error('字典数据加载失败');
}
};
// JSON安全解析方法
const tryParseJson = (str) => {
try {
return JSON.parse(str);
} catch {
return null;
}
};
const currentVarName = ref('');
const currentDict = computed(() =>
dictList.find(item => item.varName === currentVarName.value)
);
const currentValues = computed({
get: () => currentDict.value?.varValue || [],
set: (val) => {
if (currentDict.value) {
currentDict.value.varValue = val;
}
}
});
// 处理字典项变化
const handleDictChange = (val) => {
if (!val) return;
};
// 判断是否是区域字典
const isRegionDict = computed(() => currentVarName.value === '区域');
// 修改区域数据计算属性移除setter
const regionData = computed(() => {
if (!isRegionDict.value) return [];
const regionDict = dictList.find(d => d.varName === '区域');
return Object.entries(regionDict?.varValue || {}).map(([key, value]) => ({
key,
value: Array.isArray(value) ? value : [value]
}));
});
// 显示省份选择对话框
const showProvinceDialog = (row) => {
currentEditingRegion.value = row;
selectedProvinces.value = [...row.value];
provinceDialogVisible.value = true;
};
// 修改省份选择确认方法
const confirmProvinceSelection = async () => {
try {
if (!currentEditingRegion.value) return;
// 获取当前编辑的大区信息
const currentKey = currentEditingRegion.value.key;
const selectedProvincesList = selectedProvinces.value;
// 获取区域字典数据
const regionDict = dictList.find(d => d.varName === '区域');
if (!regionDict) return;
// 收集所有其他大区的省份映射
const provinceMap = new Map();
Object.entries(regionDict.varValue).forEach(([region, provinces]) => {
if (region !== currentKey) {
provinces.forEach(province => {
provinceMap.set(province, region);
});
}
});
// 检查重复省份
const duplicates = [];
selectedProvincesList.forEach(province => {
if (provinceMap.has(province)) {
duplicates.push({
province,
region: provinceMap.get(province)
});
}
});
// 如果有重复则提示
if (duplicates.length > 0) {
const confirmMessage = `
以下省份已在其他大区存在:<br>
${duplicates.map(d => `${d.province}${d.region}`).join('<br>')}<br>
是否确认继续保存?
`;
await ElMessageBox.confirm(confirmMessage, '重复省份警告', {
confirmButtonText: '强制保存',
cancelButtonText: '返回修改',
type: 'warning',
dangerouslyUseHTMLString: true
});
}
// 执行保存操作
regionDict.varValue = {
...regionDict.varValue,
[currentKey]: selectedProvincesList
};
provinceDialogVisible.value = false;
} catch (error) {
if (error !== 'cancel') {
console.error(error);
}
return; // 用户取消则中断流程
}
};
/* const confirmProvinceSelection = () => {
if (currentEditingRegion.value) {
const regionDict = dictList.find(d => d.varName === '区域');
if (regionDict) {
regionDict.varValue = {
...regionDict.varValue,
[currentEditingRegion.value.key]: selectedProvinces.value
};
}
}
provinceDialogVisible.value = false;
};*/
// 修改添加大区方法
const addNewRegion = () => {
const regionDict = dictList.find(d => d.varName === '区域');
if (!regionDict) return;
// 生成唯一键名
let newKey = "新增大区";
let counter = 1;
while (regionDict.varValue[newKey]) {
newKey = `新增大区${counter++}`;
}
// 直接修改原始数据
regionDict.varValue = {
...regionDict.varValue,
[newKey]: []
};
};
// 修改删除大区方法
const deleteRegion = (index) => {
ElMessageBox.confirm('确定要删除该大区吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const regionDict = dictList.find(d => d.varName === '区域');
if (!regionDict) return;
const keyToDelete = regionData.value[index].key;
const newValue = { ...regionDict.varValue };
delete newValue[keyToDelete];
regionDict.varValue = newValue;
});
};
// 添加行
const handleAdd = () => {
if (!currentDict.value) return;
currentDict.value.varValue.push('');
};
// 删除行
const handleDelete = (index) => {
ElMessageBox.confirm('确定要删除该行吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
currentDict.value.varValue.splice(index, 1);
});
};
// 保存字典
const saveDic = async (name, value) => {
try {
let filteredValue;
// 根据数据类型处理
if (Array.isArray(value)) {
// 处理普通数组格式
filteredValue = value
.map(item => String(item).trim())
.filter(item => item !== "");
if (filteredValue.length === 0) {
throw new Error("不能保存空字典项");
}
} else if (typeof value === 'object' && value !== null) {
// 处理区域字典的对象格式
filteredValue = Object.entries(value).reduce((acc, [key, values]) => {
const filtered = values
.map(item => String(item).trim())
.filter(item => item !== "");
// 保留key即使值为空数组如"其他"
if (key === '其他' || filtered.length > 0) {
acc[key] = filtered;
}
return acc;
}, {});
// 检查是否为空对象
if (Object.keys(filteredValue).length === 0) {
throw new Error("至少需要保留一个有效区域");
}
} else {
throw new Error("无效的字典格式");
}
const valueJs = JSON.stringify(filteredValue);
const rs = await $http.post('ConfigPY/SaveOrAddConfigString', {
VarName: name,
VarValue: valueJs
});
return filteredValue;
} catch (error) {
console.error(error);
throw error;
}
};
// 保存处理
const handleSave = async () => {
if (!currentDict.value) {
ElMessage.warning('请先选择要修改的字典项');
return;
}
try {
await ElMessageBox.confirm(
'此操作将永久修改字典数据,是否继续?',
'警告',
{
confirmButtonText: '确认保存',
cancelButtonText: '取消',
type: 'warning',
center: true,
dangerouslyUseHTMLString: true,
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;
saveDic(currentDict.value.varName, currentDict.value.varValue)
.then(() => {
done();
ElMessage.success('保存成功');
})
.catch(() => {
instance.confirmButtonLoading = false;
ElMessage.error('保存失败');
});
} else {
done();
}
}
}
);
// 处理区域字典的特殊结构
let saveData = currentDict.value.varValue;
if (isRegionDict.value) {
// 转换回原始对象格式
saveData = regionData.value.reduce((acc, cur) => {
acc[cur.key] = cur.value;
return acc;
}, {});
}
// 保存并获取处理后的数据
const filteredData = await saveDic(
currentDict.value.varName,
saveData
);
// 更新前端数据
currentDict.value.varValue = isRegionDict.value
? { ...filteredData } // 对象格式
: [...filteredData]; // 数组格式
} catch (error) {
if (error === 'cancel') {
//console.log('用户取消保存');
} else if (error.message === '不能保存空字典项') {
ElMessage.warning('字典项至少需要包含一个有效值');
} else {
ElMessage.error('保存失败: ' + error.message);
}
}
};
onMounted(() => {
getDic()
// 初始化选择第一个
if (dictList.length > 0) {
currentVarName.value = dictList[0].varName;
}
});
</script>
<style scoped>
.container {
display: block;
padding: 20px;
width: 100%;
}
.header {
width: 333px;
margin-bottom: 20px;
}
.footer {
width: 777px;
display: flex;
justify-content: space-between; /* 推荐方案 */
gap: 10px;
margin-top: 20px;
}
.el-message-box {
max-height: 70vh;
overflow-y: auto;
}
</style>