499 lines
14 KiB
Vue
499 lines
14 KiB
Vue
|
|
<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>
|