Files
RCZN-bs-program/rc_autoplc_front/src/views/devinfo/index.vue
2026-01-23 11:26:14 +08:00

1494 lines
40 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="devinfo-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>业务管理</el-breadcrumb-item>
<el-breadcrumb-item>设备管理</el-breadcrumb-item>
</el-breadcrumb>
<!-- 搜索栏 -->
<el-card class="search-card" shadow="never">
<div class="search-bar">
<el-form :inline="true" :model="queryForm" label-width="100px">
<el-form-item label="设备名称">
<el-input
v-model="queryForm.devName"
placeholder="输入设备名称"
clearable
style="width: 200px"
/>
</el-form-item>
<el-form-item label="设备状态">
<el-select
v-model="queryForm.status"
placeholder="请选择设备状态"
clearable
style="width: 200px"
>
<el-option label="空闲" :value="0" />
<el-option label="运行" :value="1" />
<el-option label="故障" :value="4" />
</el-select>
</el-form-item>
<el-form-item label="所属功能岛">
<el-select
v-model="queryForm.islandId"
placeholder="请选择功能岛"
clearable
style="width: 200px"
>
<el-option
v-for="island in islandList"
:key="island.id"
:label="island.islandName || island.name"
:value="island.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
<div class="toolbar">
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增设备
</el-button>
</div>
</div>
</el-card>
<!-- 设备列表 -->
<el-card shadow="never">
<el-table
v-loading="loading"
:data="deviceList"
border
stripe
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" :index="getIndex" />
<el-table-column prop="devName" label="设备名称" min-width="120" show-overflow-tooltip>
<template #default="{ row }">
{{ row.devName || '暂无' }}
</template>
</el-table-column>
<el-table-column prop="devModel" label="设备型号" min-width="120" show-overflow-tooltip>
<template #default="{ row }">
{{ row.devModel || '暂无' }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)" size="small">
{{ getStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="islandId" label="所属功能岛" width="150">
<template #default="{ row }">
{{ getIslandName(row.islandId) }}
</template>
</el-table-column>
<el-table-column prop="devDesc" label="描述" min-width="150" show-overflow-tooltip>
<template #default="{ row }">
{{ row.devDesc || '暂无' }}
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip>
<template #default="{ row }">
{{ row.remark || '暂无' }}
</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right">
<template #default="{ row }">
<el-button
type="success"
size="small"
link
@click="handleConfigParams(row)"
>
<el-icon><Setting /></el-icon>
配置参数
</el-button>
<el-button
type="primary"
size="small"
link
@click="handleEdit(row)"
>
<el-icon><Edit /></el-icon>
编辑
</el-button>
<el-button
type="danger"
size="small"
link
@click="handleDelete(row)"
>
<el-icon><Delete /></el-icon>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination">
<el-pagination
v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
background
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</el-card>
<!-- 新增/编辑抽屉 -->
<el-drawer
v-model="drawerVisible"
:title="drawerTitle"
direction="rtl"
size="500px"
:before-close="handleDrawerClose"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
label-position="left"
>
<el-form-item label="设备名称" prop="devName">
<el-input
v-model="formData.devName"
placeholder="请输入设备名称"
clearable
/>
</el-form-item>
<el-form-item label="设备型号" prop="devModel">
<el-input
v-model="formData.devModel"
placeholder="请输入设备型号"
clearable
/>
</el-form-item>
<el-form-item label="描述" prop="devDesc">
<el-input
v-model="formData.devDesc"
type="textarea"
:rows="3"
placeholder="请输入描述"
clearable
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
:rows="3"
placeholder="请输入备注"
clearable
/>
</el-form-item>
</el-form>
<template #footer>
<div class="drawer-footer">
<el-button @click="handleDrawerClose">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">
确定
</el-button>
</div>
</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"
:title="`设备参数配置 - ${currentDevice?.devName || ''}`"
width="1200px"
:before-close="handleParamDialogClose"
>
<div class="param-config-container">
<!-- 工具栏 -->
<div class="param-toolbar">
<el-button type="primary" @click="handleAddParamRow">
<el-icon><Plus /></el-icon>
添加一行
</el-button>
</div>
<!-- 参数列表 -->
<el-table
v-loading="paramLoading"
:data="paramList"
border
stripe
style="width: 100%"
:empty-text="paramEmptyText"
>
<el-table-column type="index" label="序号" width="60" :index="getParamIndex" />
<!-- 参数名称 -->
<el-table-column label="参数名称" min-width="120">
<template #default="{ row }">
<el-input
v-if="row.isEditing"
v-model="row.paramName"
placeholder="请输入参数名称"
size="small"
/>
<span v-else>{{ row.paramName || '暂无' }}</span>
</template>
</el-table-column>
<!-- 参数类型 -->
<el-table-column label="参数类型" min-width="120">
<template #default="{ row }">
<el-select
v-if="row.isEditing"
v-model="row.paramType"
placeholder="请选择参数类型"
size="small"
style="width: 100%"
>
<el-option
v-for="type in paramTypeOptions"
:key="type.value"
:label="type.label"
:value="type.value"
/>
</el-select>
<span v-else>{{ row.paramType || '暂无' }}</span>
</template>
</el-table-column>
<!-- 参数单位 -->
<el-table-column label="参数单位" min-width="100">
<template #default="{ row }">
<el-input
v-if="row.isEditing"
v-model="row.paramUnit"
placeholder="请输入参数单位"
size="small"
/>
<span v-else>{{ row.paramUnit || '暂无' }}</span>
</template>
</el-table-column>
<!-- 表单控件 -->
<el-table-column label="表单控件" min-width="120" align="left">
<template #default="{ row }">
<el-select
v-if="row.isEditing"
v-model="row.formType"
placeholder="请选择表单控件"
size="small"
style="width: 100%"
>
<el-option
v-for="form in formTypeOptions"
:key="form.value"
:label="form.label"
:value="form.value"
/>
</el-select>
<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>
<!-- 操作列 -->
<el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }">
<el-button
v-if="row.isEditing"
type="primary"
size="small"
link
@click="handleSaveParam(row)"
>
<el-icon><Check /></el-icon>
保存
</el-button>
<el-button
v-else
type="primary"
size="small"
link
@click="handleEditParam(row)"
>
<el-icon><Edit /></el-icon>
编辑
</el-button>
<el-button
type="danger"
size="small"
link
@click="handleDeleteParam(row)"
>
<el-icon><Delete /></el-icon>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="param-pagination">
<el-pagination
v-model:current-page="paramPagination.pageNum"
v-model:page-size="paramPagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="paramTotal"
background
@current-change="handleParamCurrentChange"
@size-change="handleParamSizeChange"
/>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Edit, Delete, Setting, Check, Close } from '@element-plus/icons-vue'
import {
devInfoadd,
devInfodel,
devInfoupd,
devInfolist,
devInfobyid,
} from '@/api/tb/devinfo'
import { islandInfobyid, islandInfolist } from '@/api/tb/islandinfo'
import {
devparamadd,
devparamdel,
devparamupd,
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)
const submitting = ref(false)
// 设备列表
const deviceList = ref<any[]>([])
const total = ref(0)
// 功能岛列表和映射
const islandList = ref<any[]>([])
const islandMap = ref<Record<number, string>>({})
// 查询表单
const queryForm = reactive({
devName: '',
status: undefined as number | undefined,
islandId: undefined as number | undefined,
})
// 分页
const pagination = reactive({
pageNum: 1,
pageSize: 10,
})
// 抽屉相关
const drawerVisible = ref(false)
const drawerTitle = ref('新增设备')
const isEdit = ref(false)
const formRef = ref()
// 表单数据
const formData = reactive({
id: undefined,
devName: '',
devModel: '',
devDesc: '',
remark: '',
status: 0, // 状态自动设置为0
})
// 表单验证规则
const formRules = {
devName: [
{ required: true, message: '请输入设备名称', trigger: 'blur' },
],
}
// 获取序号
const getIndex = (index: number) => {
return (pagination.pageNum - 1) * pagination.pageSize + index + 1
}
// 获取状态类型
const getStatusType = (status: number | null | undefined) => {
if (status === 0) return 'success' // 空闲 - 绿色
if (status === 1) return 'warning' // 运行 - 黄色
if (status === 4) return 'danger' // 故障 - 红色
return 'info'
}
// 获取状态文本
const getStatusText = (status: number | null | undefined) => {
if (status === 0) return '空闲'
if (status === 1) return '运行'
if (status === 4) return '故障'
return '未知'
}
// 获取功能岛列表
const getIslandList = async () => {
try {
const res: any = await islandInfolist({
pageNum: 1,
pageSize: 1000, // 获取所有功能岛
})
const data = res?.data ?? res ?? {}
const recordsFromData =
data.records ||
data.list ||
data.rows ||
data.items ||
(Array.isArray(data.data) ? data.data : undefined)
const records = Array.isArray(recordsFromData)
? recordsFromData
: Array.isArray(data)
? data
: []
islandList.value = records
// 建立ID到名称的映射
const map: Record<number, string> = {}
records.forEach((island: any) => {
if (island.id !== null && island.id !== undefined) {
map[island.id] = island.islandName || island.name || '未命名功能岛'
}
})
islandMap.value = map
} catch (error) {
console.error('获取功能岛列表失败:', error)
}
}
// 获取功能岛名称
const getIslandName = (islandId: number | null | undefined) => {
if (islandId === null || islandId === undefined || islandId === 0) {
return '暂未绑定功能岛'
}
return islandMap.value[islandId] || `功能岛${islandId}`
}
// 获取设备列表
const getDeviceList = async () => {
try {
loading.value = true
const params: any = {
pageNum: pagination.pageNum,
pageSize: pagination.pageSize,
}
// 设备名称模糊查询
if (queryForm.devName) {
params.devName = queryForm.devName
}
// 设备状态精确查询
if (queryForm.status !== undefined && queryForm.status !== null) {
params.status = queryForm.status
}
// 所属功能岛精确查询
if (queryForm.islandId !== undefined && queryForm.islandId !== null) {
params.islandId = queryForm.islandId
}
// 排除 devModel 为 PLC 的设备
params.devModelNot = 'PLC'
const res: any = await devInfolist(params)
const data = res?.data ?? res ?? {}
// 兼容多种返回格式
const recordsFromData =
data.records ||
data.list ||
data.rows ||
data.items ||
(Array.isArray(data.data) ? data.data : undefined)
const records = Array.isArray(recordsFromData)
? recordsFromData
: Array.isArray(data)
? data
: []
// 过滤掉 devModel 为 PLC 的设备(前端过滤作为备用)
const filteredRecords = records.filter((item: any) => {
const devModel = item.devModel || ''
return devModel.toUpperCase() !== 'PLC'
})
const totalValue =
data.total ?? data.count ?? data.totalCount ?? filteredRecords.length ?? 0
deviceList.value = filteredRecords
total.value = Number(totalValue) || 0
} catch (error) {
console.error('获取设备列表失败:', error)
ElMessage.error('获取设备列表失败')
} finally {
loading.value = false
}
}
// 查询
const handleSearch = () => {
pagination.pageNum = 1
getDeviceList()
}
// 重置查询
const resetSearch = () => {
queryForm.devName = ''
queryForm.status = undefined
queryForm.islandId = undefined
handleSearch()
}
// 分页大小改变
const handleSizeChange = (size: number) => {
pagination.pageSize = size
getDeviceList()
}
// 当前页改变
const handleCurrentChange = (page: number) => {
pagination.pageNum = page
getDeviceList()
}
// 打开新增抽屉
const handleAdd = () => {
isEdit.value = false
drawerTitle.value = '新增设备'
resetForm()
drawerVisible.value = true
}
// 重置表单
const resetForm = () => {
formData.id = undefined
formData.devName = ''
formData.devModel = ''
formData.devDesc = ''
formData.remark = ''
formData.status = 0
formRef.value?.clearValidate()
}
// 编辑设备
const handleEdit = async (item: any) => {
try {
loading.value = true
const res: any = await devInfobyid(item.id)
const data = res?.data ?? res ?? {}
formData.id = data.id ?? item.id
formData.devName = data.devName ?? ''
formData.devModel = data.devModel ?? ''
formData.devDesc = data.devDesc ?? ''
formData.remark = data.remark ?? ''
formData.status = data.status ?? 0
isEdit.value = true
drawerTitle.value = '编辑设备'
drawerVisible.value = true
} catch (error) {
console.error('获取设备信息失败:', error)
ElMessage.error('获取设备信息失败')
} finally {
loading.value = false
}
}
// 删除设备
const handleDelete = async (item: any) => {
try {
// 检查设备是否绑定了功能岛
if (item.islandId && item.islandId !== 0 && item.islandId !== '0') {
// 查询功能岛名称
let islandName = '功能岛'
try {
const islandRes: any = await islandInfobyid(item.islandId)
if (islandRes?.code === '0' || islandRes?.code === 0) {
const islandData = islandRes.data || islandRes || {}
islandName = islandData.islandName || islandData.name || '功能岛'
}
} catch (error) {
console.error('获取功能岛信息失败:', error)
}
await ElMessageBox.alert(
`该设备已绑定${islandName},请先完成解绑`,
'提示',
{
confirmButtonText: '确定',
type: 'warning',
}
)
return
}
await ElMessageBox.confirm(
`确定要删除设备"${item.devName || '未命名'}"吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
loading.value = true
const res: any = await devInfodel(item.id)
if (res?.code === '0' || res?.code === 0 || res?.success) {
ElMessage.success('删除成功')
getDeviceList()
} else {
ElMessage.error(res?.message || res?.msg || '删除失败')
}
} catch (error: any) {
if (error !== 'cancel') {
console.error('删除设备失败:', error)
ElMessage.error('删除设备失败')
}
} finally {
loading.value = false
}
}
// 提交表单
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
submitting.value = true
// 构建提交数据,只包含非空字段(根据后端要求:非空则入库/更新)
const submitData: any = {
devName: formData.devName,
}
// 添加其他字段(如果非空)
if (formData.devModel) {
submitData.devModel = formData.devModel
}
if (formData.devDesc) {
submitData.devDesc = formData.devDesc
}
if (formData.remark) {
submitData.remark = formData.remark
}
let res: any
if (isEdit.value && formData.id) {
// 编辑 - 需要传入ID其他字段可选(非空则更新)
res = await devInfoupd({
id: formData.id,
...submitData,
})
} else {
// 新增 - 状态自动设置为0
res = await devInfoadd({
...submitData,
status: 0,
})
}
if (res?.code === '0' || res?.code === 0 || res?.success) {
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
drawerVisible.value = false
getDeviceList()
} else {
ElMessage.error(res?.message || res?.msg || (isEdit.value ? '编辑失败' : '新增失败'))
}
} catch (error: any) {
if (error !== false) {
console.error('提交失败:', error)
ElMessage.error('提交失败,请检查表单')
}
} finally {
submitting.value = false
}
}
// 关闭抽屉
const handleDrawerClose = () => {
drawerVisible.value = false
resetForm()
}
// 参数配置相关
const paramDialogVisible = ref(false)
const paramLoading = ref(false)
const paramList = ref<any[]>([])
const paramTotal = ref(0)
const currentDevice = ref<any>(null)
const paramEmptyText = ref('该设备暂无参数,请完成配置')
const paramPagination = reactive({
pageNum: 1,
pageSize: 10,
})
// 参数类型选项(固定数据类型)
const paramTypeOptions = [
{ label: '字符串', value: 'string' },
{ label: '整数', value: 'integer' },
{ label: '浮点数', value: 'float' },
{ label: '布尔值', value: 'boolean' },
]
// 表单控件选项(常用表单控件)
const formTypeOptions = [
{ label: '文本输入框', value: 'input' },
{ label: '数字输入框', value: 'number' },
{ label: '下拉框', value: 'select' },
{ label: '文本域', value: 'textarea' },
]
// 获取参数序号
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
paramPagination.pageNum = 1
paramPagination.pageSize = 10
paramDialogVisible.value = true
await getParamList()
}
// 获取参数列表
const getParamList = async () => {
if (!currentDevice.value?.id) return
try {
paramLoading.value = true
const params: any = {
pageNum: paramPagination.pageNum,
pageSize: paramPagination.pageSize,
devId: currentDevice.value.id, // 只查询该设备的参数
}
const res: any = await devparamlist(params)
const data = res?.data ?? res ?? {}
// 兼容多种返回格式
const recordsFromData =
data.records ||
data.list ||
data.rows ||
data.items ||
(Array.isArray(data.data) ? data.data : undefined)
const records = Array.isArray(recordsFromData)
? recordsFromData
: Array.isArray(data)
? data
: []
// 按主键id升序排列没有id的排在最后
records.sort((a: any, b: any) => {
const idA = a.id ?? 0
const idB = b.id ?? 0
// 如果都没有id保持原顺序
if (idA === 0 && idB === 0) return 0
// 如果a没有ida排在后面
if (idA === 0) return 1
// 如果b没有idb排在后面
if (idB === 0) return -1
// 都有id按id升序
return idA - idB
})
const totalValue =
data.total ?? data.count ?? data.totalCount ?? records.length ?? 0
// 为每条记录添加编辑状态标识
paramList.value = records.map((item: any) => ({
...item,
isEditing: false,
}))
paramTotal.value = Number(totalValue) || 0
if (paramList.value.length === 0) {
paramEmptyText.value = '该设备暂无参数,请完成配置'
}
} catch (error) {
console.error('获取参数列表失败:', error)
ElMessage.error('获取参数列表失败')
paramList.value = []
paramTotal.value = 0
} finally {
paramLoading.value = false
}
}
// 参数分页大小改变
const handleParamSizeChange = (size: number) => {
paramPagination.pageSize = size
getParamList()
}
// 参数当前页改变
const handleParamCurrentChange = (page: number) => {
paramPagination.pageNum = page
getParamList()
}
// 添加一行参数
const handleAddParamRow = () => {
const newRow = {
id: undefined,
devId: currentDevice.value?.id,
paramName: '',
paramType: '',
paramUnit: '',
formType: '',
paramValue: '',
isEditing: true,
isNew: true, // 标记为新添加的行
}
paramList.value.push(newRow)
paramTotal.value += 1
}
// 编辑参数
const handleEditParam = (row: any) => {
row.isEditing = true
}
// 保存参数
const handleSaveParam = async (row: any) => {
// 验证必填项
if (!row.paramName || row.paramName.trim() === '') {
ElMessage.warning('参数名称不能为空')
return
}
if (!currentDevice.value?.id) {
ElMessage.error('设备ID不存在')
return
}
try {
paramLoading.value = true
const submitData: any = {
devId: currentDevice.value.id,
paramName: row.paramName.trim(),
}
// 添加其他字段(如果非空)
if (row.paramType) {
submitData.paramType = row.paramType
}
if (row.paramUnit) {
submitData.paramUnit = row.paramUnit
}
if (row.formType) {
submitData.formType = row.formType
}
if (row.paramValue !== undefined && row.paramValue !== null) {
submitData.paramValue = row.paramValue
}
let res: any
if (row.isNew && !row.id) {
// 新增
res = await devparamadd(submitData)
} else {
// 更新 - 需要传入ID其他字段可选(非空则更新)
res = await devparamupd({
id: row.id,
...submitData,
})
}
if (res?.code === '0' || res?.code === 0 || res?.success) {
const wasNew = row.isNew
ElMessage.success(wasNew ? '新增成功' : '更新成功')
row.isEditing = false
row.isNew = false
// 如果是新增刷新列表以获取ID
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 ? '新增失败' : '更新失败'))
}
} catch (error) {
console.error('保存参数失败:', error)
ElMessage.error('保存参数失败')
} finally {
paramLoading.value = false
}
}
// 创建字典类型并显示配置对话框
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 {
// 如果是新添加的行(还未保存),直接从列表中移除
if (row.isNew && !row.id) {
const index = paramList.value.findIndex((item) => item === row)
if (index > -1) {
paramList.value.splice(index, 1)
paramTotal.value -= 1
}
return
}
if (!row.id) {
ElMessage.warning('无法删除参数ID不存在')
return
}
await ElMessageBox.confirm(
`确定要删除参数"${row.paramName || '未命名'}"吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
paramLoading.value = true
const res: any = await devparamdel(row.id)
if (res?.code === '0' || res?.code === 0 || res?.success) {
ElMessage.success('删除成功')
await getParamList()
} else {
ElMessage.error(res?.message || res?.msg || '删除失败')
}
} catch (error: any) {
if (error !== 'cancel') {
console.error('删除参数失败:', error)
ElMessage.error('删除参数失败')
}
} finally {
paramLoading.value = false
}
}
// 关闭参数配置对话框
const handleParamDialogClose = () => {
paramDialogVisible.value = false
paramList.value = []
paramTotal.value = 0
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(() => {
getDeviceList()
})
})
</script>
<style lang="scss" scoped>
.devinfo-page {
display: flex;
flex-direction: column;
gap: 12px;
}
.search-card {
padding-bottom: 4px;
}
.search-bar {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.toolbar {
display: flex;
align-items: center;
gap: 8px;
}
.pagination {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.drawer-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.param-config-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.param-toolbar {
display: flex;
justify-content: flex-start;
gap: 8px;
}
.param-pagination {
margin-top: 16px;
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>