kkk-ops 3 weeks ago
commit 7ee9a074b3

@ -74,6 +74,7 @@ export interface DeviceContactModelVO {
dataUnit?: string dataUnit?: string
} }
// 物联设备 API // 物联设备 API
export const DeviceApi = { export const DeviceApi = {
// 查询物联设备分页 // 查询物联设备分页
@ -130,6 +131,10 @@ export const DeviceApi = {
return await request.get({ url: `/iot/device/historyRecord`, params }) return await request.get({ url: `/iot/device/historyRecord`, params })
}, },
devicePointList: async () => {
return await request.get({ url: `/iot/device/devicePointList` })
},
// ==================== 子表(设备属性) ==================== // ==================== 子表(设备属性) ====================
// 获得设备属性分页 // 获得设备属性分页

@ -48,7 +48,7 @@ export const DeviceModelAttributeApi = {
operationAnalysisDetails: async (params: { operationAnalysisDetails: async (params: {
deviceId: number deviceId: number
modelId: number modelId?: number
collectionStartTime?: string collectionStartTime?: string
collectionEndTime?: string collectionEndTime?: string
}) => { }) => {

@ -19,6 +19,9 @@ export interface OrganizationVO {
export type DeviceParameterAnalysisNodeVO = { export type DeviceParameterAnalysisNodeVO = {
id: number | string id: number | string
name: string name: string
orgClass?: string
parentId?: number | string
children?: DeviceParameterAnalysisNodeVO[]
equipments?: { equipments?: {
id: number | string id: number | string
name: string name: string
@ -69,7 +72,7 @@ export const OrganizationApi = {
return await request.download({ url: `/mes/organization/export-excel`, params }) return await request.download({ url: `/mes/organization/export-excel`, params })
}, },
deviceParameterAnalysis: async (params: { keyword?: string }) => { deviceParameterAnalysis: async (params: { keyword?: string, showDevices?: number }) => {
return await request.get({ url: `/mes/organization/deviceParameterAnalysis`, params }) return await request.get({ url: `/mes/organization/deviceParameterAnalysis`, params })
} }
} }

@ -5,7 +5,7 @@ export interface TaskManagementVO {
name?: string name?: string
taskType?: number taskType?: number
deviceList?: string deviceList?: string
projectForm?: string projectForm?: string | number
startDate?: string startDate?: string
endDate?: string endDate?: string
cronExpression?: string cronExpression?: string

@ -37,8 +37,8 @@ export const TicketManagementApi = {
return await request.get({ url: `/mes/ticket-management/page`, params }) return await request.get({ url: `/mes/ticket-management/page`, params })
}, },
batchUpdateTicketStatus: async (params: { ids: string; jobStatus: string | number }) => { batchUpdateTicketStatus: async (data: { ids: string; jobStatus: string | number }) => {
return await request.put({ url: `/mes/ticket-management/batchUpdateStatus`, params }) return await request.put({ url: `/mes/ticket-management/batchUpdateStatus`, data })
}, },
getTicketResultsPage: async (params: any) => { getTicketResultsPage: async (params: any) => {

@ -0,0 +1,47 @@
import request from '@/config/axios'
export interface TaskManagementVO {
id?: number
name?: string
taskType?: number
deviceList?: string
projectForm?: string
startDate?: string
endDate?: string
cronExpression?: string
operableUsers?: string
enabled?: boolean
execFrequency?: string
frequencyValue?: string
time?: string
validMinutes?: number
creator?: string
createTime?: string
updateTime?: string
}
export const TaskManagementApi = {
getTaskManagementPage: async (params: any) => {
return await request.get({ url: `/mes/mold-task-management/page`, params })
},
createTaskManagementTicket: async (id: number | string) => {
return await request.post({ url: `/mes/mold-task-management/createMoldTicket`, params: { id } })
},
createTaskManagement: async (data: TaskManagementVO) => {
return await request.post({ url: `/mes/mold-task-management/create`, data })
},
updateTaskManagement: async (data: TaskManagementVO) => {
return await request.put({ url: `/mes/mold-task-management/update`, data })
},
deleteTaskManagement: async (ids: string) => {
return await request.delete({ url: `/mes/mold-task-management/delete?ids=` + ids })
},
exportTaskManagement: async (params: any) => {
return await request.download({ url: `/mes/mold-task-management/export-excel`, params })
}
}

@ -0,0 +1,51 @@
import request from '@/config/axios'
export interface TicketManagementVO {
id?: number
subjectId?: string
planId?: string
planNo?: string
deviceName?: string
planType?: string | number
configName?: string
jobStatus?: string | number
jobResult?: string
jobUser?: string
taskTime?: string
taskEndTime?: string
remark?: string
creator?: string
createTime?: string
}
export interface TicketResultVO {
id?: number
inspectionItemName?: string
inspectionMethod?: string
judgmentCriteria?: string
inspectionResult?: string | number
images?: string
remark?: string
inspectionTime?: string
inspector?: string
managementId?: string | number
createTime?: string
}
export const TicketManagementApi = {
getTicketManagementPage: async (params: any) => {
return await request.get({ url: `/mes/mold-ticket-management/page`, params })
},
batchUpdateTicketStatus: async (data: { ids: string; jobStatus: string | number }) => {
return await request.put({ url: `/mes/mold-ticket-management/batchUpdateStatus`, data })
},
getTicketResultsPage: async (params: any) => {
return await request.get({ url: `/mes/mold-ticket-results/page`, params })
},
batchUpdateTicketResults: async (data: TicketResultVO[]) => {
return await request.put({ url: `/mes/mold-ticket-results/batchUpdate`, data })
}
}

@ -2,23 +2,11 @@
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="6" :xs="24"> <el-col :span="6" :xs="24">
<ContentWrap class="h-1/1"> <ContentWrap class="h-1/1">
<el-input <el-input v-model="keyword" clearable placeholder="搜索设备或参数" class="!w-1/1" @input="handleKeywordChange" />
v-model="keyword"
clearable
placeholder="搜索设备或参数"
class="!w-1/1"
@input="handleKeywordChange"
/>
<div class="mt-12px"> <div class="mt-12px">
<el-tree <el-tree
ref="treeRef" ref="treeRef" v-loading="treeLoading" :data="treeData" :props="treeProps" node-key="id"
v-loading="treeLoading" highlight-current @node-click="handleTreeNodeClick" />
:data="treeData"
:props="treeProps"
node-key="id"
highlight-current
@node-click="handleTreeNodeClick"
/>
</div> </div>
</ContentWrap> </ContentWrap>
</el-col> </el-col>
@ -28,15 +16,9 @@
<el-form class="-mb-15px" :inline="true"> <el-form class="-mb-15px" :inline="true">
<el-form-item label="时间"> <el-form-item label="时间">
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期"
type="daterange" value-format="YYYY-MM-DD" :shortcuts="dateShortcuts"
start-placeholder="开始日期" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-360px" />
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
:shortcuts="dateShortcuts"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-360px"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button :disabled="!selectedParam" @click="handleQuery"> <el-button :disabled="!selectedParam" @click="handleQuery">
@ -50,22 +32,11 @@
</ContentWrap> </ContentWrap>
<ContentWrap> <ContentWrap>
<el-alert <el-alert v-if="selectedParam" :title="selectedParamTitle" type="info" :closable="false" class="mb-12px" />
v-if="selectedParam"
:title="selectedParamTitle"
type="info"
:closable="false"
class="mb-12px"
/>
<div v-loading="chartLoading"> <div v-loading="chartLoading">
<el-empty v-if="!selectedParam" description="请选择左侧参数" /> <el-empty v-if="chartState === 'empty'" description="暂无数据" />
<el-empty v-else-if="chartState === 'empty'" description="暂无数据" /> <el-empty v-else-if="!selectedParam" description="请选择左侧节点" />
<EChart <EChart v-else-if="chartState === 'ready'" :key="chartRenderKey" :options="chartOption" height="70vh" />
v-else-if="chartState === 'ready'"
:key="chartRenderKey"
:options="chartOption"
height="70vh"
/>
</div> </div>
</ContentWrap> </ContentWrap>
</el-col> </el-col>
@ -93,6 +64,7 @@ type DeviceTreeNode = {
modelId?: number modelId?: number
paramKey?: string paramKey?: string
unit?: string unit?: string
paramCount?: number
} }
type ApiTreeParameter = { type ApiTreeParameter = {
@ -111,6 +83,7 @@ type ApiTreeEquipment = {
type ApiTreeOrg = { type ApiTreeOrg = {
id: number id: number
name: string name: string
orgClass?: string
parentId?: number | null parentId?: number | null
equipments?: ApiTreeEquipment[] equipments?: ApiTreeEquipment[]
children?: ApiTreeOrg[] children?: ApiTreeOrg[]
@ -181,7 +154,7 @@ const selectedParamTitle = computed(() => {
const param = selectedParam.value const param = selectedParam.value
if (!param) return '' if (!param) return ''
const unitText = param.unit ? `${param.unit}` : '' const unitText = param.unit ? `${param.unit}` : ''
return `参数${param.label}${unitText}` return `节点${param.label}${unitText}`
}) })
const chartOption = computed<EChartsOption>(() => { const chartOption = computed<EChartsOption>(() => {
@ -213,17 +186,39 @@ let keywordTimer: number | undefined
const handleKeywordChange = () => { const handleKeywordChange = () => {
if (keywordTimer) window.clearTimeout(keywordTimer) if (keywordTimer) window.clearTimeout(keywordTimer)
keywordTimer = window.setTimeout(() => { keywordTimer = window.setTimeout(() => {
keywordTimer = undefined
loadTree() loadTree()
}, 300) }, 300)
} }
const toFiniteId = (value: any): number | undefined => {
if (typeof value === 'number') return Number.isFinite(value) ? value : undefined
if (typeof value === 'string') {
if (/^\d+$/.test(value.trim())) return Number(value)
const n = Number(value)
return Number.isFinite(n) ? n : undefined
}
return undefined
}
const buildTreeFromApi = (orgs: ApiTreeOrg[]): DeviceTreeNode[] => { const buildTreeFromApi = (orgs: ApiTreeOrg[]): DeviceTreeNode[] => {
const normalizedOrgs: ApiTreeOrg[] = (Array.isArray(orgs) ? orgs : []).map((o) => ({ const normalizeOrg = (org: any): ApiTreeOrg => {
...o, const children = Array.isArray(org?.children) ? org.children.map(normalizeOrg) : undefined
parentId: typeof o?.parentId === 'number' ? o.parentId : Number(o?.parentId ?? 0) || 0 const equipments = Array.isArray(org?.equipments) ? org.equipments : undefined
})) const parentId = typeof org?.parentId === 'number' ? org.parentId : Number(org?.parentId ?? 0) || 0
return {
id: Number(org?.id ?? 0) || 0,
name: String(org?.name ?? ''),
orgClass: org?.orgClass ? String(org.orgClass) : undefined,
parentId,
equipments,
children
}
}
const orgTree = handleTree(normalizedOrgs, 'id', 'parentId', 'children') as ApiTreeOrg[] const normalizedOrgs: ApiTreeOrg[] = Array.isArray(orgs) ? orgs.map(normalizeOrg) : []
const isNestedTree = normalizedOrgs.some((o) => typeof (o as any)?.children !== 'undefined')
const orgTree = isNestedTree ? normalizedOrgs : (handleTree(normalizedOrgs, 'id', 'parentId', 'children') as ApiTreeOrg[])
const toOrgNode = (org: ApiTreeOrg): DeviceTreeNode => { const toOrgNode = (org: ApiTreeOrg): DeviceTreeNode => {
const orgChildren = Array.isArray(org?.children) ? org.children.map(toOrgNode) : [] const orgChildren = Array.isArray(org?.children) ? org.children.map(toOrgNode) : []
@ -235,8 +230,8 @@ const buildTreeFromApi = (orgs: ApiTreeOrg[]): DeviceTreeNode[] => {
id: `param-${eq.id}-${p.id}`, id: `param-${eq.id}-${p.id}`,
label: p?.name ?? p?.code ?? String(p?.id ?? ''), label: p?.name ?? p?.code ?? String(p?.id ?? ''),
type: 'param', type: 'param',
deviceId: Number(eq.id), deviceId: toFiniteId(eq.id),
modelId: Number(p.id), modelId: toFiniteId(p.id),
paramKey: p?.code ? String(p.code) : undefined, paramKey: p?.code ? String(p.code) : undefined,
unit: p?.unit ? String(p.unit) : undefined unit: p?.unit ? String(p.unit) : undefined
})) }))
@ -245,6 +240,8 @@ const buildTreeFromApi = (orgs: ApiTreeOrg[]): DeviceTreeNode[] => {
id: `equipment-${org.id}-${eq.id}`, id: `equipment-${org.id}-${eq.id}`,
label: eq?.name ?? String(eq?.id ?? ''), label: eq?.name ?? String(eq?.id ?? ''),
type: 'device', type: 'device',
deviceId: toFiniteId(eq.id),
paramCount: params.length,
children: paramNodes.length ? paramNodes : undefined children: paramNodes.length ? paramNodes : undefined
} }
}) })
@ -261,11 +258,19 @@ const buildTreeFromApi = (orgs: ApiTreeOrg[]): DeviceTreeNode[] => {
return Array.isArray(orgTree) ? orgTree.map(toOrgNode) : [] return Array.isArray(orgTree) ? orgTree.map(toOrgNode) : []
} }
const extractApiOrgs = (res: any): ApiTreeOrg[] => {
if (Array.isArray(res)) return res as ApiTreeOrg[]
if (Array.isArray(res?.data)) return res.data as ApiTreeOrg[]
if (Array.isArray(res?.result)) return res.result as ApiTreeOrg[]
if (Array.isArray(res?.list)) return res.list as ApiTreeOrg[]
return []
}
const loadTree = async () => { const loadTree = async () => {
treeLoading.value = true treeLoading.value = true
try { try {
const data = await OrganizationApi.deviceParameterAnalysis({ keyword: keyword.value || undefined }) const res = await OrganizationApi.deviceParameterAnalysis({ keyword: keyword.value || undefined, showDevices: 1 })
treeData.value = buildTreeFromApi(Array.isArray(data) ? (data as ApiTreeOrg[]) : []) treeData.value = buildTreeFromApi(extractApiOrgs(res))
if (keyword.value) { if (keyword.value) {
treeRef.value?.setCurrentKey?.(undefined) treeRef.value?.setCurrentKey?.(undefined)
selectedParam.value = null selectedParam.value = null
@ -311,7 +316,7 @@ const toNumber = (value: any) => {
const fetchChart = async () => { const fetchChart = async () => {
const param = selectedParam.value const param = selectedParam.value
if (!selectedDeviceId.value || !selectedModelId.value) return if (typeof selectedDeviceId.value !== 'number' || !Number.isFinite(selectedDeviceId.value)) return
ensureDateRange() ensureDateRange()
const [start, end] = dateRange.value const [start, end] = dateRange.value
@ -325,17 +330,21 @@ const fetchChart = async () => {
chartLoading.value = true chartLoading.value = true
resetChartData() resetChartData()
try { try {
const data = await DeviceModelAttributeApi.operationAnalysisDetails({ const req: Record<string, any> = {
deviceId: selectedDeviceId.value, deviceId: selectedDeviceId.value,
modelId: selectedModelId.value,
collectionStartTime: buildDateTime(start, '00:00:00'), collectionStartTime: buildDateTime(start, '00:00:00'),
collectionEndTime: buildDateTime(end, '23:59:59') collectionEndTime: buildDateTime(end, '23:59:59')
}) }
if (typeof selectedModelId.value === 'number' && Number.isFinite(selectedModelId.value)) {
req.modelId = selectedModelId.value
}
const data = await DeviceModelAttributeApi.operationAnalysisDetails(req as any)
const rows: Record<string, any>[] = Array.isArray(data) ? data : [] const rows: Record<string, any>[] = Array.isArray(data) ? data : []
if (!rows.length) { if (!rows.length) {
chartXAxis.value = [] chartXAxis.value = []
chartSeries.value = [] chartSeries.value = []
chartState.value = 'empty' chartState.value = 'empty'
message.warning('该节点下暂无参数')
return return
} }
@ -390,16 +399,50 @@ const fetchChart = async () => {
} }
const handleTreeNodeClick = async (data: DeviceTreeNode) => { const handleTreeNodeClick = async (data: DeviceTreeNode) => {
if (data.type !== 'param') return if (keywordTimer) {
window.clearTimeout(keywordTimer)
keywordTimer = undefined
}
const hasChildren = Array.isArray(data?.children) && data.children.length > 0
if (hasChildren) {
return
}
const isEquipmentNode = typeof data?.id === 'string' && data.id.startsWith('equipment-')
if (isEquipmentNode && (data.paramCount ?? 0) <= 0) {
selectedParam.value = data
selectedDeviceId.value = undefined
selectedModelId.value = undefined
chartState.value = 'empty'
resetChartData()
message.warning('该设备下没有参数')
return
}
const toNodeIds = (node: DeviceTreeNode): { deviceId?: number; modelId?: number } => {
const modelId = typeof node.modelId === 'number' && Number.isFinite(node.modelId) ? node.modelId : undefined
const deviceId = typeof node.deviceId === 'number' && Number.isFinite(node.deviceId) ? node.deviceId : undefined
if (typeof deviceId === 'number') return { deviceId, modelId }
const parts = String(node.id ?? '').split('-').filter(Boolean)
const last = parts.length ? parts[parts.length - 1] : undefined
const parsed = toFiniteId(last)
if (typeof parsed !== 'number' || !Number.isFinite(parsed) || parsed <= 0) {
return { modelId }
}
return { deviceId: parsed, modelId }
}
const { deviceId, modelId } = toNodeIds(data)
selectedParam.value = data selectedParam.value = data
selectedDeviceId.value = data.deviceId selectedDeviceId.value = deviceId
selectedModelId.value = data.modelId selectedModelId.value = modelId
dateRange.value = buildDefaultDateRange() dateRange.value = buildDefaultDateRange()
await fetchChart() await fetchChart()
} }
const handleQuery = async () => { const handleQuery = async () => {
if (!selectedParam.value) return
await fetchChart() await fetchChart()
} }

@ -101,7 +101,7 @@ v-model="formData.confirmBy" filterable clearable placeholder="请选择验收
</el-form> </el-form>
<!-- 子表的表单 --> <!-- 子表的表单 -->
<el-tabs v-model="subTabsName"> <el-tabs v-model="subTabsName">
<el-tab-pane label="设备维修记录行" name="dvRepairLine"> <el-tab-pane label="设备维修项目行" name="dvRepairLine">
<DvRepairLineForm ref="dvRepairLineFormRef" :repair-id="formData.id" /> <DvRepairLineForm ref="dvRepairLineFormRef" :repair-id="formData.id" />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>

@ -1,12 +1,6 @@
<template> <template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px"> <Dialog :title="dialogTitle" v-model="dialogVisible" width="800px">
<el-form <el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="表编码" prop="code"> <el-form-item label="表编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入表编码" :disabled="formType === 'update'" /> <el-input v-model="formData.code" placeholder="请输入表编码" :disabled="formType === 'update'" />
</el-form-item> </el-form-item>
@ -16,59 +10,29 @@
</el-form-item> </el-form-item>
<el-form-item label="能源类型" prop="deviceTypeId"> <el-form-item label="能源类型" prop="deviceTypeId">
<el-select <el-select v-model="formData.deviceTypeId" @change="handleDeviceTypeChange" placeholder="请选择能源类型" clearable
v-model="formData.deviceTypeId" filterable class="!w-full">
@change="handleDeviceTypeChange"
placeholder="请选择能源类型"
clearable
filterable
class="!w-full"
>
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="所属区域" prop="orgId"> <el-form-item label="所属区域" prop="orgId">
<el-select <el-tree-select v-model="formData.orgId" :data="orgSelectTree" :props="orgTreeSelectProps" filterable
v-model="formData.orgId" check-strictly clearable class="!w-full" placeholder="根据区域结构筛选列表" :loading="analysisLoading"
placeholder="根据区域结构筛选列表" :render-after-expand="false" @change="handleOrgChange" />
clearable
filterable
class="!w-full"
:loading="analysisLoading"
@change="handleOrgChange"
>
<el-option v-for="org in analysisList" :key="org.id" :label="org.name" :value="org.id" />
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="计算规则" prop="operationRulesVOList"> <el-form-item label="计算规则" prop="operationRulesVOList">
<div class="w-full flex flex-col gap-8px"> <div class="w-full flex flex-col gap-8px">
<div <div v-for="(rule, index) in formData.operationRulesVOList" :key="index"
v-for="(rule, index) in formData.operationRulesVOList" class="w-full flex items-center gap-8px">
:key="index" <el-tree-select v-model="rule.pointValue" :data="equipmentTree" :props="treeSelectProps" filterable
class="w-full flex items-center gap-8px" clearable class="!w-full" placeholder="根据设备选择点位" :loading="devicePointLoading"
> @change="(val) => handlePointSelected(index, val)" />
<el-tree-select
v-model="rule.pointValue"
:data="equipmentTree"
:props="treeSelectProps"
check-strictly
filterable
clearable
class="!w-full"
placeholder="根据设备选择点位"
:disabled="!formData.orgId"
@change="(val) => handlePointSelected(index, val)"
/>
<template v-if="index < formData.operationRulesVOList.length - 1"> <template v-if="index < formData.operationRulesVOList.length - 1">
<el-select <el-select v-model="formData.operationRulesVOList[index].operator" placeholder="运算符" class="!w-110px"
v-model="formData.operationRulesVOList[index].operator" clearable>
placeholder="运算符"
class="!w-110px"
clearable
>
<el-option v-for="op in operatorOptions" :key="op" :label="op" :value="op" /> <el-option v-for="op in operatorOptions" :key="op" :label="op" :value="op" />
</el-select> </el-select>
</template> </template>
@ -77,13 +41,10 @@
<el-button link type="primary" @click="addRule"> <el-button link type="primary" @click="addRule">
<Icon icon="ep:plus" /> <Icon icon="ep:plus" />
</el-button> </el-button>
<el-button <el-button link type="danger" :disabled="formData.operationRulesVOList.length <= 1" @click="removeRule">
link <el-icon>
type="danger" <Remove />
:disabled="formData.operationRulesVOList.length <= 1" </el-icon>
@click="removeRule"
>
<Icon icon="ep:minus" />
</el-button> </el-button>
</div> </div>
</template> </template>
@ -106,8 +67,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { EnergyDeviceApi, EnergyDeviceVO } from '@/api/mes/energydevice' import { EnergyDeviceApi, EnergyDeviceVO } from '@/api/mes/energydevice'
import {EnergyTypeApi, EnergyTypeVO} from "@/api/mes/energytype"; import { EnergyTypeApi, EnergyTypeVO } from "@/api/mes/energytype";
import { OrganizationApi, DeviceParameterAnalysisNodeVO } from '@/api/mes/organization' import { OrganizationApi, DeviceParameterAnalysisNodeVO } from '@/api/mes/organization'
import { DeviceApi } from '@/api/iot/device'
import { Remove } from '@element-plus/icons-vue'
import { handleTree } from '@/utils/tree'
/** 能源设备 表单 */ /** 能源设备 表单 */
defineOptions({ name: 'EnergyDeviceForm' }) defineOptions({ name: 'EnergyDeviceForm' })
@ -147,7 +111,55 @@ const formRules = reactive({
const formRef = ref() // Ref const formRef = ref() // Ref
const analysisLoading = ref(false) const analysisLoading = ref(false)
const analysisList = ref<DeviceParameterAnalysisNodeVO[]>([]) type OrgAnalysisNode = DeviceParameterAnalysisNodeVO & { children?: OrgAnalysisNode[] }
const analysisList = ref<OrgAnalysisNode[]>([])
const analysisTree = computed(() => {
const list = analysisList.value ?? []
if (!list.length) return [] as OrgAnalysisNode[]
const hasChildren = list.some((n) => Array.isArray((n as any).children) && (n as any).children.length)
if (hasChildren) {
return list.map((n) => ({ ...n })) as OrgAnalysisNode[]
}
const hasParentId = list.some((n) => (n as any).parentId !== undefined && (n as any).parentId !== null)
if (!hasParentId) {
return list.map((n) => ({ ...n })) as OrgAnalysisNode[]
}
const cloned = list.map((n) => ({ ...n })) as OrgAnalysisNode[]
return handleTree(cloned as any[], 'id', 'parentId', 'children') as OrgAnalysisNode[]
})
type OrgSelectNode = {
id: number | string
name: string
children?: OrgSelectNode[]
}
const orgSelectTree = computed(() => {
const tree = analysisTree.value ?? []
if (!tree.length) return [] as OrgSelectNode[]
const pruneAndAttach = (nodes: OrgAnalysisNode[]): OrgSelectNode[] => {
const kept: OrgSelectNode[] = []
nodes.forEach((node) => {
const childNodes = Array.isArray(node.children) ? pruneAndAttach(node.children) : []
kept.push({ id: node.id, name: node.name, children: childNodes.length ? childNodes : undefined })
})
return kept
}
return pruneAndAttach(tree)
})
const orgTreeSelectProps = {
label: 'name',
children: 'children',
value: 'id'
}
const treeSelectProps = { const treeSelectProps = {
label: 'name', label: 'name',
@ -158,21 +170,100 @@ const treeSelectProps = {
const operatorOptions = ['+', '-', '*', '/'] const operatorOptions = ['+', '-', '*', '/']
const currentOrgNode = computed(() => { const isSameId = (a: any, b: any) => {
return analysisList.value.find((o) => o.id === formData.value.orgId) return String(a ?? '') === String(b ?? '')
}) }
const findOrgNode = (nodes: OrgAnalysisNode[], id: any): OrgAnalysisNode | undefined => {
for (const node of nodes) {
if (isSameId(node.id, id)) return node
const children = Array.isArray(node.children) ? node.children : []
if (children.length) {
const found = findOrgNode(children, id)
if (found) return found
}
}
return undefined
}
const devicePointLoading = ref(false)
const devicePointTree = ref<any[]>([])
const loadDevicePointTree = async () => {
if (devicePointTree.value.length > 0) {
return
}
devicePointLoading.value = true
try {
const res: any = await DeviceApi.devicePointList()
const list = Array.isArray(res) ? res : Array.isArray(res?.data) ? res.data : []
const normalizeToString = (v: any) => String(v ?? '')
const isNonEmptyArray = (v: any) => Array.isArray(v) && v.length > 0
const deviceGroups = new Map<string, { deviceId: string; deviceName: string; points: any[] }>()
const pushPoint = (deviceId: any, deviceName: any, pointId: any, pointName: any) => {
const dId = normalizeToString(deviceId)
if (!dId) return
const group = deviceGroups.get(dId) ?? { deviceId: dId, deviceName: String(deviceName ?? ''), points: [] }
if (!group.deviceName) group.deviceName = String(deviceName ?? '')
const pId = normalizeToString(pointId)
if (!pId) {
deviceGroups.set(dId, group)
return
}
group.points.push({ id: `${dId}:${pId}`, name: `${group.deviceName}: ${String(pointName ?? '')}`.trim() })
deviceGroups.set(dId, group)
}
if (isNonEmptyArray(list) && ((list[0] as any)?.deviceId !== undefined || (list[0] as any)?.deviceName !== undefined)) {
; (list as any[]).forEach((row) => {
const deviceId = row?.deviceId ?? row?.devId ?? row?.device_id
const deviceName = row?.deviceName ?? row?.devName ?? row?.device_name
const points =
row?.contactModelDOList ??
row?.points ??
row?.pointList ??
row?.devicePoints ??
row?.devicePointList ??
row?.parameters
if (Array.isArray(points)) {
points.forEach((p) => {
const pointId = p?.pointId ?? p?.id
const pointName = p?.attributeName ?? p?.pointName ?? p?.name
pushPoint(deviceId, deviceName, pointId, pointName)
})
} else {
const pointId = row?.pointId ?? row?.attributeId ?? row?.devicePointId
const pointName = row?.attributeName ?? row?.pointName ?? row?.attributeName
pushPoint(deviceId, deviceName, pointId, pointName)
}
})
} else if (isNonEmptyArray(list)) {
; (list as any[]).forEach((row) => {
const deviceId = row?.deviceId ?? row?.devId
const deviceName = row?.deviceName ?? row?.devName
const pointId = row?.pointId ?? row?.id
const pointName = row?.attributeName ?? row?.pointName ?? row?.name
pushPoint(deviceId, deviceName, pointId, pointName)
})
}
devicePointTree.value = Array.from(deviceGroups.values())
.map((g) => {
const children = (g.points ?? []).filter((p) => p?.id)
if (!children.length) return null
return { id: `device:${g.deviceId}`, name: g.deviceName, disabled: true, children }
})
.filter(Boolean) as any[]
} finally {
devicePointLoading.value = false
}
}
const equipmentTree = computed(() => { const equipmentTree = computed(() => {
const equipments = currentOrgNode.value?.equipments ?? [] return devicePointTree.value
return equipments.map((eq) => ({
id: `device:${eq.id}`,
name: eq.name,
disabled: true,
children: (eq.parameters ?? []).map((p) => ({
id: `${eq.id}:${p.id}`,
name: `${eq.name}: ${p.name}`
}))
}))
}) })
/** 打开弹窗 */ /** 打开弹窗 */
@ -183,13 +274,14 @@ const open = async (type: string, id?: number) => {
resetForm() resetForm()
await loadAnalysis() await loadAnalysis()
await loadDevicePointTree()
// //
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
formData.value = await EnergyDeviceApi.getEnergyDevice(id) formData.value = await EnergyDeviceApi.getEnergyDevice(id)
if (!Array.isArray((formData.value as any).operationRulesVOList)) { if (!Array.isArray((formData.value as any).operationRulesVOList)) {
;(formData.value as any).operationRulesVOList = [ ; (formData.value as any).operationRulesVOList = [
{ deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined } { deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined }
] ]
} }
@ -209,21 +301,23 @@ const submitForm = async () => {
// //
formLoading.value = true formLoading.value = true
try { try {
const data = formData.value as unknown as EnergyDeviceVO const base = formData.value as unknown as EnergyDeviceVO
data.deviceTypeName = typeList.value.find(item => item.id === data.deviceTypeId)?.name const payload: any = {
...(base as any),
deviceTypeName: typeList.value.find(item => item.id === base.deviceTypeId)?.name,
operationRulesVOList: buildOperationRulesPayload()
}
const org = analysisList.value.find((o) => o.id === data.orgId) const org = findOrgNode(analysisTree.value ?? [], base.orgId)
if (org) { if (org) {
;(data as any).orgName = org.name payload.orgName = org.name
} }
;(data as any).operationRulesVOList = buildOperationRulesPayload()
if (formType.value === 'create') { if (formType.value === 'create') {
await EnergyDeviceApi.createEnergyDevice(data) await EnergyDeviceApi.createEnergyDevice(payload)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
} else { } else {
await EnergyDeviceApi.updateEnergyDevice(data) await EnergyDeviceApi.updateEnergyDevice(payload)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
} }
dialogVisible.value = false dialogVisible.value = false
@ -267,7 +361,9 @@ const loadAnalysis = async () => {
} }
analysisLoading.value = true analysisLoading.value = true
try { try {
analysisList.value = await OrganizationApi.deviceParameterAnalysis({}) const res: any = await OrganizationApi.deviceParameterAnalysis({ showDevices: 2 })
const list = Array.isArray(res) ? res : Array.isArray(res?.data) ? res.data : []
analysisList.value = list as OrgAnalysisNode[]
} finally { } finally {
analysisLoading.value = false analysisLoading.value = false
} }
@ -278,11 +374,8 @@ const handleDeviceTypeChange = () => {
} }
const handleOrgChange = () => { const handleOrgChange = () => {
const org = analysisList.value.find((o) => o.id === formData.value.orgId) const org = findOrgNode(analysisTree.value ?? [], formData.value.orgId)
formData.value.orgName = org?.name formData.value.orgName = org?.name
formData.value.operationRulesVOList = [
{ deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined }
]
} }
const handlePointSelected = (index: number, val: any) => { const handlePointSelected = (index: number, val: any) => {

@ -31,13 +31,12 @@
<el-form-item label="项目表单" prop="projectForm"> <el-form-item label="项目表单" prop="projectForm">
<el-select <el-select
v-model="formData.projectForm" v-model="formData.projectForm"
multiple
filterable filterable
clearable clearable
placeholder="请选择项目表单" placeholder="请选择项目表单"
class="!w-full" class="!w-full"
> >
<el-option v-for="item in planOptions" :key="String(item.id)" :label="item.planName" :value="String(item.id)" /> <el-option v-for="item in planOptions" :key="String(item.id)" :label="item.planName" :value="Number(item.id)" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="起止日期" prop="dateRange"> <el-form-item label="起止日期" prop="dateRange">
@ -142,7 +141,7 @@ const formData = ref({
name: undefined as string | undefined, name: undefined as string | undefined,
taskType: undefined as number | undefined, taskType: undefined as number | undefined,
deviceList: [] as string[], deviceList: [] as string[],
projectForm: [] as string[], projectForm: undefined as number | undefined,
dateRange: [] as string[], dateRange: [] as string[],
cronExpression: undefined as string | undefined, cronExpression: undefined as string | undefined,
operableUsers: [] as string[], operableUsers: [] as string[],
@ -152,7 +151,10 @@ const formData = ref({
const formRules = reactive({ const formRules = reactive({
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }], name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
taskType: [{ required: true, message: '类型不能为空', trigger: 'change' }], taskType: [{ required: true, message: '类型不能为空', trigger: 'change' }],
enabled: [{ required: true, message: '是否启用不能为空', trigger: 'change' }] enabled: [{ required: true, message: '是否启用不能为空', trigger: 'change' }],
deviceList: [{ required: true, message: '设备列表不能为空', trigger: 'change' }],
projectForm: [{ required: true, message: '项目表单不能为空', trigger: 'change' }],
dateRange: [{required: true, message: '起止日期不能为空', trigger: 'change' }]
}) })
const resetForm = () => { const resetForm = () => {
@ -161,7 +163,7 @@ const resetForm = () => {
name: undefined, name: undefined,
taskType: undefined, taskType: undefined,
deviceList: [], deviceList: [],
projectForm: [], projectForm: undefined,
dateRange: [], dateRange: [],
cronExpression: undefined, cronExpression: undefined,
operableUsers: [], operableUsers: [],
@ -184,14 +186,16 @@ const open = async (type: string, row?: TaskManagementVO) => {
formData.value.deviceList = parseIdsValue((row as any).deviceList) formData.value.deviceList = parseIdsValue((row as any).deviceList)
const projectFormIds = parseIdsValue((row as any).projectForm) const projectFormIds = parseIdsValue((row as any).projectForm)
if (projectFormIds.length) { if (projectFormIds.length) {
formData.value.projectForm = projectFormIds const n = Number(projectFormIds[0])
formData.value.projectForm = Number.isFinite(n) ? n : undefined
} else { } else {
const projectFormNames = parseIdsValue((row as any).projectFormName) const projectFormNames = parseIdsValue((row as any).projectFormName)
const mappedIds = projectFormNames const mapped = projectFormNames
.map((name) => planOptions.value.find((p) => p.planName === name)?.id) .map((name) => planOptions.value.find((p) => p.planName === name)?.id)
.filter((id) => id !== undefined && id !== null) .filter((id) => id !== undefined && id !== null)
.map((id) => String(id)) .map((id) => Number(id))
formData.value.projectForm = mappedIds const firstId = mapped[0]
formData.value.projectForm = typeof firstId === 'number' && Number.isFinite(firstId) ? firstId : undefined
} }
formData.value.dateRange = [row.startDate, row.endDate].filter(Boolean) as string[] formData.value.dateRange = [row.startDate, row.endDate].filter(Boolean) as string[]
formData.value.cronExpression = row.cronExpression formData.value.cronExpression = row.cronExpression
@ -220,7 +224,7 @@ const submitForm = async () => {
name: formData.value.name, name: formData.value.name,
taskType: formData.value.taskType, taskType: formData.value.taskType,
deviceList: toCommaSeparatedIds((formData.value as any).deviceList), deviceList: toCommaSeparatedIds((formData.value as any).deviceList),
projectForm: toCommaSeparatedIds((formData.value as any).projectForm), projectForm: formData.value.projectForm,
startDate: startDate || undefined, startDate: startDate || undefined,
endDate: endDate || undefined, endDate: endDate || undefined,
cronExpression: formData.value.cronExpression, cronExpression: formData.value.cronExpression,

@ -22,13 +22,16 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="项目表单" prop="projectForm"> <el-form-item label="项目表单" prop="projectForm">
<el-input <el-select
v-model="queryParams.projectForm" v-model="queryParams.projectForm"
placeholder="请输入项目表单" multiple
filterable
clearable clearable
@keyup.enter="handleQuery" placeholder="请选择项目表单"
class="!w-240px" class="!w-240px"
/> >
<el-option v-for="item in planOptions" :key="String(item.id)" :label="item.planName" :value="String(item.id)" />
</el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
@ -71,7 +74,7 @@
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="项目表单" align="center" prop="projectForm" min-width="140" /> <el-table-column label="项目表单" align="center" prop="projectFormName" min-width="140" />
<el-table-column label="开始日期" align="center" prop="startDate" :formatter="dateFormatter2" width="120" /> <el-table-column label="开始日期" align="center" prop="startDate" :formatter="dateFormatter2" width="120" />
<el-table-column label="结束日期" align="center" prop="endDate" :formatter="dateFormatter2" width="120" /> <el-table-column label="结束日期" align="center" prop="endDate" :formatter="dateFormatter2" width="120" />
<el-table-column label="cron 表达式" align="center" prop="cronExpression" min-width="180" /> <el-table-column label="cron 表达式" align="center" prop="cronExpression" min-width="180" />
@ -124,6 +127,7 @@ import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime' import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
import download from '@/utils/download' import download from '@/utils/download'
import { TaskManagementApi, TaskManagementVO } from '@/api/mes/taskManagement' import { TaskManagementApi, TaskManagementVO } from '@/api/mes/taskManagement'
import { PlanMaintenanceApi } from '@/api/mes/planmaintenance'
import TaskManagementForm from './TaskManagementForm.vue' import TaskManagementForm from './TaskManagementForm.vue'
defineOptions({ name: 'TaskManagement' }) defineOptions({ name: 'TaskManagement' })
@ -142,19 +146,52 @@ const total = ref(0)
const exportLoading = ref(false) const exportLoading = ref(false)
const selectedIds = ref<number[]>([]) const selectedIds = ref<number[]>([])
type PlanOption = {
id: number | string
planName: string
}
const planOptions = ref<PlanOption[]>([])
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
name: undefined as string | undefined, name: undefined as string | undefined,
taskType: undefined as number | undefined, taskType: undefined as number | undefined,
projectForm: undefined as string | undefined projectForm: [] as string[]
}) })
const queryFormRef = ref() const queryFormRef = ref()
const parseIdsValue = (value: any): string[] => {
if (!value) return []
if (Array.isArray(value)) return value.map((v) => String(v).trim()).filter(Boolean)
return String(value)
.split(',')
.map((v) => v.trim())
.filter(Boolean)
}
const toCommaSeparatedIds = (value: any): string | undefined => {
const ids = parseIdsValue(value)
return ids.length ? ids.join(',') : undefined
}
const ensurePlanOptionsLoaded = async () => {
const planRes = await PlanMaintenanceApi.getPlanMaintenancePage({ pageNo: 1, pageSize: 100 })
planOptions.value = (planRes?.list ?? []) as PlanOption[]
}
const buildQueryParams = () => {
return {
...queryParams,
projectForm: toCommaSeparatedIds(queryParams.projectForm)
}
}
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
try { try {
const data = await TaskManagementApi.getTaskManagementPage(queryParams) const data = await TaskManagementApi.getTaskManagementPage(buildQueryParams())
list.value = data.list list.value = data.list
total.value = data.total total.value = data.total
} finally { } finally {
@ -211,7 +248,7 @@ const handleExport = async () => {
try { try {
await message.exportConfirm() await message.exportConfirm()
exportLoading.value = true exportLoading.value = true
const params: Record<string, any> = { ...queryParams } const params: Record<string, any> = buildQueryParams()
if (selectedIds.value.length > 0) { if (selectedIds.value.length > 0) {
params.ids = selectedIds.value.join(',') params.ids = selectedIds.value.join(',')
} }
@ -224,6 +261,6 @@ const handleExport = async () => {
} }
onMounted(() => { onMounted(() => {
getList() Promise.all([ensurePlanOptionsLoaded(), getList()])
}) })
</script> </script>

@ -73,7 +73,7 @@ v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" min-width="160" /> <!-- <el-table-column label="备注" align="center" prop="remark" min-width="160" /> -->
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180" /> <el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180" />
</el-table> </el-table>

@ -1,12 +1,6 @@
<template> <template>
<Dialog :title="dialogTitle" v-model="dialogVisible"> <Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form <el-form ref="formRef" :model="formData" :rules="formRules" label-width="110px" v-loading="formLoading">
ref="formRef"
:model="formData"
:rules="formRules"
label-width="110px"
v-loading="formLoading"
>
<el-form-item label="名称" prop="name"> <el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" /> <el-input v-model="formData.name" placeholder="请输入名称" />
</el-form-item> </el-form-item>
@ -16,58 +10,32 @@
<el-radio :label="2">保养</el-radio> <el-radio :label="2">保养</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="设备列表" prop="deviceList"> <el-form-item label="模具列表" prop="moldList">
<el-select <el-select v-model="formData.moldList" multiple filterable clearable placeholder="请选择设备列表" class="!w-full">
v-model="formData.deviceList" <el-option v-for="item in deviceOptions" :key="String(item.id)" :label="item.name" :value="String(item.id)" />
multiple
filterable
clearable
placeholder="请选择设备列表"
class="!w-full"
>
<el-option v-for="item in deviceOptions" :key="String(item.id)" :label="item.deviceName" :value="String(item.id)" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="项目表单" prop="projectForm"> <el-form-item label="项目表单" prop="projectForm">
<el-select <el-select v-model="formData.projectForm" multiple filterable clearable placeholder="请选择项目表单" class="!w-full">
v-model="formData.projectForm" <el-option v-for="item in planOptions" :key="String(item.id)" :label="item.planName" :value="item.id" />
multiple
filterable
clearable
placeholder="请选择项目表单"
class="!w-full"
>
<el-option v-for="item in planOptions" :key="String(item.id)" :label="item.planName" :value="String(item.id)" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="起止日期" prop="dateRange"> <el-form-item label="起止日期" prop="dateRange">
<el-date-picker <el-date-picker v-model="formData.dateRange" value-format="YYYY-MM-DD" type="daterange" start-placeholder=""
v-model="formData.dateRange" end-placeholder="结束日期" class="!w-320px" />
value-format="YYYY-MM-DD"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
class="!w-320px"
/>
</el-form-item> </el-form-item>
<el-form-item label="cron 表达式" prop="cronExpression"> <el-form-item label="cron 表达式" prop="cronExpression">
<crontab v-model="formData.cronExpression" /> <crontab v-model="formData.cronExpression" />
</el-form-item> </el-form-item>
<el-form-item label="可操作人" prop="operableUsers"> <el-form-item label="可操作人" prop="operableUsers">
<el-select <el-select v-model="formData.operableUsers" multiple filterable clearable placeholder="请选择可操作人" class="!w-full">
v-model="formData.operableUsers"
multiple
filterable
clearable
placeholder="请选择可操作人"
class="!w-full"
>
<el-option v-for="item in users" :key="String(item.id)" :label="item.nickname" :value="String(item.id)" /> <el-option v-for="item in users" :key="String(item.id)" :label="item.nickname" :value="String(item.id)" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="是否启用" prop="enabled"> <el-form-item label="是否启用" prop="enabled">
<el-radio-group v-model="formData.enabled"> <el-radio-group v-model="formData.enabled">
<el-radio v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" :key="String(dict.value)" :label="dict.value"> <el-radio v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" :key="String(dict.value)"
:label="dict.value">
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
@ -82,9 +50,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { DICT_TYPE, getBoolDictOptions } from '@/utils/dict' import { DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
import { TaskManagementApi, TaskManagementVO } from '@/api/mes/taskManagement' import { TaskManagementApi, TaskManagementVO } from '@/api/mold/taskManagement'
import { DeviceLedgerApi } from '@/api/mes/deviceledger' import { MoldBrandApi } from '@/api/erp/mold'
import { PlanMaintenanceApi } from '@/api/mes/planmaintenance' import { PlanMaintenanceApi } from '@/api/mold/planmaintenance'
import { getSimpleUserList, UserVO } from '@/api/system/user' import { getSimpleUserList, UserVO } from '@/api/system/user'
defineOptions({ name: 'MoldTaskConfigurationForm' }) defineOptions({ name: 'MoldTaskConfigurationForm' })
@ -100,7 +68,7 @@ const formRef = ref()
type DeviceOption = { type DeviceOption = {
id: number | string id: number | string
deviceName: string name: string
} }
type PlanOption = { type PlanOption = {
@ -128,8 +96,8 @@ const toCommaSeparatedIds = (value: any): string | undefined => {
const ensureOptionsLoaded = async () => { const ensureOptionsLoaded = async () => {
const [deviceRes, planRes, userRes] = await Promise.all([ const [deviceRes, planRes, userRes] = await Promise.all([
DeviceLedgerApi.getDeviceLedgerPage({}), MoldBrandApi.getMoldPage({}),
PlanMaintenanceApi.getPlanMaintenancePage({ pageNo: 1, pageSize: 100 }), PlanMaintenanceApi.getPlanMaintenancePage({}),
getSimpleUserList() getSimpleUserList()
]) ])
deviceOptions.value = (deviceRes?.list ?? []) as DeviceOption[] deviceOptions.value = (deviceRes?.list ?? []) as DeviceOption[]
@ -141,7 +109,7 @@ const formData = ref({
id: undefined as number | undefined, id: undefined as number | undefined,
name: undefined as string | undefined, name: undefined as string | undefined,
taskType: undefined as number | undefined, taskType: undefined as number | undefined,
deviceList: [] as string[], moldList: [] as string[],
projectForm: [] as string[], projectForm: [] as string[],
dateRange: [] as string[], dateRange: [] as string[],
cronExpression: undefined as string | undefined, cronExpression: undefined as string | undefined,
@ -152,7 +120,10 @@ const formData = ref({
const formRules = reactive({ const formRules = reactive({
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }], name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
taskType: [{ required: true, message: '类型不能为空', trigger: 'change' }], taskType: [{ required: true, message: '类型不能为空', trigger: 'change' }],
enabled: [{ required: true, message: '是否启用不能为空', trigger: 'change' }] enabled: [{ required: true, message: '是否启用不能为空', trigger: 'change' }],
moldList: [{ required: true, message: '设备列表不能为空', trigger: 'change' }],
projectForm: [{ required: true, message: '项目表单不能为空', trigger: 'change' }],
dateRange: [{ required: true, message: '起止日期不能为空', trigger: 'change' }]
}) })
const resetForm = () => { const resetForm = () => {
@ -160,7 +131,7 @@ const resetForm = () => {
id: undefined, id: undefined,
name: undefined, name: undefined,
taskType: undefined, taskType: undefined,
deviceList: [], moldList: [],
projectForm: [], projectForm: [],
dateRange: [], dateRange: [],
cronExpression: undefined, cronExpression: undefined,
@ -181,7 +152,7 @@ const open = async (type: string, row?: TaskManagementVO) => {
formData.value.id = row.id formData.value.id = row.id
formData.value.name = row.name formData.value.name = row.name
formData.value.taskType = row.taskType formData.value.taskType = row.taskType
formData.value.deviceList = parseIdsValue((row as any).deviceList) formData.value.moldList = parseIdsValue((row as any).moldList)
const projectFormIds = parseIdsValue((row as any).projectForm) const projectFormIds = parseIdsValue((row as any).projectForm)
if (projectFormIds.length) { if (projectFormIds.length) {
formData.value.projectForm = projectFormIds formData.value.projectForm = projectFormIds
@ -219,7 +190,7 @@ const submitForm = async () => {
id: formData.value.id, id: formData.value.id,
name: formData.value.name, name: formData.value.name,
taskType: formData.value.taskType, taskType: formData.value.taskType,
deviceList: toCommaSeparatedIds((formData.value as any).deviceList), moldList: toCommaSeparatedIds((formData.value as any).moldList),
projectForm: toCommaSeparatedIds((formData.value as any).projectForm), projectForm: toCommaSeparatedIds((formData.value as any).projectForm),
startDate: startDate || undefined, startDate: startDate || undefined,
endDate: endDate || undefined, endDate: endDate || undefined,
@ -242,4 +213,3 @@ const submitForm = async () => {
} }
} }
</script> </script>

@ -22,13 +22,16 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="项目表单" prop="projectForm"> <el-form-item label="项目表单" prop="projectForm">
<el-input <el-select
v-model="queryParams.projectForm" v-model="queryParams.projectForm"
placeholder="请输入项目表单" multiple
filterable
clearable clearable
@keyup.enter="handleQuery" placeholder="请选择项目表单"
class="!w-240px" class="!w-240px"
/> >
<el-option v-for="item in planOptions" :key="String(item.id)" :label="item.planName" :value="String(item.id)" />
</el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
@ -71,7 +74,7 @@
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="项目表单" align="center" prop="projectForm" min-width="140" /> <el-table-column label="项目表单" align="center" prop="projectFormName" min-width="140" />
<el-table-column label="开始日期" align="center" prop="startDate" :formatter="dateFormatter2" width="120" /> <el-table-column label="开始日期" align="center" prop="startDate" :formatter="dateFormatter2" width="120" />
<el-table-column label="结束日期" align="center" prop="endDate" :formatter="dateFormatter2" width="120" /> <el-table-column label="结束日期" align="center" prop="endDate" :formatter="dateFormatter2" width="120" />
<el-table-column label="cron 表达式" align="center" prop="cronExpression" min-width="180" /> <el-table-column label="cron 表达式" align="center" prop="cronExpression" min-width="180" />
@ -123,7 +126,8 @@
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime' import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
import download from '@/utils/download' import download from '@/utils/download'
import { TaskManagementApi, TaskManagementVO } from '@/api/mes/taskManagement' import { TaskManagementApi, TaskManagementVO } from '@/api/mold/taskManagement'
import { PlanMaintenanceApi } from '@/api/mold/planmaintenance'
import TaskConfigurationForm from './TaskConfigurationForm.vue' import TaskConfigurationForm from './TaskConfigurationForm.vue'
defineOptions({ name: 'MoldTaskConfiguration' }) defineOptions({ name: 'MoldTaskConfiguration' })
@ -142,19 +146,52 @@ const total = ref(0)
const exportLoading = ref(false) const exportLoading = ref(false)
const selectedIds = ref<number[]>([]) const selectedIds = ref<number[]>([])
type PlanOption = {
id: number | string
planName: string
}
const planOptions = ref<PlanOption[]>([])
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
name: undefined as string | undefined, name: undefined as string | undefined,
taskType: undefined as number | undefined, taskType: undefined as number | undefined,
projectForm: undefined as string | undefined projectForm: [] as string[]
}) })
const queryFormRef = ref() const queryFormRef = ref()
const parseIdsValue = (value: any): string[] => {
if (!value) return []
if (Array.isArray(value)) return value.map((v) => String(v).trim()).filter(Boolean)
return String(value)
.split(',')
.map((v) => v.trim())
.filter(Boolean)
}
const toCommaSeparatedIds = (value: any): string | undefined => {
const ids = parseIdsValue(value)
return ids.length ? ids.join(',') : undefined
}
const ensurePlanOptionsLoaded = async () => {
const planRes = await PlanMaintenanceApi.getPlanMaintenancePage({ pageNo: 1, pageSize: 100 })
planOptions.value = (planRes?.list ?? []) as PlanOption[]
}
const buildQueryParams = () => {
return {
...queryParams,
projectForm: toCommaSeparatedIds(queryParams.projectForm)
}
}
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
try { try {
const data = await TaskManagementApi.getTaskManagementPage(queryParams) const data = await TaskManagementApi.getTaskManagementPage(buildQueryParams())
list.value = data.list list.value = data.list
total.value = data.total total.value = data.total
} finally { } finally {
@ -211,7 +248,7 @@ const handleExport = async () => {
try { try {
await message.exportConfirm() await message.exportConfirm()
exportLoading.value = true exportLoading.value = true
const params: Record<string, any> = { ...queryParams } const params: Record<string, any> = buildQueryParams()
if (selectedIds.value.length > 0) { if (selectedIds.value.length > 0) {
params.ids = selectedIds.value.join(',') params.ids = selectedIds.value.join(',')
} }
@ -223,8 +260,11 @@ const handleExport = async () => {
} }
} }
onMounted(() => { onMounted(async () => {
getList() try {
await ensurePlanOptionsLoaded()
} finally {
getList()
}
}) })
</script> </script>

@ -0,0 +1,173 @@
<template>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="1100px">
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" :row-key="getRowKey">
<el-table-column type="index" label="序号" align="center" width="70" />
<el-table-column label="检验项名称" align="center" prop="inspectionItemName" min-width="180" />
<el-table-column label="检验方式" align="center" prop="inspectionMethod" min-width="140">
<template #default="scope">
<dict-tag type="Inspection_method" :value="scope.row.inspectionMethod" />
</template>
</el-table-column>
<el-table-column label="判定基准" align="center" prop="judgmentCriteria" min-width="160" />
<el-table-column label="检验结果" align="center" prop="inspectionResult" width="120">
<template #default="scope">
<el-tag v-if="String(scope.row.inspectionResult) === '0'" type="info"></el-tag>
<el-tag v-else-if="String(scope.row.inspectionResult) === '1'" type="success">通过</el-tag>
<el-tag v-else-if="String(scope.row.inspectionResult) === '2'" type="danger">不通过</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="图片" align="center" prop="images" width="160">
<template #default="scope">
<UploadImg
v-if="String(scope.row.inspectionResult) === '0'" v-model="imageMap[String(scope.row.id)]"
:drag="false" :show-btn-text="false" width="64px" height="64px" />
<el-image
v-else-if="scope.row.images" :src="parseFirstImage(scope.row.images)"
:preview-src-list="parseImages(scope.row.images)" preview-teleported fit="cover"
style="width: 64px; height: 64px" />
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" min-width="160" />
<el-table-column label="操作" align="center" width="200" fixed="right">
<template #default="scope">
<el-radio-group v-if="String(scope.row.inspectionResult) === '0'" v-model="decisionMap[String(scope.row.id)]">
<el-radio :label="'1'">通过</el-radio>
<el-radio :label="'2'">不通过</el-radio>
</el-radio-group>
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
<el-pagination
v-show="total > 0" v-model:current-page="queryParams.pageNo" v-model:page-size="queryParams.pageSize"
:background="true" :page-sizes="[10, 20, 30, 50, 100]" :pager-count="7" :total="total"
class="mt-15px mb-15px flex justify-end" layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleSave" :loading="submitLoading"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { TicketManagementApi, TicketResultVO } from '@/api/mold/ticketManagement'
defineOptions({ name: 'TicketResultDialog' })
const message = useMessage()
const dialogVisible = ref(false)
const dialogTitle = ref('检验结果')
const loading = ref(false)
const submitLoading = ref(false)
const list = ref<TicketResultVO[]>([])
const total = ref(0)
const managementId = ref<number | undefined>(undefined)
const decisionMap = reactive<Record<string, '1' | '2' | undefined>>({})
const imageMap = reactive<Record<string, string>>({})
const queryParams = reactive({
pageNo: 1,
pageSize: 10
})
const open = async (options: { managementId: number; title?: string }) => {
dialogVisible.value = true
dialogTitle.value = options.title || '检验结果'
managementId.value = options.managementId
for (const key of Object.keys(decisionMap)) delete decisionMap[key]
for (const key of Object.keys(imageMap)) delete imageMap[key]
queryParams.pageNo = 1
await getList()
}
defineExpose({ open })
const getList = async () => {
if (!managementId.value) return
loading.value = true
try {
const data = await TicketManagementApi.getTicketResultsPage({
pageNo: queryParams.pageNo,
pageSize: queryParams.pageSize,
managementId: managementId.value
})
list.value = data.list
total.value = data.total
for (const row of list.value) {
const id = row?.id
if (!id) continue
if (row.images && !imageMap[String(id)]) {
imageMap[String(id)] = parseFirstImage(row.images)
}
}
} finally {
loading.value = false
}
}
const handleSizeChange = () => {
queryParams.pageNo = 1
getList()
}
const handleCurrentChange = () => {
getList()
}
const handleSave = async () => {
const payload: TicketResultVO[] = []
for (const row of list.value) {
if (!row?.id) continue
if (String(row.inspectionResult) !== '0') continue
const decision = decisionMap[String(row.id)]
if (!decision) continue
const img = imageMap[String(row.id)]
payload.push({ ...(row as any), inspectionResult: decision, images: img || row.images })
}
if (!payload.length) {
message.error('请先为待检测记录选择通过或不通过')
return
}
submitLoading.value = true
try {
await TicketManagementApi.batchUpdateTicketResults(payload)
message.success('更新成功')
dialogVisible.value = false
} catch {
message.error('更新失败')
} finally {
submitLoading.value = false
}
}
const getRowKey = (row: TicketResultVO) => {
return String(row.id ?? '')
}
const parseImages = (value: any): string[] => {
if (!value) return []
if (Array.isArray(value)) return value.map(String).filter(Boolean)
return String(value)
.split(',')
.map((v) => v.trim())
.filter(Boolean)
}
const parseFirstImage = (value: any): string => {
return parseImages(value)[0] || ''
}
</script>
<style scoped lang="scss">
:deep(.el-upload) {
margin: 0 auto;
}
</style>

@ -54,7 +54,7 @@
<el-table-column type="selection" width="55" /> <el-table-column type="selection" width="55" />
<el-table-column type="index" label="序号" align="center" width="70" /> <el-table-column type="index" label="序号" align="center" width="70" />
<el-table-column label="单号" align="center" prop="planNo" min-width="160" /> <el-table-column label="单号" align="center" prop="planNo" min-width="160" />
<el-table-column label="设备名称" align="center" prop="deviceName" min-width="160" /> <el-table-column label="模具名称" align="center" prop="moldName" min-width="160" />
<el-table-column label="类型" align="center" prop="planType" width="90"> <el-table-column label="类型" align="center" prop="planType" width="90">
<template #default="scope"> <template #default="scope">
<el-tag v-if="String(scope.row.planType) === '1'" type="primary"></el-tag> <el-tag v-if="String(scope.row.planType) === '1'" type="primary"></el-tag>
@ -78,7 +78,7 @@
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" min-width="160" /> <!-- <el-table-column label="备注" align="center" prop="remark" min-width="160" /> -->
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180" /> <el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180" />
</el-table> </el-table>
@ -92,8 +92,8 @@
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import { getStrDictOptions } from '@/utils/dict' import { getStrDictOptions } from '@/utils/dict'
import { useDictStoreWithOut } from '@/store/modules/dict' import { useDictStoreWithOut } from '@/store/modules/dict'
import { TicketManagementApi, TicketManagementVO } from '@/api/mes/ticketManagement' import { TicketManagementApi, TicketManagementVO } from '@/api/mold/ticketManagement'
import TicketResultDialog from '@/views/mes/workOrderManagement/components/TicketResultDialog.vue' import TicketResultDialog from '@/views/mold/workOrderInquiry/components/TicketResultDialog.vue'
defineOptions({ name: 'MoldWorkOrderInquiry' }) defineOptions({ name: 'MoldWorkOrderInquiry' })

Loading…
Cancel
Save