字典类型管理+字典数据管理

This commit is contained in:
Lxq
2026-01-05 13:07:30 +08:00
parent bdd50a498b
commit 5f6808b2a4
4 changed files with 617 additions and 8 deletions

View File

@@ -0,0 +1,53 @@
import request from '@/utils/request'
export function dicdataadd(data: any) {
return request({
url: '/sysDicData/add',
method: 'post',
data,
})
}
export function dicdatadel(id: string | number) {
return request({
url: `/sysDicData/del/${id}`,
method: 'delete',
})
}
export function dicdataupd(data: any) {
return request({
url: '/sysDicData/update',
method: 'put',
data,
})
}
export function dicdatalist(data: any) {
return request({
url: '/sysDicData/listPage',
method: 'get',
params: data,
})
}
export function dicdatabyid(id: string | number) {
return request({
url: `/sysDicData/getById/${id}`,
method: 'get',
})
}
export function dicdataall() {
return request({
url: `/sysDicData/list`,
method: 'get',
})
}
export function dicdatabydicid(dicId : string | number) {
return request({
url: `/sysDicData/listByDicId/${dicId}`,
method: 'get',
})
}

View File

@@ -0,0 +1,46 @@
import request from '@/utils/request'
export function dictypeadd(data: any) {
return request({
url: '/sysDicType/add',
method: 'post',
data,
})
}
export function dictypedel(id: string | number) {
return request({
url: `/sysDicType/del/${id}`,
method: 'delete',
})
}
export function dictypeupd(data: any) {
return request({
url: '/sysDicType/update',
method: 'put',
data,
})
}
export function dictypelist(data: any) {
return request({
url: '/sysDicType/listPage',
method: 'get',
params: data,
})
}
export function dictypebyid(id: string | number) {
return request({
url: `/sysDicType/getById/${id}`,
method: 'get',
})
}
export function dictypeall() {
return request({
url: `/sysDicType/list`,
method: 'get',
})
}

View File

