You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
besure_web/src/views/mes/deviceledger/DeviceLedgerForm.vue

1111 lines
42 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="920px">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="110px" v-loading="formLoading">
<el-row :gutter="16">
<!-- <el-col :span="24">
<el-form-item label="资产编号" prop="id">
<el-input v-model="formData.id" placeholder="系统自动生成" disabled />
</el-form-item>
</el-col> -->
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.images')" prop="images">
<UploadImg style="height: 100px" v-model="formData.images" />
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceCode')" prop="deviceCode">
<el-row :gutter="20" style="width: 100%">
<el-col :xs="18" :sm="18" :md="16" :lg="14" :xl="10">
<el-input v-model="formData.deviceCode"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderDeviceCode')"
:disabled="Boolean(formData.isCode) || formType === 'update'" />
</el-col>
<el-col :xs="24" :sm="6" :md="4" :lg="3" :xl="2">
<div>
<el-switch v-model="formData.isCode" :disabled="formType === 'update'"
@change="handleCodeAutoChange" />
</div>
</el-col>
</el-row>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceName')" prop="deviceName" required>
<el-input v-model="formData.deviceName"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderDeviceName')" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceType')" prop="deviceType" required>
<el-tree-select v-model="formData.deviceType" :data="deviceTypeTree" :props="treeSelectProps"
check-strictly default-expand-all value-key="id"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderDeviceType')" class="!w-full" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceSpec')" prop="deviceSpec">
<el-input v-model="formData.deviceSpec"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderDeviceSpec')" />
</el-form-item>
</el-col>
<el-col v-if="isScheduledEnabled" :span="12">
<el-form-item
:label="t('EquipmentManagement.EquipmentLedger.ratedCapacity')"
prop="ratedCapacity"
:required="isScheduledEnabled"
>
<el-input-number v-model="formData.ratedCapacity" :min="0" :precision="0" controls-position="right"
class="!w-full" :placeholder="t('EquipmentManagement.EquipmentLedger.placeholderRatedCapacity')" />
</el-form-item>
</el-col>
</el-row>
</el-col>
<!-- <el-col :span="12">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceBrand')" prop="deviceBrand">
<el-input v-model="formData.deviceBrand" placeholder="请输入品牌" />
</el-form-item>
</el-col> -->
<!-- <el-col :span="24">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceModel')" prop="deviceModel">
<el-input v-model="formData.deviceModel" :placeholder="t('EquipmentManagement.EquipmentLedger.placeholderDeviceModel')" />
</el-form-item>
</el-col>-->
<!-- <el-col :span="12">
<el-form-item label="供应商" prop="supplier">
<el-input v-model="formData.supplier" placeholder="请输入供应商" /> -->
<!--
<el-select v-model="formData.supplier" placeholder="请选择供应商" clearable filterable class="!w-full">
<el-option v-for="item in supplierOptions" :key="item" :label="item" :value="item" />
</el-select>
-->
<!-- </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属车间" prop="workshop">
<el-input v-model="formData.workshop" placeholder="请输入所属车间" /> -->
<!--
<el-tree-select
v-model="formData.workshop"
:data="deptTree"
:props="treeSelectProps"
check-strictly
default-expand-all
value-key="id"
placeholder="请选择所属车间"
class="!w-full"
/>
-->
<!-- </el-form-item>
</el-col> -->
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.isSchedueld')" prop="isScheduled">
<el-switch v-model="formData.isScheduled" :active-value="1" :inactive-value="0"
:active-text="t('EquipmentManagement.EquipmentLedger.yes')"
:inactive-text="t('EquipmentManagement.EquipmentLedger.no')" inline-prompt />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.productionDate')" prop="productionDate" required>
<el-date-picker v-model="formData.productionDate" type="date" value-format="YYYY-MM-DD"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderProductionDate')" class="!w-full" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.factoryEntryDate')" prop="factoryEntryDate"
required>
<el-date-picker v-model="formData.factoryEntryDate" type="date" value-format="YYYY-MM-DD"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderFactoryEntryDate')" class="!w-full" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceLocation')" prop="deviceLocation">
<el-input v-model="formData.deviceLocation"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderDeviceLocation')" />
</el-form-item>
</el-col>
<!-- <el-col :span="24">
<el-form-item label="所属系统组织" prop="systemOrg">
<el-input v-model="formData.systemOrg" placeholder="请输入所属系统组织" /> -->
<!--
<el-tree-select
v-model="formData.systemOrg"
:data="deptTree"
:props="treeSelectProps"
check-strictly
default-expand-all
value-key="id"
placeholder="请选择部门"
class="!w-full"
/>
-->
<!-- </el-form-item>
</el-col> -->
<el-col :span="12">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.deviceManagerName')" prop="deviceManagerIds">
<el-select v-model="formData.deviceManagerIds" multiple filterable clearable
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderDeviceManagerIds')" class="!w-full">
<el-option v-for="item in users" :key="item.id" :label="item.nickname" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<!-- <el-col :span="24">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.dvName')" prop="dvId">
<el-select v-model="formData.dvId" filterable :placeholder="t('EquipmentManagement.EquipmentLedger.dvId')">
<el-option
v-for="item in deviceList"
:key="item.id"
:label="item.deviceName"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col> -->
<el-col :span="24">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.criticalComponent')" prop="componentIds">
<el-input :model-value="criticalComponentDisplay" readonly clearable class="device-ledger-selection-input"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderComponentIds')"
@clear="clearCriticalComponent" @click="openCriticalComponentDialog" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.sparePart')" prop="beijianIds">
<el-input :model-value="beijianDisplay" readonly clearable class="device-ledger-selection-input"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderBeijianIds')" @clear="clearBeijian"
@click="openBeijianDialog" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.fileUrl')" prop="fileUrl">
<UploadFile :is-show-tip="false" v-model="formData.fileUrl" :limit="9" />
</el-form-item>
</el-col>
<el-col v-if="formType === 'update'" :span="24">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.qrcode')" prop="qrcodeUrl">
<QrcodeActionCard :image-url="formData.qrcodeUrl" :print-id="formData.id"
:print-title="`${formData.deviceName || '设备'}码打印预览`" :print-paper-width="80" :print-paper-height="80"
:print-max-width="220" :empty-text="t('EquipmentManagement.EquipmentLedger.qrcodeEmpty')"
:error-text="t('EquipmentManagement.EquipmentLedger.qrcodeLoadError')"
:refresh-url="getQrcodeRefreshUrl()" :refresh-disabled="!formData.id || !formData.deviceCode"
refresh-confirm-text="确认刷新该设备二维码吗?"
:template-json="formData.templateJson"
:print-data="buildPrintData()"
@refresh-success="handleQrcodeRefreshSuccess" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.remark')" prop="remark">
<el-input v-model="formData.remark"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderRemark')" type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('common.ok') }}</el-button>
<el-button @click="dialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
<el-dialog v-model="criticalComponentDialogVisible" :title="t('EquipmentManagement.EquipmentLedger.gjTitle')" width="1200px" class="device-ledger-transfer-dialog"
append-to-body>
<!-- <div class="device-ledger-transfer">
<el-transfer
v-model="criticalComponentDraft"
:data="criticalComponentTransferData"
filterable
:filter-placeholder="t('EquipmentManagement.EquipmentLedger.placeholderComponentIds')"
/>
</div>-->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" min-label-width="68px"
style="margin-bottom: 10px">
<el-form-item :label="t('EquipmentManagement.EquipmentKeyItems.code')" prop="code">
<el-input v-model="queryParams.code" :placeholder="t('EquipmentManagement.EquipmentKeyItems.placeholderCode')"
clearable @keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item :label="t('EquipmentManagement.EquipmentKeyItems.name')" prop="name">
<el-input v-model="queryParams.name" :placeholder="t('EquipmentManagement.EquipmentKeyItems.placeholderName')"
clearable @keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item :label="t('EquipmentManagement.EquipmentKeyItems.description')" prop="description">
<el-input v-model="queryParams.description"
:placeholder="t('EquipmentManagement.EquipmentKeyItems.placeholderDescription')" clearable
@keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" />
{{ t('common.reset') }}
</el-button>
</el-form-item>
</el-form>
<ContentWrap>
<el-table ref="multipleTableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" class="no-select-all"
@selection-change="handleSelectionChange" @select="handleSelect" @select-all="handleSelectAll" row-key="id">
<el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column :label="t('EquipmentManagement.EquipmentKeyItems.code')" align="center" prop="code"
min-width="140" sortable />
<el-table-column :label="t('EquipmentManagement.EquipmentKeyItems.name')" align="center" prop="name"
min-width="140" sortable />
<el-table-column :label="t('EquipmentManagement.EquipmentKeyItems.description')" align="center"
prop="description" min-width="180" />
<el-table-column :label="t('EquipmentManagement.EquipmentKeyItems.count')" align="center" prop="count"
min-width="180" />
<el-table-column :label="t('EquipmentManagement.EquipmentKeyItems.remark')" align="center" prop="remark"
min-width="180" />
<el-table-column :label="t('EquipmentManagement.EquipmentKeyItems.createTime')" align="center" prop="createTime"
:formatter="dateFormatter" width="180" sortable />
</el-table>
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</ContentWrap>
<template #footer>
<el-button @click="criticalComponentDialogVisible = false">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="confirmCriticalComponentDialog">{{ t('common.ok') }}</el-button>
</template>
</el-dialog>
<el-dialog v-model="beijianDialogVisible" :title="t('EquipmentManagement.EquipmentLedger.bjTitle')" width="860px" class="device-ledger-transfer-dialog"
append-to-body>
<!-- <div class="device-ledger-transfer">
<el-transfer
v-model="beijianDraft"
:data="beijianTransferData"
filterable
:filter-placeholder="t('EquipmentManagement.EquipmentLedger.placeholderBeijianIds')"
/>
</div>-->
<!-- 列表 -->
<el-form class="-mb-15px" :model="bjQueryParams" ref="bjQueryFormRef" :inline="true" min-label-width="68px"
style="margin-bottom: 10px">
<el-form-item :label="t('SparePartsManagement.SpareInfo.code')" prop="barCode">
<el-input v-model="bjQueryParams.barCode" :placeholder="t('SparePartsManagement.SpareInfo.placeholderCode')"
clearable @keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item :label="t('SparePartsManagement.SpareInfo.name')" prop="name">
<el-input v-model="bjQueryParams.name" :placeholder="t('SparePartsManagement.SpareInfo.placeholderName')"
clearable @keyup.enter="bjHandleQuery" class="!w-240px" />
</el-form-item>
<el-form-item>
<el-button @click="bjHandleQuery">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</el-button>
<el-button @click="bjResetQuery">
<Icon icon="ep:refresh" class="mr-5px" />
{{ t('common.reset') }}
</el-button>
</el-form-item>
</el-form>
<ContentWrap>
<el-table ref="bjMultipleTableRef" v-loading="loading" :data="bjList" :stripe="true" class="no-select-all"
@selection-change="bjHandleSelectionChange" @select="bjHandleSelect" @select-all="bjHandleSelectAll"
:show-overflow-tooltip="true" row-key="id">
<el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column :label="t('SparePartsManagement.SpareInfo.code')" align="center" prop="barCode" width="240px"
sortable />
<el-table-column :label="t('SparePartsManagement.SpareInfo.name')" align="left" prop="name" width="220px"
sortable />
<el-table-column :label="t('SparePartsManagement.SpareInfo.unit')" align="center" prop="unitName" sortable />
<el-table-column :label="t('SparePartsManagement.SpareInfo.status')" align="center" prop="status" sortable>
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column :label="t('SparePartsManagement.SpareInfo.createTime')" align="center" prop="createTime"
:formatter="dateFormatter" width="180px" sortable />
</el-table>
<!-- 分页 -->
<Pagination :total="bjTotal" v-model:page="bjQueryParams.pageNo" v-model:limit="bjQueryParams.pageSize"
@pagination="bjGetList" />
</ContentWrap>
<template #footer>
<el-button @click="beijianDialogVisible = false">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="confirmBeijianDialog">{{ t('common.ok') }}</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { DeviceLedgerApi, DeviceLedgerVO } from '@/api/mes/deviceledger'
import { CriticalComponentApi, CriticalComponentVO } from '@/api/mes/criticalComponent'
import { DeviceTypeApi, DeviceTypeTreeVO } from '@/api/mes/devicetype'
import { getSimpleUserList, UserVO } from '@/api/system/user'
import { formatDate } from '@/utils/formatTime'
import type { FormRules } from 'element-plus'
import { ProductApi, ProductVO } from '@/api/erp/product/product'
import { DeviceApi, DeviceVO } from '@/api/iot/device'
import QrcodeActionCard from '@/components/QrcodeActionCard/index.vue'
import { dateFormatter } from '@/utils/formatTime'
import { ref } from "vue";
import { ElTable } from "element-plus";
import { DICT_TYPE } from '@/utils/dict'
/** */
defineOptions({ name: 'DeviceLedgerForm' })
const queryFormRef = ref()
const bjQueryFormRef = ref()
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(true)
const total = ref(0)
const bjTotal = ref(0)
const selectedIds = ref<number[]>([])
const bjSelectedIds = ref<number[]>([])
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formType = ref('') // 表单的类型create - 新增update - 修改
const deviceList = ref<DeviceVO[]>([]) // 列表
const list = ref<CriticalComponentVO[]>([])
const bjList = ref<ProductVO[]>([])
const selectedRows = ref<any[]>([]) // 存储所有选中的行
const bjSelectedRows = ref<any[]>([]) // 存储所有选中的行
// 表格引用
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
const bjMultipleTableRef = ref<InstanceType<typeof ElTable>>()
const parseIdsValue = (value: any): number[] => {
if (!value) return []
if (Array.isArray(value)) return value.map((v) => Number(v)).filter((v) => !Number.isNaN(v))
return String(value)
.split(',')
.map((v) => Number(v.trim()))
.filter((v) => !Number.isNaN(v))
}
const normalizeNumberish = (value: any): number | undefined => {
if (value === null || value === undefined || value === '') return undefined
if (typeof value === 'number') return Number.isFinite(value) ? value : undefined
if (typeof value === 'string') {
const trimmed = value.trim()
if (!trimmed) return undefined
const n = Number(trimmed)
return Number.isFinite(n) ? n : undefined
}
return undefined
}
const normalizeYmd = (value: any): string | undefined => {
if (value === null || value === undefined || value === '') return undefined
if (typeof value === 'string') {
const trimmed = value.trim()
const matched = trimmed.match(/^(\d{4}-\d{2}-\d{2})/)
if (matched?.[1]) return matched[1]
const parsed = Date.parse(trimmed)
if (!Number.isNaN(parsed)) return formatDate(new Date(parsed), 'YYYY-MM-DD')
return trimmed
}
if (typeof value === 'number') return formatDate(new Date(value), 'YYYY-MM-DD')
if (value instanceof Date) return formatDate(value, 'YYYY-MM-DD')
return formatDate(new Date(value), 'YYYY-MM-DD')
}
const normalizeFileUrlAsJsonArrayString = (value: any): string | undefined => {
if (value === null || value === undefined || value === '') return undefined
if (typeof value === 'string') {
const trimmed = value.trim()
if (!trimmed) return undefined
try {
const parsed = JSON.parse(trimmed)
if (Array.isArray(parsed)) {
const normalized = parsed
.map((item) => {
if (!item) return undefined
if (typeof item === 'string') {
const url = item.trim()
if (!url) return undefined
const idx = url.lastIndexOf('/')
const name = idx !== -1 ? url.substring(idx + 1) : url
return { fileName: name, fileUrl: url }
}
if (typeof item === 'object' && (item as any).fileUrl) {
const url = String((item as any).fileUrl)
const name = (item as any).fileName ? String((item as any).fileName) : undefined
const idx = url.lastIndexOf('/')
return { fileName: name || (idx !== -1 ? url.substring(idx + 1) : url), fileUrl: url }
}
return undefined
})
.filter((v) => Boolean(v))
return normalized.length ? JSON.stringify(normalized) : undefined
}
if (parsed && typeof parsed === 'object') {
const url = (parsed as any).fileUrl ? String((parsed as any).fileUrl) : undefined
if (!url) return undefined
const name = (parsed as any).fileName ? String((parsed as any).fileName) : undefined
const idx = url.lastIndexOf('/')
return JSON.stringify([{ fileName: name || (idx !== -1 ? url.substring(idx + 1) : url), fileUrl: url }])
}
} catch { }
const urls = trimmed
.split(',')
.map((s) => s.trim())
.filter((s) => !!s)
if (!urls.length) return undefined
const infos = urls.map((url) => {
const idx = url.lastIndexOf('/')
const name = idx !== -1 ? url.substring(idx + 1) : url
return { fileName: name, fileUrl: url }
})
return JSON.stringify(infos)
}
if (Array.isArray(value)) return JSON.stringify(value)
if (typeof value === 'object' && (value as any).fileUrl) return JSON.stringify([value])
return JSON.stringify([{ fileName: '', fileUrl: String(value) }])
}
const initFormData = () => ({
id: undefined,
deviceCode: undefined,
isCode: true,
deviceName: undefined,
deviceStatus: undefined,
deviceBrand: undefined,
deviceModel: undefined,
deviceSpec: undefined,
isScheduled: 0,
ratedCapacity: undefined,
deviceType: undefined as number | undefined,
deviceLine: undefined as number | undefined,
supplier: undefined,
workshop: undefined,
deviceLocation: undefined,
systemOrg: undefined,
deviceManagerIds: [] as number[],
productionDate: undefined,
factoryEntryDate: undefined,
remark: undefined,
componentIds: [] as number[],
beijianIds: [] as number[],
fileUrl: undefined,
qrcodeUrl: undefined,
templateJson: undefined,
sort: undefined,
dvId: undefined
})
const formData = ref({
...initFormData()
})
const isScheduledEnabled = computed(() => Number(formData.value.isScheduled) === 1)
const validateDeviceCode = (_rule, value, callback) => {
if (Boolean(formData.value.isCode)) {
callback()
return
}
if (value === undefined || value === null || String(value).trim() === '') {
callback(new Error(t('EquipmentManagement.EquipmentLedger.validatorDeviceCodeRequired')))
return
}
callback()
}
const validateScheduledRequired = (label: string) => (_rule, value, callback) => {
if (!isScheduledEnabled.value) {
callback()
return
}
const normalized = normalizeNumberish(value)
if (normalized === undefined) {
callback(new Error(`请输入${label}`))
return
}
callback()
}
const formRules = reactive<FormRules>({
deviceCode: [{ validator: validateDeviceCode, trigger: ['blur', 'change'] }],
deviceName: [{ required: true, message: t('EquipmentManagement.EquipmentLedger.placeholderDeviceName'), trigger: 'blur' }],
deviceType: [{ required: true, message: t('EquipmentManagement.EquipmentLedger.placeholderDeviceType'), trigger: 'change' }],
ratedCapacity: [{ validator: validateScheduledRequired('额定产能'), trigger: ['blur', 'change'] }],
productionDate: [{ required: true, message: t('EquipmentManagement.EquipmentLedger.placeholderProductionDate'), trigger: 'change' }],
factoryEntryDate: [{ required: true, message: t('EquipmentManagement.EquipmentLedger.placeholderFactoryEntryDate'), trigger: 'change' }]
})
const formRef = ref() // 表单 Ref
watch(
() => formData.value.isScheduled,
() => {
formRef.value?.clearValidate?.(['ratedCapacity'])
}
)
const treeSelectProps = { label: 'name', children: 'children' }
const deviceTypeTree = ref<DeviceTypeTreeVO[]>([])
const users = ref<UserVO[]>([])
const criticalComponentOptions = ref<{ label: string; value: number }[]>([])
const beijianOptions = ref<{ label: string; value: number }[]>([])
const criticalComponentTransferData = computed(() =>
criticalComponentOptions.value.map((item) => ({ key: item.value, label: item.label }))
)
const beijianTransferData = computed(() =>
beijianOptions.value.map((item) => ({ key: item.value, label: item.label }))
)
const criticalComponentDialogVisible = ref(false)
const beijianDialogVisible = ref(false)
const criticalComponentDraft = ref<number[]>([])
const beijianDraft = ref<number[]>([])
const formatSelectedSummary = (ids: number[], options: { label: string; value: number }[]) => {
const optionMap = new Map<number, string>(options.map((item) => [item.value, item.label]))
const labels = ids.map((id) => optionMap.get(id)).filter((v): v is string => Boolean(v))
if (!labels.length) return ''
if (labels.length <= 3) return labels.join('、')
return `${labels.slice(0, 3).join('、')}…等${labels.length}`
}
const criticalComponentDisplay = computed(() =>
formatSelectedSummary(formData.value.componentIds ?? [], criticalComponentOptions.value)
)
const beijianDisplay = computed(() =>
formatSelectedSummary(formData.value.beijianIds ?? [], beijianOptions.value)
)
const clearCriticalComponent = () => {
formData.value.componentIds = []
}
const clearBeijian = () => {
formData.value.beijianIds = []
}
const openCriticalComponentDialog = () => {
criticalComponentDraft.value = [...(formData.value.componentIds ?? [])]
criticalComponentDialogVisible.value = true
ids.value = formData.value.componentIds
setDefaultSelections()
}
const openBeijianDialog = () => {
beijianDraft.value = [...(formData.value.beijianIds ?? [])]
beijianDialogVisible.value = true
bjIds.value = formData.value.beijianIds
setBJDefaultSelections()
}
const ids = ref([])
const confirmCriticalComponentDialog = () => {
//let ids = selectedRows.value.map(item => item.id);
//const validMap = new Set(criticalComponentOptions.value.map((item) => item.value))
//const selected = Array.from(new Set(criticalComponentDraft.value.map((v) => Number(v)).filter((v) => validMap.has(v))))
formData.value.componentIds = ids.value
criticalComponentDialogVisible.value = false
//multipleTableRef.value.clearSelection()
}
const bjIds = ref([])
const confirmBeijianDialog = () => {
// let ids = bjSelectedRows.value.map(item => item.id);
/* const validMap = new Set(beijianOptions.value.map((item) => item.value))
const selected = Array.from(new Set(beijianDraft.value.map((v) => Number(v)).filter((v) => validMap.has(v))))*/
formData.value.beijianIds = bjIds.value
beijianDialogVisible.value = false
//multipleTableRef.value.clearSelection()
}
const handleCodeAutoChange = (value: boolean) => {
if (value) {
formData.value.deviceCode = undefined
}
formRef.value?.clearValidate('deviceCode')
}
const getQrcodeRefreshUrl = () => {
if (!formData.value.id || !formData.value.deviceCode) return ''
return `/mes/device-ledger/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.deviceCode))}`
}
const buildPrintData = () => {
return {
id: formData.value.id,
deviceCode: formData.value.deviceCode,
deviceName: formData.value.deviceName,
deviceSpec: formData.value.deviceSpec,
deviceBrand: formData.value.deviceBrand,
deviceModel: formData.value.deviceModel,
deviceLocation: formData.value.deviceLocation,
remark: formData.value.remark,
qrcodeUrl: formData.value.qrcodeUrl
}
}
const handleQrcodeRefreshSuccess = async (data: any) => {
if (!formData.value.id) return
if (data?.qrcodeUrl) {
formData.value.qrcodeUrl = data.qrcodeUrl
return
}
const detail = await DeviceLedgerApi.getDeviceLedger(formData.value.id)
formData.value.qrcodeUrl = detail?.qrcodeUrl
formData.value.deviceCode = detail?.deviceCode ?? formData.value.deviceCode
}
const ensureOptionsLoaded = async () => {
const [deviceTypeRes, userRes, criticalRes, beijianRes] = await Promise.all([
DeviceTypeApi.getDeviceTypeTree({ pageNo: 1, pageSize: 10 }),
getSimpleUserList(),
CriticalComponentApi.getCriticalComponentList(),
ProductApi.getComponentSimpleList()
])
deviceTypeTree.value = deviceTypeRes
users.value = userRes ?? []
criticalComponentOptions.value = (criticalRes ?? []).map((item: any) => {
const code = item.code ? String(item.code) : ''
const name = item.name ? String(item.name) : ''
const label = code && name ? `${code}-${name}` : name || code || String(item.id)
return { label, value: Number(item.id) }
})
beijianOptions.value = (beijianRes ?? []).map((item: any) => {
const code = item.barCode ? String(item.barCode) : ''
const name = item.name ? String(item.name) : ''
const label = code && name ? `${code}-${name}` : name || code || String(item.id)
return { label, value: Number(item.id) }
})
}
/** 打开弹窗 */
const open = async (type: string, id?: number, defaultDeviceTypeId?: number, defaultDeviceLineId?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
await ensureOptionsLoaded()
if (type === 'create' && defaultDeviceTypeId) {
formData.value.deviceType = defaultDeviceTypeId
}
if (type === 'create' && defaultDeviceLineId) {
;(formData.value as any).deviceLine = defaultDeviceLineId
}
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
const detail = await DeviceLedgerApi.getDeviceLedger(id)
const templateJson = (detail as any)?.templateJson
const parsedTemplateJson = typeof templateJson === 'string'
? JSON.parse(templateJson)
: templateJson
formData.value = {
...initFormData(),
...(detail as any),
templateJson: parsedTemplateJson,
isCode: (detail as any)?.isCode ?? false,
isScheduled: normalizeNumberish((detail as any)?.isScheduled ?? (detail as any)?.isScheduled) ?? 0,
ratedCapacity: normalizeNumberish((detail as any)?.ratedCapacity),
deviceType: normalizeNumberish((detail as any)?.deviceType),
deviceLine: normalizeNumberish((detail as any)?.deviceLine),
deviceManagerIds: parseIdsValue((detail as any)?.deviceManager),
productionDate: normalizeYmd((detail as any)?.productionDate),
factoryEntryDate: normalizeYmd((detail as any)?.factoryEntryDate),
componentIds: parseIdsValue((detail as any)?.componentId),
beijianIds: parseIdsValue((detail as any)?.beijianId),
qrcodeUrl: (detail as any)?.qrcodeUrl,
}
} finally {
formLoading.value = false
}
}
if (type == 'create' || typeof formData.value.dvId != 'number') {
deviceList.value = await DeviceApi.getDeviceListByNoUsed()
} else {
deviceList.value = await DeviceApi.getDeviceList2ByNoUsed(formData.value.dvId)
}
getList()
bjGetList()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
code: undefined as string | undefined,
name: undefined as string | undefined,
description: undefined as string | undefined,
remark: undefined as string | undefined,
createTime: [] as string[]
})
const bjQueryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined,
categoryId: undefined
})
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 配件搜索按钮操作 */
const bjHandleQuery = () => {
bjQueryParams.pageNo = 1
bjGetList()
}
/** 配件重置按钮操作 */
const bjResetQuery = () => {
bjQueryFormRef.value.resetFields()
bjHandleQuery()
}
const handleSelectionChange = (rows: CriticalComponentVO[]) => {
selectedIds.value = rows.map((r) => r.id).filter((id): id is number => typeof id === 'number')
// 获取当前页所有行的 id
const currentPageIds = rows.map(item => item.id)
// 从已选中的数组中移除当前页的数据
selectedRows.value = selectedRows.value.filter(
item => !currentPageIds.includes(item.id)
)
// 添加当前页新选中的数据
selectedRows.value.push(...rows)
}
// 存储当前已选中的行
const currentSelectedRows = ref([])
// select 事件row 是当前操作的行selected 是操作后的状态
const handleSelect = (selection, row) => {
// 判断是选中还是取消选中
const isSelected = selection.includes(row)
if (isSelected) {
// console.log(`✅ 行被选中: ID=${row.id}, Name=${row.name}`)
ids.value.push(row.id)
} else {
ids.value = ids.value.filter(
item => item !== row.id
)
// console.log(`❌ 行被取消选中: ID=${row.id}, Name=${row.name}`)
}
// 更新当前选中状态
currentSelectedRows.value = selection
}
const handleSelectAll = (selection) => {
ids.value = selection?.map((row) => row.id).filter((id) => id !== undefined) ?? []
/* let newVar = selection?.map((row) => row.id).filter((id) => id !== undefined) ?? [];
newVar.forEach(row => {
ids.value.push(row)
})*/
}
const bjHandleSelectionChange = (rows: CriticalComponentVO[]) => {
bjSelectedIds.value = rows.map((r) => r.id).filter((id): id is number => typeof id === 'number')
// 获取当前页所有行的 id
const currentPageIds = rows.map(item => item.id)
// 从已选中的数组中移除当前页的数据
bjSelectedRows.value = bjSelectedRows.value.filter(
item => !currentPageIds.includes(item.id)
)
// 添加当前页新选中的数据
bjSelectedRows.value.push(...rows)
}
// 存储当前已选中的行
const bjCurrentSelectedRows = ref([])
// select 事件row 是当前操作的行selected 是操作后的状态
const bjHandleSelect = (selection, row) => {
// 判断是选中还是取消选中
const isSelected = selection.includes(row)
if (isSelected) {
// console.log(`✅ 行被选中: ID=${row.id}, Name=${row.name}`)
bjIds.value.push(row.id)
} else {
bjIds.value = bjIds.value.filter(
item => item !== row.id
)
// console.log(`❌ 行被取消选中: ID=${row.id}, Name=${row.name}`)
}
// 更新当前选中状态
bjCurrentSelectedRows.value = selection
}
const bjHandleSelectAll = (selection) => {
bjIds.value = selection?.map((row) => row.id).filter((id) => id !== undefined) ?? []
}
const getList = async () => {
loading.value = true
try {
const data = await CriticalComponentApi.getCriticalComponentPage(queryParams)
list.value = data.list
total.value = data.total
// 数据加载后,重新设置选中状态
nextTick(() => {
toggleSelection()
})
} finally {
loading.value = false
}
}
/** 配件查询列表 */
const bjGetList = async () => {
loading.value = true
try {
bjQueryParams.categoryId = 5
const data = await ProductApi.getProductPage(bjQueryParams)
bjList.value = data.list
bjTotal.value = data.total
// 数据加载后,重新设置选中状态
nextTick(() => {
bjToggleSelection()
})
} finally {
loading.value = false
}
}
// 切换选中状态
const toggleSelection = () => {
if (!multipleTableRef.value || !selectedRows.value.length) return
// 遍历当前页的数据
list.value.forEach(row => {
// 检查这一行是否在已选中数组中
const isSelected = selectedRows.value.some(item => item.id === row.id)
if (isSelected) {
// 如果应该选中,就选中
multipleTableRef.value!.toggleRowSelection(row, true)
} else {
// 否则取消选中
multipleTableRef.value!.toggleRowSelection(row, false)
}
})
}
// 切换选中状态
const bjToggleSelection = () => {
if (!bjMultipleTableRef.value || !bjSelectedRows.value.length) return
// 遍历当前页的数据
bjList.value.forEach(row => {
// 检查这一行是否在已选中数组中
const isSelected = bjSelectedRows.value.some(item => item.id === row.id)
if (isSelected) {
// 如果应该选中,就选中
bjMultipleTableRef.value!.toggleRowSelection(row, true)
} else {
// 否则取消选中
bjMultipleTableRef.value!.toggleRowSelection(row, false)
}
})
}
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = {
...(formData.value as any),
isScheduled: normalizeNumberish((formData.value as any).isScheduled) ?? 0,
ratedCapacity: normalizeNumberish((formData.value as any).ratedCapacity),
deviceType: normalizeNumberish(formData.value.deviceType),
deviceLine: normalizeNumberish((formData.value as any).deviceLine),
productionDate: normalizeYmd(formData.value.productionDate),
factoryEntryDate: normalizeYmd(formData.value.factoryEntryDate),
deviceManager: formData.value.deviceManagerIds?.length ? formData.value.deviceManagerIds.join(',') : undefined,
componentId: formData.value.componentIds?.length ? formData.value.componentIds.join(',') : undefined,
beijianId: formData.value.beijianIds?.length ? formData.value.beijianIds.join(',') : undefined,
fileUrl: normalizeFileUrlAsJsonArrayString((formData.value as any).fileUrl)
} as unknown as DeviceLedgerVO
delete (data as any).deviceManagerIds
delete (data as any).componentIds
delete (data as any).beijianIds
if (formType.value === 'create') {
await DeviceLedgerApi.createDeviceLedger(data)
message.success(t('common.createSuccess'))
} else {
await DeviceLedgerApi.updateDeviceLedger(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
// 方法1直接监听 data
watch(list, (newData, oldData) => {
// 可以在这里执行相关操作
if (newData.length > 0) {
// 数据加载完成后的操作
setDefaultSelections()
}
}, { deep: true })
//const defaultSelectedIds = [144, 143,141]
// 设置默认选中的行
const setDefaultSelections = () => {
// 等待DOM更新完成
nextTick(() => {
if (!multipleTableRef.value) return
multipleTableRef.value.clearSelection()
const rawSubjectIds = toRaw(formData.value.componentIds)
if (rawSubjectIds.length != 0) {
let row = {
id: undefined
}
multipleTableRef.value.toggleRowSelection(row, true)
}
// 遍历数据,找到需要选中的行
list.value.forEach(row => {
let id = row.id;
if (rawSubjectIds.includes(row.id)) {
multipleTableRef.value.toggleRowSelection(row, true)
}
})
})
}
// 方法1直接监听 data
watch(bjList, (newData, oldData) => {
// 可以在这里执行相关操作
if (newData.length > 0) {
// 数据加载完成后的操作
setBJDefaultSelections()
}
}, { deep: true })
//const defaultSelectedIds = [144, 143,141]
// 设置默认选中的行
const setBJDefaultSelections = () => {
// 等待DOM更新完成
nextTick(() => {
if (!bjMultipleTableRef.value) return
bjMultipleTableRef.value.clearSelection()
const rawSubjectIds = toRaw(formData.value.beijianIds)
if (rawSubjectIds.length != 0) {
let row = {
id: undefined
}
bjMultipleTableRef.value.toggleRowSelection(row, true)
}
// 遍历数据,找到需要选中的行
bjList.value.forEach(row => {
let id = row.id;
if (rawSubjectIds.includes(row.id)) {
bjMultipleTableRef.value.toggleRowSelection(row, true)
}
})
})
}
/** 重置表单 */
const resetForm = () => {
formData.value = initFormData()
formRef.value?.resetFields()
}
</script>
<style scoped>
.device-ledger-manager {
display: flex;
width: 100%;
gap: 12px;
}
.device-ledger-manager :deep(.el-input) {
flex: 1;
}
:deep(.ellipsis-text) {
max-width: 300px;
}
.device-ledger-transfer {
width: 100%;
overflow-x: auto;
}
.device-ledger-transfer :deep(.el-transfer) {
width: 100%;
min-width: 760px;
flex-wrap: nowrap;
}
.device-ledger-transfer :deep(.el-transfer-panel) {
flex: 1 0 340px;
width: 340px;
}
.device-ledger-transfer-dialog :deep(.el-dialog__body) {
padding-top: 8px;
}
.device-ledger-transfer-dialog .device-ledger-transfer :deep(.el-transfer-panel) {
height: 560px;
}
.device-ledger-transfer-dialog .device-ledger-transfer :deep(.el-transfer-panel__body) {
height: 520px;
}
.device-ledger-transfer-dialog .device-ledger-transfer :deep(.el-transfer-panel__list) {
height: 100%;
}
.device-ledger-transfer :deep(.el-transfer__buttons) {
flex: 0 0 72px;
padding: 0 12px;
}
.device-ledger-selection-input :deep(.el-input__inner) {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.no-select-all :deep(.el-table__header-wrapper .el-checkbox) {
display: none;
}
</style>