feat:设备管理-任务计划-新增/编辑-设备列表/项目表单改为弹框展示

main
黄伟杰 20 hours ago
parent 2d678580cb
commit 3647f12c81

@ -2140,7 +2140,7 @@ export default {
deviceList: 'Device List',
moldList: 'Mold List',
dateRange: 'Date Range',
operableUsers: 'Operable Users',
operableUsers: 'Responsible Person',
createWorkOrder: 'Create Work Order',
placeholderName: 'Please input name',
placeholderTaskType: 'Please select type',
@ -2150,9 +2150,12 @@ export default {
placeholderStartDate: 'Please select start date',
placeholderEndDate: 'Please select end date',
placeholderDateRange: 'Please select date range',
placeholderOperableUsers: 'Please select operable users',
placeholderOperableUsers: 'Please select responsible person',
placeholderCronExpression: 'Please input cron expression',
placeholderEnabled: 'Please select enabled status',
selectDeviceDialogTitle: 'Select Devices',
selectPlanDialogTitle: 'Select Project Form',
selectedDeviceCount: '{count} devices selected',
selectCronBeforeEnable: 'Please fill in cron expression before enabling task',
cronAlertTitle: 'Tip',
cronAlertConfirm: 'Got it',

@ -2135,7 +2135,7 @@ export default {
deviceList: '设备列表',
moldList: '模具列表',
dateRange: '起止日期',
operableUsers: '可操作人',
operableUsers: '责任人',
createWorkOrder: '新增工单',
placeholderName: '请输入名称',
placeholderTaskType: '请选择类型',
@ -2145,9 +2145,12 @@ export default {
placeholderStartDate: '请选择开始日期',
placeholderEndDate: '请选择结束日期',
placeholderDateRange: '请选择起止日期',
placeholderOperableUsers: '请选择可操作人',
placeholderOperableUsers: '请选择责任人',
placeholderCronExpression: '请输入 cron 表达式',
placeholderEnabled: '请选择是否启用',
selectDeviceDialogTitle: '选择设备',
selectPlanDialogTitle: '选择项目表单',
selectedDeviceCount: '已选择 {count} 台设备',
selectCronBeforeEnable: '请先填写 cron 表达式,再启用任务',
cronAlertTitle: '提示',
cronAlertConfirm: '我知道了',

@ -20,27 +20,38 @@
</el-radio-group>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.deviceList')" prop="deviceList">
<el-select
v-model="formData.deviceList"
multiple
filterable
<el-input
:model-value="selectedDeviceText"
readonly
clearable
:placeholder="t('EquipmentManagement.TaskManagement.placeholderDeviceList')"
class="!w-full"
@clear="clearDeviceSelection"
>
<el-option v-for="item in deviceOptions" :key="String(item.id)" :label="item.deviceName" :value="String(item.id)" />
</el-select>
<template #append>
<el-button @click="openDeviceDialog">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.select') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.projectForm')" prop="projectForm">
<el-select
v-model="formData.projectForm"
filterable
<el-input
:model-value="selectedPlanText"
readonly
clearable
:placeholder="t('EquipmentManagement.TaskManagement.placeholderProjectForm')"
class="!w-full"
@clear="clearPlanSelection"
>
<el-option v-for="item in planOptions" :key="String(item.id)" :label="item.planName" :value="Number(item.id)" />
</el-select>
<template #append>
<el-button @click="openPlanDialog">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.select') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.dateRange')" prop="dateRange">
<el-date-picker
@ -79,13 +90,200 @@
<el-button @click="dialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
<Dialog
:title="t('EquipmentManagement.TaskManagement.selectDeviceDialogTitle')"
v-model="deviceDialogVisible"
:appendToBody="true"
width="1080px"
>
<el-form
ref="deviceDialogQueryFormRef"
:model="deviceDialogQueryParams"
:inline="true"
class="-mb-15px"
>
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceCode')" prop="deviceCode">
<el-input
v-model="deviceDialogQueryParams.deviceCode"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderDeviceCode')"
clearable
class="!w-220px"
@keyup.enter="handleDeviceDialogQuery"
/>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceName')" prop="deviceName">
<el-input
v-model="deviceDialogQueryParams.deviceName"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderDeviceName')"
clearable
class="!w-220px"
@keyup.enter="handleDeviceDialogQuery"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleDeviceDialogQuery">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</el-button>
<el-button @click="resetDeviceDialogQuery">
<Icon icon="ep:refresh" class="mr-5px" />
{{ t('common.reset') }}
</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="deviceDialogLoading"
:data="deviceDialogList"
:stripe="true"
:show-overflow-tooltip="true"
row-key="id"
max-height="420"
@row-click="toggleDeviceDialogRow"
>
<el-table-column width="55" align="center">
<template #header>
<el-checkbox
:model-value="isDeviceCurrentPageAllSelected"
:indeterminate="isDeviceCurrentPageIndeterminate"
@change="toggleDeviceCurrentPageSelection"
/>
</template>
<template #default="{ row }">
<el-checkbox
:model-value="isDeviceDialogSelected(row)"
@click.stop
@change="(checked) => toggleDeviceDialogSelection(row, checked)"
/>
</template>
</el-table-column>
<el-table-column :label="t('EquipmentManagement.EquipmentLedger.deviceCode')" prop="deviceCode" min-width="150" />
<el-table-column :label="t('EquipmentManagement.EquipmentLedger.deviceName')" prop="deviceName" min-width="150" />
<el-table-column :label="t('EquipmentManagement.EquipmentLedger.deviceType')" prop="deviceTypeName" min-width="120">
<template #default="{ row }">
{{ row.deviceTypeName ?? row.deviceType ?? '-' }}
</template>
</el-table-column>
<el-table-column :label="t('EquipmentManagement.EquipmentLedger.deviceStatus')" prop="deviceStatus" width="100" />
<el-table-column :label="t('EquipmentManagement.EquipmentLedger.deviceBrand')" prop="deviceBrand" min-width="120" />
<el-table-column :label="t('EquipmentManagement.EquipmentLedger.sn')" prop="sn" min-width="140" />
</el-table>
<div class="task-selector-pagination flex items-center justify-between mt-15px">
<div class="task-selector-selected">
{{ t('EquipmentManagement.TaskManagement.selectedDeviceCount', { count: deviceDialogSelectedIds.length }) }}
</div>
<Pagination
:total="deviceDialogTotal"
v-model:page="deviceDialogQueryParams.pageNo"
v-model:limit="deviceDialogQueryParams.pageSize"
@pagination="getDeviceDialogList"
/>
</div>
<template #footer>
<el-button type="primary" @click="confirmDeviceSelection">{{ t('common.ok') }}</el-button>
<el-button @click="deviceDialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
<Dialog
:title="t('EquipmentManagement.TaskManagement.selectPlanDialogTitle')"
v-model="planDialogVisible"
:appendToBody="true"
width="960px"
>
<el-form
ref="planDialogQueryFormRef"
:model="planDialogQueryParams"
:inline="true"
class="-mb-15px"
>
<el-form-item :label="t('EquipmentManagement.PlanMaintenance.planName')" prop="planName">
<el-input
v-model="planDialogQueryParams.planName"
:placeholder="t('EquipmentManagement.PlanMaintenance.placeholderPlanName')"
clearable
class="!w-220px"
@keyup.enter="handlePlanDialogQuery"
/>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.PlanMaintenance.planType')" prop="planType">
<el-select
v-model="planDialogQueryParams.planType"
:placeholder="t('EquipmentManagement.PlanMaintenance.placeholderPlanType')"
clearable
class="!w-220px"
>
<el-option :value="1" :label="t('EquipmentManagement.PlanMaintenance.planTypeMaintain')" />
<el-option :value="2" :label="t('EquipmentManagement.PlanMaintenance.planTypeInspect')" />
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handlePlanDialogQuery">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</el-button>
<el-button @click="resetPlanDialogQuery">
<Icon icon="ep:refresh" class="mr-5px" />
{{ t('common.reset') }}
</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="planDialogLoading"
:data="planDialogList"
:stripe="true"
:show-overflow-tooltip="true"
row-key="id"
max-height="420"
@row-click="handlePlanDialogRowClick"
@row-dblclick="confirmPlanSelection"
>
<el-table-column width="55" align="center">
<template #default="{ row }">
<el-radio
v-model="planDialogSelectedId"
:label="String(row.id)"
@click.stop
@change="handlePlanDialogRadioChange(row)"
>
&nbsp;
</el-radio>
</template>
</el-table-column>
<el-table-column :label="t('EquipmentManagement.PlanMaintenance.planName')" prop="planName" min-width="180" />
<el-table-column :label="t('EquipmentManagement.PlanMaintenance.planType')" prop="planType" width="120">
<template #default="{ row }">
{{ getPlanTypeLabel(row.planType) }}
</template>
</el-table-column>
<el-table-column :label="t('EquipmentManagement.PlanMaintenance.description')" prop="description" min-width="220" />
<el-table-column :label="t('EquipmentManagement.PlanMaintenance.creatorName')" prop="creatorName" width="140" />
</el-table>
<div class="flex justify-end mt-15px">
<Pagination
:total="planDialogTotal"
v-model:page="planDialogQueryParams.pageNo"
v-model:limit="planDialogQueryParams.pageSize"
@pagination="getPlanDialogList"
/>
</div>
<template #footer>
<el-button type="primary" @click="confirmPlanSelection">{{ t('common.ok') }}</el-button>
<el-button @click="planDialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
import { TaskManagementApi, TaskManagementVO } from '@/api/mes/taskManagement'
import { DeviceLedgerApi } from '@/api/mes/deviceledger'
import { PlanMaintenanceApi } from '@/api/mes/planmaintenance'
import { PlanMaintenanceApi, PlanMaintenanceVO } from '@/api/mes/planmaintenance'
import { getSimpleUserList, UserVO } from '@/api/system/user'
defineOptions({ name: 'TaskManagementForm' })
@ -102,9 +300,15 @@ const formRef = ref()
type DeviceOption = {
id: number | string
deviceName: string
deviceCode?: string
deviceType?: string | number
deviceTypeName?: string
deviceStatus?: number | string
deviceBrand?: string
sn?: string
}
type PlanOption = {
type PlanOption = PlanMaintenanceVO & {
id: number | string
planName: string
}
@ -112,6 +316,35 @@ type PlanOption = {
const deviceOptions = ref<DeviceOption[]>([])
const planOptions = ref<PlanOption[]>([])
const users = ref<UserVO[]>([])
const deviceSelectionCache = ref<Record<string, DeviceOption>>({})
const planSelectionCache = ref<Record<string, PlanOption>>({})
const deviceDialogVisible = ref(false)
const deviceDialogLoading = ref(false)
const deviceDialogList = ref<DeviceOption[]>([])
const deviceDialogTotal = ref(0)
const deviceDialogSelectedIds = ref<string[]>([])
const deviceDialogQueryFormRef = ref()
const deviceDialogQueryParams = reactive({
pageNo: 1,
pageSize: 10,
deviceCode: undefined as string | undefined,
deviceName: undefined as string | undefined
})
const planDialogVisible = ref(false)
const planDialogLoading = ref(false)
const planDialogList = ref<PlanOption[]>([])
const planDialogTotal = ref(0)
const planDialogSelectedId = ref('')
const planDialogSelectedRow = ref<PlanOption>()
const planDialogQueryFormRef = ref()
const planDialogQueryParams = reactive({
pageNo: 1,
pageSize: 10,
planName: undefined as string | undefined,
planType: undefined as number | undefined
})
const parseIdsValue = (value: any): string[] => {
if (!value) return []
@ -127,6 +360,44 @@ const toCommaSeparatedIds = (value: any): string | undefined => {
return ids.length ? ids.join(',') : undefined
}
const normalizePageData = <T = any>(res: any): { list: T[]; total: number } => {
const list = Array.isArray(res) ? res : res?.list ?? res?.data?.list ?? []
const total = Array.isArray(res) ? res.length : res?.total ?? res?.data?.total ?? list.length
return { list: (Array.isArray(list) ? list : []) as T[], total: Number(total || 0) }
}
const cacheDeviceRows = (rows: DeviceOption[]) => {
const next = { ...deviceSelectionCache.value }
rows.forEach((row) => {
if (row?.id !== undefined && row?.id !== null) {
next[String(row.id)] = row
}
})
deviceSelectionCache.value = next
}
const cachePlanRows = (rows: PlanOption[]) => {
const next = { ...planSelectionCache.value }
rows.forEach((row) => {
if (row?.id !== undefined && row?.id !== null) {
next[String(row.id)] = row
}
})
planSelectionCache.value = next
}
const getDeviceLabel = (id: string) => {
const row = deviceSelectionCache.value[id] || deviceOptions.value.find((item) => String(item.id) === id)
return row?.deviceName || row?.deviceCode || id
}
const getPlanLabel = (id?: number | string) => {
if (id === undefined || id === null) return ''
const key = String(id)
const row = planSelectionCache.value[key] || planOptions.value.find((item) => String(item.id) === key)
return row?.planName || key
}
const ensureOptionsLoaded = async () => {
const [deviceRes, planRes, userRes] = await Promise.all([
DeviceLedgerApi.getDeviceLedgerList({}),
@ -135,6 +406,8 @@ const ensureOptionsLoaded = async () => {
])
deviceOptions.value = (Array.isArray(deviceRes) ? deviceRes : deviceRes?.list ?? deviceRes?.data ?? []) as DeviceOption[]
planOptions.value = (planRes?.list ?? []) as PlanOption[]
cacheDeviceRows(deviceOptions.value)
cachePlanRows(planOptions.value)
users.value = userRes ?? []
}
@ -150,6 +423,25 @@ const formData = ref({
enabled: false as boolean
})
const selectedDeviceText = computed(() => {
const ids = parseIdsValue(formData.value.deviceList)
return ids.map((id) => getDeviceLabel(id)).join(', ')
})
const selectedPlanText = computed(() => getPlanLabel(formData.value.projectForm))
const isDeviceCurrentPageAllSelected = computed(() => {
if (!deviceDialogList.value.length) return false
return deviceDialogList.value.every((row) => deviceDialogSelectedIds.value.includes(String(row.id)))
})
const isDeviceCurrentPageIndeterminate = computed(() => {
const selectedCount = deviceDialogList.value.filter((row) =>
deviceDialogSelectedIds.value.includes(String(row.id))
).length
return selectedCount > 0 && selectedCount < deviceDialogList.value.length
})
const formRules = reactive({
name: [
{ required: true, message: t('EquipmentManagement.TaskManagement.placeholderName'), trigger: 'blur' }
@ -202,6 +494,14 @@ const open = async (type: string, row?: TaskManagementVO) => {
if (projectFormIds.length) {
const n = Number(projectFormIds[0])
formData.value.projectForm = Number.isFinite(n) ? n : undefined
const projectFormName = String((row as any).projectFormName ?? '').trim()
if (formData.value.projectForm !== undefined && projectFormName) {
planSelectionCache.value[String(formData.value.projectForm)] = {
id: formData.value.projectForm,
planName: projectFormName,
planType: ''
}
}
} else {
const projectFormNames = parseIdsValue((row as any).projectFormName)
const mapped = projectFormNames
@ -228,6 +528,163 @@ defineExpose({ open })
const emit = defineEmits(['success'])
const getDeviceDialogList = async () => {
deviceDialogLoading.value = true
try {
const data = await DeviceLedgerApi.getDeviceLedgerPage(deviceDialogQueryParams)
const normalized = normalizePageData<DeviceOption>(data)
deviceDialogList.value = normalized.list
deviceDialogTotal.value = normalized.total
cacheDeviceRows(normalized.list)
} finally {
deviceDialogLoading.value = false
}
}
const openDeviceDialog = async () => {
deviceDialogVisible.value = true
deviceDialogSelectedIds.value = parseIdsValue(formData.value.deviceList)
deviceDialogQueryParams.pageNo = 1
deviceDialogQueryParams.deviceCode = undefined
deviceDialogQueryParams.deviceName = undefined
await getDeviceDialogList()
}
const handleDeviceDialogQuery = () => {
deviceDialogQueryParams.pageNo = 1
getDeviceDialogList()
}
const resetDeviceDialogQuery = () => {
deviceDialogQueryFormRef.value?.resetFields?.()
handleDeviceDialogQuery()
}
const isDeviceDialogSelected = (row: DeviceOption) => {
return deviceDialogSelectedIds.value.includes(String(row.id))
}
const toggleDeviceDialogSelection = (row: DeviceOption, checked: boolean) => {
const id = String(row.id)
const next = new Set(deviceDialogSelectedIds.value)
checked ? next.add(id) : next.delete(id)
deviceDialogSelectedIds.value = Array.from(next)
cacheDeviceRows([row])
}
const toggleDeviceDialogRow = (row: DeviceOption) => {
toggleDeviceDialogSelection(row, !isDeviceDialogSelected(row))
}
const toggleDeviceCurrentPageSelection = (checked: boolean) => {
const next = new Set(deviceDialogSelectedIds.value)
deviceDialogList.value.forEach((row) => {
const id = String(row.id)
checked ? next.add(id) : next.delete(id)
})
deviceDialogSelectedIds.value = Array.from(next)
cacheDeviceRows(deviceDialogList.value)
}
const confirmDeviceSelection = () => {
if (!deviceDialogSelectedIds.value.length) {
message.warning(t('EquipmentManagement.TaskManagement.placeholderDeviceList'))
return
}
formData.value.deviceList = [...deviceDialogSelectedIds.value]
deviceDialogVisible.value = false
formRef.value?.clearValidate?.('deviceList')
}
const clearDeviceSelection = () => {
formData.value.deviceList = []
deviceDialogSelectedIds.value = []
formRef.value?.clearValidate?.('deviceList')
}
const getPlanDialogList = async () => {
planDialogLoading.value = true
try {
const data = await PlanMaintenanceApi.getPlanMaintenancePage(planDialogQueryParams)
const normalized = normalizePageData<PlanOption>(data)
planDialogList.value = normalized.list
planDialogTotal.value = normalized.total
cachePlanRows(normalized.list)
} finally {
planDialogLoading.value = false
}
}
const openPlanDialog = async () => {
planDialogVisible.value = true
planDialogSelectedId.value =
formData.value.projectForm === undefined || formData.value.projectForm === null
? ''
: String(formData.value.projectForm)
planDialogSelectedRow.value = planDialogSelectedId.value
? planSelectionCache.value[planDialogSelectedId.value]
: undefined
planDialogQueryParams.pageNo = 1
planDialogQueryParams.planName = undefined
planDialogQueryParams.planType = undefined
await getPlanDialogList()
}
const handlePlanDialogQuery = () => {
planDialogQueryParams.pageNo = 1
getPlanDialogList()
}
const resetPlanDialogQuery = () => {
planDialogQueryFormRef.value?.resetFields?.()
handlePlanDialogQuery()
}
const handlePlanDialogRowClick = (row: PlanOption) => {
planDialogSelectedId.value = String(row.id)
planDialogSelectedRow.value = row
cachePlanRows([row])
}
const handlePlanDialogRadioChange = (row: PlanOption) => {
planDialogSelectedRow.value = row
cachePlanRows([row])
}
const confirmPlanSelection = (row?: PlanOption) => {
const selected =
row?.id !== undefined && row?.id !== null
? row
: planDialogSelectedRow.value ||
planDialogList.value.find((item) => String(item.id) === planDialogSelectedId.value) ||
planSelectionCache.value[planDialogSelectedId.value]
if (!selected?.id) {
message.warning(t('EquipmentManagement.TaskManagement.placeholderProjectForm'))
return
}
const id = Number(selected.id)
formData.value.projectForm = Number.isFinite(id) ? id : undefined
cachePlanRows([selected])
planDialogVisible.value = false
formRef.value?.clearValidate?.('projectForm')
}
const clearPlanSelection = () => {
formData.value.projectForm = undefined
planDialogSelectedId.value = ''
planDialogSelectedRow.value = undefined
formRef.value?.clearValidate?.('projectForm')
}
const getPlanTypeLabel = (value: any) => {
const normalized = Number(value)
if (normalized === 1) return t('EquipmentManagement.PlanMaintenance.planTypeMaintain')
if (normalized === 2) return t('EquipmentManagement.PlanMaintenance.planTypeInspect')
return value ?? '-'
}
const submitForm = async () => {
await formRef.value.validate()
formLoading.value = true
@ -260,3 +717,16 @@ const submitForm = async () => {
}
}
</script>
<style lang="scss" scoped>
.task-selector-selected {
flex: 0 0 auto;
color: var(--el-text-color-secondary);
font-size: 13px;
}
.task-selector-pagination :deep(.pagination-container) {
margin: 0;
padding: 0;
}
</style>

Loading…
Cancel
Save