@@ -201,6 +201,55 @@
</template>
</el-drawer>
<!-- 下拉框选项配置对话框 -->
<el-dialog
v-model="selectOptionsDialogVisible"
:title="`[${currentSelectParam?.paramName || ''}]下拉框选项列表配置`"
width="600px"
:before-close="handleSelectOptionsDialogClose"
>
<div class="select-options-container">
<!-- 输入区域 -->
<div class="select-options-input-area">
<span class="input-label">选项数据</span>
<el-input
v-model="selectOptionInput"
placeholder="请输入选项数据"
clearable
style="flex: 1; margin-right: 8px"
@keyup.enter="handleAddSelectOption"
/>
<el-button type="primary" @click="handleAddSelectOption">添加</el-button>
</div>
<!-- 选项卡片列表 -->
<div class="select-options-cards">
<div
v-for="option in selectOptionsList"
:key="option.id"
class="select-option-card"
>
<span class="option-text">{{ option.dicValue }}</span>
<el-icon
class="option-delete-icon"
@click="handleDeleteSelectOption(option)"
>
<Close />
</el-icon>
</div>
<div v-if="selectOptionsList.length === 0" class="empty-tip">
暂无选项数据请添加
</div>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleSelectOptionsDialogClose">取消</el-button>
<el-button type="primary" @click="handleSaveSelectOptions">保存</el-button>
</div>
</template>
</el-dialog>
<!-- 参数配置对话框 -->
<el-dialog
v-model="paramDialogVisible"
@@ -276,7 +325,7 @@
</el-table-column>
<!-- 表单控件 -->
<el-table-column label="表单控件" min-width="120">
<el-table-column label="表单控件" min-width="120" align="left">
<template #default="{ row }">
<el-select
v-if="row.isEditing"
@@ -292,7 +341,17 @@
:value="form.value"
/>
</el-select>
<span v-else>{{ row.formType || '暂无' }}</span>
<el-button
v-else-if="row.formType === 'select'"
type="success"
size="small"
link
class="form-type-select-btn"
@click="handleConfigSelectOptions(row)"
>
下拉框
</el-button>
<span v-else>{{ getFormTypeLabel(row.formType) || '暂无' }}</span>
</template>
</el-table-column>
@@ -353,7 +412,7 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Edit, Delete, Setting, Check } from '@element-plus/icons-vue'
import { Plus, Edit, Delete, Setting, Check, Close } from '@element-plus/icons-vue'
import {
devInfoadd,
devInfodel,
@@ -369,6 +428,8 @@ import {
devparamlist,
devparambyid,
} from '@/api/tb/devparam'
import { dictypeadd, dictypelist } from '@/api/system/dictype'
import { dicdataadd, dicdatadel, dicdatabydicid } from '@/api/system/dicdata'
// 加载状态
const loading = ref(false)
@@ -769,6 +830,12 @@ const getParamIndex = (index: number) => {
return (paramPagination.pageNum - 1) * paramPagination.pageSize + index + 1
}
// 获取表单控件标签
const getFormTypeLabel = (formType: string) => {
const option = formTypeOptions.find((item) => item.value === formType)
return option ? option.label : formType
}
// 打开参数配置对话框
const handleConfigParams = async (row: any) => {
currentDevice.value = row
@@ -935,6 +1002,37 @@ const handleSaveParam = async (row: any) => {
if (wasNew) {
await getParamList()
}
// 如果表单控件是下拉框,检查是否已配置过选项,只有未配置时才弹出配置对话框
if (row.formType === 'select') {
// 查找对应的字典类型
const dicTypeListRes: any = await dictypelist({
pageNum: 1,
pageSize: 1000,
})
const dicTypeData = dicTypeListRes?.data ?? dicTypeListRes ?? {}
const dicTypeRecords = dicTypeData.records || dicTypeData.list || dicTypeData.rows || []
const dicType = dicTypeRecords.find(
(item: any) => item.dicName === row.paramName
)
if (dicType) {
// 检查是否已有选项数据
const optionsRes: any = await dicdatabydicid(dicType.id)
const optionsData = optionsRes?.data ?? optionsRes ?? {}
const optionsRecords = Array.isArray(optionsData) ? optionsData : (optionsData.records || optionsData.list || optionsData.rows || [])
// 只有没有选项数据时才弹出配置对话框
if (optionsRecords.length === 0) {
await handleCreateDicTypeAndShowConfig(row)
}
} else {
// 如果字典类型不存在,创建并弹出配置对话框
await handleCreateDicTypeAndShowConfig(row)
}
}
} else {
ElMessage.error(res?.message || res?.msg || (row.isNew ? '新增失败' : '更新失败'))
}
@@ -946,6 +1044,87 @@ const handleSaveParam = async (row: any) => {
}
}
// 创建字典类型并显示配置对话框
const handleCreateDicTypeAndShowConfig = async (row: any) => {
try {
// 检查是否已存在该字典类型
const dicTypeListRes: any = await dictypelist({
pageNum: 1,
pageSize: 1000,
})
const dicTypeData = dicTypeListRes?.data ?? dicTypeListRes ?? {}
const dicTypeRecords = dicTypeData.records || dicTypeData.list || dicTypeData.rows || []
// 查找是否已存在相同名称的字典类型
const existingDicType = dicTypeRecords.find(
(item: any) => item.dicName === row.paramName
)
let dicTypeId: number | undefined
if (existingDicType) {
// 如果已存在使用现有的ID
dicTypeId = existingDicType.id
} else {
// 如果不存在,创建新的字典类型
const dicTypeRes: any = await dictypeadd({
dicName: row.paramName,
})
if (dicTypeRes?.code === '0' || dicTypeRes?.code === 0 || dicTypeRes?.success) {
// 获取新创建的字典类型ID
const newDicTypeData = dicTypeRes?.data ?? dicTypeRes ?? {}
dicTypeId = newDicTypeData.id
// 如果返回的数据中没有ID重新查询
if (!dicTypeId) {
const refreshListRes: any = await dictypelist({
pageNum: 1,
pageSize: 1000,
})
const refreshData = refreshListRes?.data ?? refreshListRes ?? {}
const refreshRecords = refreshData.records || refreshData.list || refreshData.rows || []
const newDicType = refreshRecords.find(
(item: any) => item.dicName === row.paramName
)
dicTypeId = newDicType?.id
}
} else {
ElMessage.error('创建字典类型失败')
return
}
}
// 打开配置对话框
if (dicTypeId) {
// 先加载选项列表,检查是否已有选项数据
await loadSelectOptions(dicTypeId)
// 如果还没有选项数据,才弹出提示
if (selectOptionsList.value.length === 0) {
await ElMessageBox.alert('请编辑下拉框选项列表内容', '提示', {
confirmButtonText: '确定',
type: 'info',
})
}
currentSelectParam.value = {
...row,
dicTypeId: dicTypeId,
}
selectOptionsDialogVisible.value = true
} else {
ElMessage.error('无法获取字典类型ID')
}
} catch (error: any) {
if (error !== 'cancel') {
console.error('创建字典类型失败:', error)
ElMessage.error('创建字典类型失败')
}
}
}
// 删除参数
const handleDeleteParam = async (row: any) => {
try {
@@ -1001,6 +1180,156 @@ const handleParamDialogClose = () => {
currentDevice.value = null
}
// 下拉框选项配置相关
const selectOptionsDialogVisible = ref(false)
const currentSelectParam = ref<any>(null)
const selectOptionInput = ref('')
const selectOptionsList = ref<any[]>([])
// 打开下拉框选项配置对话框
const handleConfigSelectOptions = async (row: any) => {
try {
// 查找对应的字典类型
const dicTypeListRes: any = await dictypelist({
pageNum: 1,
pageSize: 1000,
})
const dicTypeData = dicTypeListRes?.data ?? dicTypeListRes ?? {}
const dicTypeRecords = dicTypeData.records || dicTypeData.list || dicTypeData.rows || []
const dicType = dicTypeRecords.find(
(item: any) => item.dicName === row.paramName
)
if (!dicType) {
ElMessage.warning('未找到对应的字典类型,请先保存参数')
return
}
currentSelectParam.value = {
...row,
dicTypeId: dicType.id,
}
selectOptionsDialogVisible.value = true
await loadSelectOptions(dicType.id)
} catch (error) {
console.error('打开下拉框配置失败:', error)
ElMessage.error('打开下拉框配置失败')
}
}
// 加载下拉框选项列表
const loadSelectOptions = async (dicId: number) => {
try {
const res: any = await dicdatabydicid(dicId)
const data = res?.data ?? res ?? {}
const records = Array.isArray(data) ? data : (data.records || data.list || data.rows || [])
selectOptionsList.value = records
} catch (error) {
console.error('加载下拉框选项失败:', error)
ElMessage.error('加载下拉框选项失败')
selectOptionsList.value = []
}
}
// 添加下拉框选项
const handleAddSelectOption = async () => {
if (!selectOptionInput.value || selectOptionInput.value.trim() === '') {
ElMessage.warning('请输入选项数据')
return
}
if (!currentSelectParam.value?.dicTypeId) {
ElMessage.error('字典类型ID不存在')
return
}
try {
const res: any = await dicdataadd({
dicId: currentSelectParam.value.dicTypeId,
dicLabel: currentSelectParam.value.paramType || '',
dicValue: selectOptionInput.value.trim(),
})
if (res?.code === '0' || res?.code === 0 || res?.success) {
ElMessage.success('添加成功')
selectOptionInput.value = ''
// 重新加载选项列表
await loadSelectOptions(currentSelectParam.value.dicTypeId)
} else {
ElMessage.error(res?.message || res?.msg || '添加失败')
}
} catch (error) {
console.error('添加下拉框选项失败:', error)
ElMessage.error('添加下拉框选项失败')
}
}
// 删除下拉框选项
const handleDeleteSelectOption = async (option: any) => {
try {
await ElMessageBox.confirm(
`确定要删除"${option.dicValue || '该选项'}"吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
const res: any = await dicdatadel(option.id)
if (res?.code === '0' || res?.code === 0 || res?.success) {
ElMessage.success('删除成功')
// 重新加载选项列表
if (currentSelectParam.value?.dicTypeId) {
await loadSelectOptions(currentSelectParam.value.dicTypeId)
}
} else {
ElMessage.error(res?.message || res?.msg || '删除失败')
}
} catch (error: any) {
if (error !== 'cancel') {
console.error('删除下拉框选项失败:', error)
ElMessage.error('删除下拉框选项失败')
}
}
}
// 保存下拉框选项配置
const handleSaveSelectOptions = async () => {
if (!currentSelectParam.value) {
return
}
if (selectOptionsList.value.length === 0) {
await ElMessageBox.alert(
`${currentSelectParam.value.paramName}的下拉框选项列表还未配置,请先完成配置`,
'提示',
{
confirmButtonText: '确定',
type: 'warning',
}
)
return
}
selectOptionsDialogVisible.value = false
ElMessage.success(`${currentSelectParam.value.paramName}下拉框选项列表保存成功!`)
}
// 关闭下拉框选项配置对话框
const handleSelectOptionsDialogClose = () => {
selectOptionsDialogVisible.value = false
selectOptionInput.value = ''
selectOptionsList.value = []
currentSelectParam.value = null
}
// 初始化
onMounted(() => {
getIslandList().then(() => {
@@ -1063,5 +1392,97 @@ onMounted(() => {
display: flex;
justify-content: flex-end;
}
.select-options-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.select-options-input-area {
display: flex;
align-items: center;
gap: 8px;
}
.input-label {
min-width: 80px;
font-weight: 500;
font-size: 16px;
}
.select-options-cards {
display: flex;
flex-wrap: wrap;
gap: 20px;
min-height: 100px;
max-height: 300px;
overflow-y: auto;
padding: 20px;
background: linear-gradient(135deg, #e3f2fd 0%, #f0f2f5 100%);
border-radius: 4px;
background-image:
linear-gradient(rgba(64, 158, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(64, 158, 255, 0.03) 1px, transparent 1px);
background-size: 20px 20px;
}
.select-option-card {
position: relative;
padding: 16px 20px;
background-color: #fff;
border: 1px solid rgba(64, 158, 255, 0.1);
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
cursor: default;
transition: all 0.3s;
min-width: 120px;
font-size: 16px;
}
.select-option-card:hover {
box-shadow: 0 4px 16px rgba(64, 158, 255, 0.2);
transform: translateY(-4px);
border-color: rgba(64, 158, 255, 0.3);
}
.option-text {
font-size: 1.2em;
color: #303133;
font-weight: bold;
}
.option-delete-icon {
position: absolute;
top: 10px;
right: 10px;
font-size: 16px;
color: #909399;
cursor: pointer;
transition: color 0.3s;
padding: 4px;
}
.option-delete-icon:hover {
color: #f56c6c;
}
.empty-tip {
width: 100%;
text-align: center;
color: #909399;
font-size: 16px;
padding: 20px 0;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.form-type-select-btn {
font-size: inherit;
}
</style>

View File

@@ -146,7 +146,7 @@
class="param-select"
>
<el-option
v-for="(option, optIndex) in getParamOptions(param)"
v-for="(option, optIndex) in (paramOptionsMap[param.id] || [])"
:key="optIndex"
:label="option.label"
:value="option.value"
@@ -196,6 +196,8 @@ import {
import { islandInfolist } from '@/api/tb/islandinfo'
import { devselect } from '@/api/tb/devinfo'
import { devparamselect } from '@/api/tb/devparam'
import { dictypelist } from '@/api/system/dictype'
import { dicdatabydicid } from '@/api/system/dicdata'
// 功能岛列表
const islandList = ref<any[]>([])
@@ -237,6 +239,13 @@ const paramFormData = reactive<Record<string, any>>({})
const paramLoading = ref(false)
const savingParams = ref(false)
// 字典类型和字典数据缓存
const dicTypeCache = ref<Map<string, any>>(new Map())
const dicDataCache = ref<Map<number, any[]>>(new Map())
// 参数选项映射paramId -> options[]
const paramOptionsMap = reactive<Record<string | number, Array<{ label: string; value: string }>>>({})
// 中文数字
const chineseNumbers = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
@@ -659,6 +668,9 @@ const handleEdit = async (item: any) => {
}
})
// 为所有 select 类型的参数异步加载选项
await loadParamOptions(allParams)
drawerVisible.value = true
} catch (error) {
console.error('加载参数失败:', error)
@@ -668,10 +680,83 @@ const handleEdit = async (item: any) => {
}
}
// 获取参数选项用于select类型
const getParamOptions = (param: any): Array<{ label: string; value: string }> => {
// 这里可以根据param的配置返回选项暂时返回空数组
return []
// 加载参数选项用于select类型
const loadParamOptions = async (params: any[]) => {
// 清空之前的选项映射
Object.keys(paramOptionsMap).forEach((key) => {
delete paramOptionsMap[key]
})
// 为所有 select 类型的参数并行加载选项
const loadPromises = params
.filter((param) => param.formType === 'select' && param.paramName && param.id)
.map(async (param) => {
try {
// 先从缓存中查找字典类型
let dicType = dicTypeCache.value.get(param.paramName)
// 如果缓存中没有,查询字典类型列表
if (!dicType) {
const dicTypeListRes: any = await dictypelist({
pageNum: 1,
pageSize: 1000,
})
const dicTypeData = dicTypeListRes?.data ?? dicTypeListRes ?? {}
const dicTypeRecords = dicTypeData.records || dicTypeData.list || dicTypeData.rows || []
// 根据 dicName 匹配 paramName 找到对应的字典类型
dicType = dicTypeRecords.find(
(item: any) => item.dicName === param.paramName
)
// 如果找到了,存入缓存
if (dicType) {
dicTypeCache.value.set(param.paramName, dicType)
}
}
// 如果没有找到对应的字典类型,设置空数组
if (!dicType || !dicType.id) {
paramOptionsMap[param.id] = []
return
}
// 先从缓存中查找字典数据
let dicDataList = dicDataCache.value.get(dicType.id)
// 如果缓存中没有,根据 dicId 查询字典数据
if (!dicDataList) {
const dicDataRes: any = await dicdatabydicid(dicType.id)
const dicDataData = dicDataRes?.data ?? dicDataRes ?? {}
dicDataList = Array.isArray(dicDataData)
? dicDataData
: (dicDataData.records || dicDataData.list || dicDataData.rows || [])
// 存入缓存
if (dicDataList && dicDataList.length > 0) {
dicDataCache.value.set(dicType.id, dicDataList)
}
}
// 将字典数据转换为下拉框选项格式,使用 dicValue 作为 label 和 value
if (dicDataList && Array.isArray(dicDataList) && dicDataList.length > 0) {
paramOptionsMap[param.id] = dicDataList.map((item: any) => ({
label: item.dicValue || '',
value: item.dicValue || '',
}))
} else {
paramOptionsMap[param.id] = []
}
} catch (error) {
console.error(`获取参数${param.paramName}的选项失败:`, error)
paramOptionsMap[param.id] = []
}
})
// 等待所有选项加载完成
await Promise.all(loadPromises)
}
// 保存参数
@@ -752,6 +837,10 @@ const handleDrawerClose = () => {
Object.keys(paramFormData).forEach((key) => {
delete paramFormData[key]
})
// 清空选项映射
Object.keys(paramOptionsMap).forEach((key) => {
delete paramOptionsMap[key]
})
}
// 初始化