Merge remote-tracking branch 'origin/main'

main
liutao 7 hours ago
commit ebff0725ef

@ -215,6 +215,11 @@ export const DeviceApi = {
return await request.put({ url: `/iot/device-contact-model/update`, data })
},
// 批量修改设备属性
batchUpdateDeviceAttribute: async (data) => {
return await request.put({ url: '/iot/device/device-attribute/batch-update', data })
},
// 删除设备属性
deleteDeviceAttribute: async (ids: string) => {
return await request.delete({ url: `/iot/device-contact-model/delete?ids=` + ids })

@ -2007,7 +2007,11 @@ export default {
validatorDeviceCodeRequired: '编码不能为空',
gjTitle: '选择关键件',
bjTitle: '选择备件',
all: '全部',
normal: '正常',
disabled: '停用',
scheduled: '已排产',
unscheduled: '未排产',
stop: '停用',
maintenance: '维修',
scrap: '报废',
@ -2149,6 +2153,7 @@ export default {
// 任务管理
TaskManagement: {
all: '全部',
name: '名称',
taskType: '类型',
taskTypeInspect: '点检',
@ -2159,6 +2164,7 @@ export default {
planEndDate: '计划结束时间',
cronExpression: 'cron 表达式',
enabled: '启用',
disabled: '未启用',
creatorName: '创建人',
createTime: '创建时间',
updateTime: '更新时间',
@ -2199,11 +2205,17 @@ export default {
// 工单管理
WorkOrderManagement: {
all: '全部',
basicInfo: '基本信息',
planNo: '单号',
planType: '类型',
planTypeInspect: '点检',
planTypeMaintain: '保养',
unworked: '未作业',
worked: '已作业',
overtimeUnworked: '超时未作业',
overtimeWorked: '超时已作业',
canceled: '取消',
jobStatus: '作业状态',
jobResult: '结果',
deviceName: '设备名称',
@ -2253,6 +2265,7 @@ export default {
// 维修单
DvRepair: {
all: '全部',
repairCode: '维修单号',
repairName: '维修单',
machineryName: '设备名称',
@ -3115,6 +3128,7 @@ export default {
},
MoldRepair: {
all: '全部',
moduleName: '维修单',
basicInfo: '基本信息',
faultInfo: '故障信息',
@ -3322,12 +3336,18 @@ export default {
},
MoldWorkOrderInquiry: {
all: '全部',
moduleName: '工单查询',
basicInfo: '基本信息',
planNo: '单号',
planType: '类型',
planTypeInspect: '点检',
planTypeMaintain: '保养',
unworked: '未作业',
worked: '已作业',
overtimeUnworked: '超时未作业',
overtimeWorked: '超时已作业',
canceled: '取消',
jobStatus: '作业状态',
jobResult: '结果',
jobResultOk: '通过',

@ -230,9 +230,9 @@ const onKeywordClear = () => {
const statusCards = computed(() => [
{ key: 'all', label: t('MoldManagement.MoldBrandPage.all'), value: counters.allCount, status: undefined },
{ key: 'onMachine', label: t('MoldManagement.MoldBrandPage.onMachine'), value: counters.onMachineCount, status: 1 },
{ key: 'onMachine', label: t('MoldManagement.MoldBrandPage.onMachine'), value: counters.onMachineCount, status: 0 },
{ key: 'inStock', label: t('MoldManagement.MoldBrandPage.inStock'), value: counters.inStockCount, status: 4 },
{ key: 'standby', label: t('MoldManagement.MoldBrandPage.standby'), value: counters.standbyCount, status: 0 },
{ key: 'standby', label: t('MoldManagement.MoldBrandPage.standby'), value: counters.standbyCount, status: 1 },
{ key: 'repairing', label: t('MoldManagement.MoldBrandPage.repairing'), value: counters.repairingCount, status: 2 },
{ key: 'scrapped', label: t('MoldManagement.MoldBrandPage.scrapped'), value: counters.scrappedCount, status: 3 }
])
@ -458,12 +458,12 @@ onBeforeUnmount(() => {
<style scoped>
.mold-brand-page__header {
display: grid;
grid-template-columns: repeat(6, 10vw);
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 12px;
width: 100%;
}
.mold-brand-page__stat {
width: 10vw;
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
@ -553,13 +553,13 @@ onBeforeUnmount(() => {
@media (max-width: 1200px) {
.mold-brand-page__header {
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
@media (max-width: 768px) {
.mold-brand-page__header {
grid-template-columns: 1fr;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
</style>

@ -33,8 +33,7 @@
</ContentWrap>
<div v-loading="cardsLoading" class="stock-overview__cards">
<ContentWrap v-for="item in summaryCards" :key="item.title" class="stock-overview__card-wrap"
:body-style="{ padding: '22px 24px' }">
<div v-for="item in summaryCards" :key="item.title" class="stock-overview__card-wrap">
<div class="stock-overview__card">
<div class="stock-overview__card-main">
<div class="stock-overview__card-title">{{ item.title }}</div>
@ -45,7 +44,7 @@
<Icon :icon="item.icon" :class="['stock-overview__card-icon', `is-${item.color}`]" :size="34" />
</div>
<div v-if="item.tip" class="stock-overview__card-tip">{{ item.tip }}</div>
</ContentWrap>
</div>
</div>
<div class="stock-overview__tables">
@ -507,13 +506,22 @@ onMounted(async () => {
}
.stock-overview__card-wrap {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
background: #fff;
cursor: pointer;
transition: box-shadow 0.2s ease;
margin-bottom: 0 !important;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.stock-overview__card {
display: flex;
justify-content: space-between;
min-height: 174px;
}
.stock-overview__card-main {

@ -38,7 +38,7 @@ v-model="formData.dataUnit" clearable
<el-form-item :label="t('DataCollection.Device.ratio')" prop="ratio">
<el-input-number
v-model="formData.ratio" :placeholder="t('DataCollection.Device.placeholderRatio')" :min="0.00"
:decision="2" :step="0.01" class="!w-full" :disabled="!ratioEnabled" />
:decision="2" :step="0.01" class="!w-full" />
</el-form-item>
<el-form-item :label="t('DataCollection.Device.remark')" prop="remark">
<el-input
@ -174,7 +174,8 @@ const formRules = reactive({
},
trigger: ['blur', 'change']
}
]
],
attributeType: [{ required: true, message: '点位类型不能为空', trigger: 'change' }]
})
const formRef = ref() // Ref

@ -76,6 +76,9 @@
>
<Icon icon="ep:delete" class="mr-5px" /> {{ t('DataCollection.Device.batchDelete') }}
</el-button>
<el-button type="primary" plain @click="openBatchEdit" v-hasPermi="['iot:device:update']">
<Icon icon="ep:edit" class="mr-5px" /> 批量编辑
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
@ -137,7 +140,11 @@ ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-to
align="center"
prop="dataUnit"
width="80px"
/>
>
<template #default="scope">
{{ formatDataUnit(scope.row.dataUnit) }}
</template>
</el-table-column>
<el-table-column
:label="t('DataCollection.Device.ratio')"
align="center"
@ -193,6 +200,27 @@ ref="planPaginationRef"
<DeviceAttributeForm ref="formRef" @success="getList" />
<!-- 单位导入对话框 -->
<Dialog title="批量编辑" v-model="batchEditVisible">
<el-form ref="batchEditFormRef" :model="batchEditForm" label-width="120px">
<el-form-item :label="t('DataCollection.Device.attributeType')">
<el-select v-model="batchEditForm.attributeType" clearable filterable :placeholder="t('DataCollection.Device.placeholderAttributeType')" class="w-1/1">
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item :label="t('DataCollection.Device.dataUnit')">
<el-select v-model="batchEditForm.dataUnit" clearable :placeholder="t('DataCollection.DeviceModel.placeholderDataUnit')" class="w-1/1">
<el-option v-for="unit in unitList" :key="unit.id" :label="unit.name" :value="unit.id" />
</el-select>
</el-form-item>
<el-form-item :label="t('DataCollection.Device.ratio')">
<el-input-number v-model="batchEditForm.ratio" :placeholder="t('DataCollection.Device.placeholderRatio')" :min="0" :precision="2" :step="0.01" class="!w-full" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="batchEditVisible = false">{{ t('common.cancel') }}</el-button>
<el-button type="primary" :loading="batchEditLoading" @click="handleBatchEdit">{{ t('common.ok') }}</el-button>
</template>
</Dialog>
<DeviceAttributeImportForm ref="importFormRef" @success="getList" />
</template>
<script setup lang="ts">
@ -203,6 +231,7 @@ import { DeviceApi } from '@/api/iot/device'
import DeviceAttributeForm from './DeviceAttributeForm.vue'
import DeviceAttributeImportForm from './DeviceAttributeImportForm.vue'
import { DeviceAttributeTypeApi, DeviceAttributeTypeVO } from '@/api/iot/deviceattributetype'
import { ProductUnitApi, ProductUnitVO } from '@/api/erp/product/unit'
const { t } = useI18n() //
const message = useMessage() //
@ -225,6 +254,7 @@ const props = defineProps<{
}>()
const typeList = ref<DeviceAttributeTypeVO[]>([])
const unitList = ref<ProductUnitVO[]>([])
const loading = ref(false) //
const list = ref([]) //
@ -252,6 +282,19 @@ const formatAddressValue = (value: unknown) => {
return ''
}
const loadUnitList = async () => {
if (unitList.value.length) {
return
}
unitList.value = await ProductUnitApi.getProductUnitSimpleList()
}
const formatDataUnit = (value: unknown) => {
if (value === null || value === undefined || value === '') {
return '-'
}
return unitList.value.find((unit) => String(unit.id) === String(value))?.name ?? String(value)
}
const selectedIds = ref<number[]>([])
const handleSelectionChange = (rows: any[]) => {
selectedIds.value = rows?.map((row) => row.id).filter((id) => id !== undefined) ?? []
@ -353,6 +396,51 @@ const handleBatchDelete = async () => {
await handleDelete(selectedIds.value)
}
const batchEditVisible = ref(false)
const batchEditLoading = ref(false)
const batchEditFormRef = ref()
const batchEditForm = reactive({
attributeType: undefined as number | undefined,
dataUnit: undefined as string | undefined,
ratio: undefined as number | undefined
})
const resetBatchEditForm = () => {
batchEditForm.attributeType = undefined
batchEditForm.dataUnit = undefined
batchEditForm.ratio = undefined
batchEditFormRef.value?.resetFields()
}
const openBatchEdit = async () => {
if (!selectedIds.value.length) {
message.error('请选择需要编辑的数据')
return
}
resetBatchEditForm()
await loadUnitList()
batchEditVisible.value = true
}
const handleBatchEdit = async () => {
try {
batchEditLoading.value = true
await DeviceApi.batchUpdateDeviceAttribute({
ids: selectedIds.value,
attributeType: batchEditForm.attributeType,
dataUnit: batchEditForm.dataUnit,
ratio: batchEditForm.ratio
})
message.success(t('common.updateSuccess'))
batchEditVisible.value = false
selectedIds.value = []
tableRef.value?.clearSelection?.()
await getList()
} finally {
batchEditLoading.value = false
}
}
/** 导入 */
const importFormRef = ref()
const handleImport = () => {
@ -393,6 +481,11 @@ onMounted(async () => {
} catch {
typeList.value = []
}
try {
await loadUnitList()
} catch {
unitList.value = []
}
updatePlanTableMaxHeight()
window.addEventListener('resize', updatePlanTableMaxHeight)
})

@ -63,8 +63,7 @@
:min="0.00"
:decision="2"
:step="0.01"
class="!w-full"
:disabled = "!ratioEnabled"/>
class="!w-full"/>
</el-form-item>
<el-form-item :label="t('DataCollection.DeviceModel.remark')" prop="remark">
<el-input

@ -143,12 +143,19 @@
<div class="device-ledger-right">
<ContentWrap>
<div class="device-ledger__header">
<div v-for="card in statusCards" :key="card.key" class="device-ledger__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.status, card.isSchedueld)">
<div class="device-ledger__stat-title">{{ card.label }}</div>
<div class="device-ledger__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="120px"
label-width="68px"
>
<el-form-item
:label="t('EquipmentManagement.EquipmentLedger.deviceCode')"
@ -177,6 +184,7 @@
<el-form-item
:label="t('EquipmentManagement.EquipmentLedger.deviceStatus')"
prop="deviceStatus"
v-show="showAllFilters"
>
<el-select
v-model="queryParams.deviceStatus"
@ -195,6 +203,7 @@
<el-form-item
:label="t('EquipmentManagement.EquipmentLedger.deviceType')"
prop="deviceType"
v-show="showAllFilters"
>
<el-tree-select
v-model="queryParams.deviceType"
@ -211,6 +220,7 @@
<el-form-item
:label="t('EquipmentManagement.EquipmentLedger.deviceBrand')"
prop="deviceBrand"
v-show="showAllFilters"
>
<el-input
v-model="queryParams.deviceBrand"
@ -220,7 +230,7 @@
class="!w-240px"
/>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.sn')" prop="sn">
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.sn')" prop="sn" v-show="showAllFilters">
<el-input
v-model="queryParams.sn"
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderSn')"
@ -232,6 +242,7 @@
<el-form-item
:label="t('EquipmentManagement.EquipmentLedger.outgoingTime')"
prop="outgoingTime"
v-show="showAllFilters"
>
<el-date-picker
v-model="queryParams.outgoingTime"
@ -242,6 +253,12 @@
class="!w-240px"
/>
</el-form-item>
<el-form-item v-if="filterCount > 2">
<el-button type="text" class="text-primary" @click="toggleFilters">
<Icon :icon="showAllFilters ? 'ep:arrow-up' : 'ep:arrow-down'" class="mr-5px" />
{{ showAllFilters ? t('FactoryModeling.FactoryStructure.collapseText') : t('FactoryModeling.FactoryStructure.expandText') }}
</el-button>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" />
@ -678,20 +695,47 @@ const loading = ref(true) // 列表的加载中
const list = ref<DeviceLedgerVO[]>([]) //
const total = ref(0) //
const selectedDeviceLineId = ref<string | undefined>(undefined)
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
normalCount: 0,
disabledCount: 0,
scheduledCount: 0,
unscheduledCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('EquipmentManagement.EquipmentLedger.all'), value: counters.allCount, status: undefined, isSchedueld: undefined },
{ key: 'normal', label: t('EquipmentManagement.EquipmentLedger.normal'), value: counters.normalCount, status: 0, isSchedueld: undefined },
{ key: 'disabled', label: t('EquipmentManagement.EquipmentLedger.disabled'), value: counters.disabledCount, status: 1, isSchedueld: undefined },
{ key: 'scheduled', label: t('EquipmentManagement.EquipmentLedger.scheduled'), value: counters.scheduledCount, status: undefined, isSchedueld: 1 },
{ key: 'unscheduled', label: t('EquipmentManagement.EquipmentLedger.unscheduled'), value: counters.unscheduledCount, status: undefined, isSchedueld: 0 }
])
const changeStatus = (key: string, status?: number, isSchedueld?: number) => {
currentStatusKey.value = key
queryParams.deviceStatus = status
queryParams.isSchedueld = isSchedueld
handleQuery()
}
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
deviceCode: undefined,
deviceName: undefined,
deviceStatus: undefined,
deviceStatus: undefined as number | undefined,
deviceBrand: undefined,
sn: undefined,
outgoingTime: undefined,
deviceType: undefined as string | undefined,
deviceLine: undefined as string | undefined
deviceLine: undefined as string | undefined,
isSchedueld: undefined as number | undefined
})
const queryFormRef = ref() //
const exportLoading = ref(false)
const showAllFilters = ref(false)
const filterCount = 7
const toggleFilters = () => {
showAllFilters.value = !showAllFilters.value
}
const tableRef = ref()
const planTableMaxHeight = ref(520)
@ -1012,10 +1056,16 @@ const getList = async () => {
sn: queryParams.sn,
outgoingTime: queryParams.outgoingTime,
deviceType: queryParams.deviceType,
deviceLine: queryParams.deviceLine
deviceLine: queryParams.deviceLine,
isScheduled: queryParams.isSchedueld
})
list.value = data.list
total.value = data.total
list.value = data.list ?? []
total.value = data.total ?? 0
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.normalCount = Number(data?.normalCount ?? 0)
counters.disabledCount = Number(data?.disabledCount ?? 0)
counters.scheduledCount = Number(data?.scheduledCount ?? 0)
counters.unscheduledCount = Number(data?.unscheduledCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -1031,6 +1081,9 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
currentStatusKey.value = 'all'
queryParams.deviceStatus = undefined
queryParams.isSchedueld = undefined
handleQuery()
}
@ -1122,6 +1175,45 @@ onBeforeUnmount(() => {
min-width: 0;
}
.device-ledger__header {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.device-ledger__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.device-ledger__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.device-ledger__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
.simple-grid-view {
background-color: #fff;
border-radius: 4px;

@ -727,14 +727,14 @@ const ensureDeviceOptionsLoaded = async () => {
}
}
const deviceTypeNameMap = ref<Record<number, string>>({})
const deviceTypeNameMap = ref<Record<string, string>>({})
const buildDeviceTypeNameMap = (nodes: DeviceTypeTreeVO[]) => {
const map: Record<number, string> = {}
const map: Record<string, string> = {}
const walk = (list: DeviceTypeTreeVO[]) => {
list.forEach((node) => {
if (typeof node?.id === 'number') {
map[node.id] = node.name ?? ''
if (node?.id != null) {
map[String(node.id)] = node.name ?? ''
}
if (Array.isArray(node?.children)) walk(node.children)
})
@ -744,8 +744,9 @@ const buildDeviceTypeNameMap = (nodes: DeviceTypeTreeVO[]) => {
}
const getDeviceTypeName = (value: any) => {
const id = typeof value === 'number' ? value : Number(value)
if (!Number.isNaN(id) && deviceTypeNameMap.value[id]) return deviceTypeNameMap.value[id]
if (value == null || value === '') return '-'
const key = String(value)
if (deviceTypeNameMap.value[key]) return deviceTypeNameMap.value[key]
return value ?? '-'
}

@ -2,6 +2,13 @@
<div class="dv-repair-page">
<template v-if="!formVisible">
<ContentWrap>
<div class="dv-repair__header">
<div v-for="card in statusCards" :key="card.key" class="dv-repair__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.status, card.repairStatus)">
<div class="dv-repair__stat-title">{{ card.label }}</div>
<div class="dv-repair__stat-value">{{ card.value }}</div>
</div>
</div>
<!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" min-label-width="68px">
<el-form-item :label="t('EquipmentManagement.DvRepair.repairCode')" prop="repairCode">
@ -21,7 +28,7 @@
class="!w-240px" />
</el-form-item>
<el-form-item :label="t('EquipmentManagement.DvRepair.machineryName')" prop="machineryName">
<el-form-item :label="t('EquipmentManagement.DvRepair.machineryName')" prop="machineryName" v-show="showAllFilters">
<el-input
v-model="queryParams.machineryName"
:placeholder="t('EquipmentManagement.DvRepair.placeholderMachineryName')"
@ -86,7 +93,7 @@
<el-option v-for="opt in repairStatusOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item v-if="filterCount > 3">
<el-form-item v-if="filterCount > 2">
<el-button type="text" class="text-primary" @click="toggleFilters">
<Icon :icon="showAllFilters ? 'ep:arrow-up' : 'ep:arrow-down'" class="mr-5px" />
{{ showAllFilters ? t('FactoryModeling.FactoryStructure.collapseText') : t('FactoryModeling.FactoryStructure.expandText') }}
@ -255,6 +262,29 @@ const queryParams = reactive({
remark: undefined,
createTime: [],
})
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
pendingCount: 0,
finishedCount: 0,
pendingRepairCount: 0,
passedCount: 0,
failedCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('EquipmentManagement.DvRepair.all'), value: counters.allCount, status: undefined, repairStatus: undefined },
{ key: 'pending', label: t('EquipmentManagement.DvRepair.statusPending'), value: counters.pendingCount, status: '0', repairStatus: undefined },
{ key: 'finished', label: t('EquipmentManagement.DvRepair.statusFinished'), value: counters.finishedCount, status: '1', repairStatus: undefined },
{ key: 'pendingRepair', label: t('EquipmentManagement.DvRepair.repairResultPending'), value: counters.pendingRepairCount, status: undefined, repairStatus: '0' },
{ key: 'passed', label: t('EquipmentManagement.DvRepair.repairResultOk'), value: counters.passedCount, status: undefined, repairStatus: '1' },
{ key: 'failed', label: t('EquipmentManagement.DvRepair.repairResultNg'), value: counters.failedCount, status: undefined, repairStatus: '2' }
])
const changeStatus = (key: string, status?: string, repairStatus?: string) => {
currentStatusKey.value = key
queryParams.status = status
queryParams.repairStatus = repairStatus
handleQuery()
}
const queryFormRef = ref() //
const exportLoading = ref(false) //
const showAllFilters = ref(false)
@ -332,8 +362,14 @@ const getList = async () => {
loading.value = true
try {
const data = await DvRepairApi.getDvRepairPage(queryParams)
list.value = data.list
total.value = data.total
list.value = data.list ?? []
total.value = data.total ?? 0
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.pendingCount = Number(data?.pendingCount ?? 0)
counters.finishedCount = Number(data?.finishedCount ?? 0)
counters.pendingRepairCount = Number(data?.pendingRepairCount ?? 0)
counters.passedCount = Number(data?.passedCount ?? 0)
counters.failedCount = Number(data?.failedCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -349,6 +385,9 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
currentStatusKey.value = 'all'
queryParams.status = undefined
queryParams.repairStatus = undefined
handleQuery()
}
@ -422,3 +461,44 @@ onBeforeUnmount(() => {
window.removeEventListener("resize", updatePlanTableMaxHeight)
})
</script>
<style scoped lang="scss">
.dv-repair__header {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.dv-repair__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.dv-repair__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.dv-repair__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>

@ -162,10 +162,14 @@
<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 ?? '-' }}
{{ getDeviceTypeName(row.deviceTypeName ?? row.deviceType) }}
</template>
</el-table-column>
<el-table-column :label="t('EquipmentManagement.EquipmentLedger.deviceStatus')" prop="deviceStatus" width="100">
<template #default="{ row }">
<dict-tag type="mes_tz_status" :value="row.deviceStatus ?? ''" />
</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>
@ -283,6 +287,7 @@
<script setup lang="ts">
import { TaskManagementApi, TaskManagementVO } from '@/api/mes/taskManagement'
import { DeviceLedgerApi } from '@/api/mes/deviceledger'
import { DeviceTypeApi, DeviceTypeTreeVO } from '@/api/mes/devicetype'
import { PlanMaintenanceApi, PlanMaintenanceVO } from '@/api/mes/planmaintenance'
import { getSimpleUserList, UserVO } from '@/api/system/user'
@ -332,6 +337,29 @@ const deviceDialogQueryParams = reactive({
deviceName: undefined as string | undefined
})
const deviceTypeNameMap = ref<Record<string, string>>({})
const buildDeviceTypeNameMap = (nodes: DeviceTypeTreeVO[]) => {
const map: Record<string, string> = {}
const walk = (list: DeviceTypeTreeVO[]) => {
list.forEach((node) => {
if (node?.id != null) {
map[String(node.id)] = node.name ?? ''
}
if (Array.isArray(node?.children)) walk(node.children)
})
}
walk(nodes)
deviceTypeNameMap.value = map
}
const getDeviceTypeName = (value: any) => {
if (value == null || value === '') return '-'
const key = String(value)
if (deviceTypeNameMap.value[key]) return deviceTypeNameMap.value[key]
return value ?? '-'
}
const planDialogVisible = ref(false)
const planDialogLoading = ref(false)
const planDialogList = ref<PlanOption[]>([])
@ -546,6 +574,12 @@ const openDeviceDialog = async () => {
deviceDialogQueryParams.pageNo = 1
deviceDialogQueryParams.deviceCode = undefined
deviceDialogQueryParams.deviceName = undefined
if (!Object.keys(deviceTypeNameMap.value).length) {
try {
const treeData = await DeviceTypeApi.getDeviceTypeTree({})
buildDeviceTypeNameMap(treeData ?? [])
} catch (e) {}
}
await getDeviceDialogList()
}

@ -1,5 +1,12 @@
<template>
<ContentWrap>
<div class="task-management__header">
<div v-for="card in statusCards" :key="card.key" class="task-management__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.taskType, card.enabled)">
<div class="task-management__stat-title">{{ card.label }}</div>
<div class="task-management__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form
class="-mb-15px"
:model="queryParams"
@ -26,7 +33,7 @@
<el-option v-for="opt in taskTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.projectForm')" prop="projectForm">
<el-form-item :label="t('EquipmentManagement.TaskManagement.projectForm')" prop="projectForm" v-show="showAllFilters">
<el-select
v-model="queryParams.projectForm"
multiple
@ -38,7 +45,7 @@
<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 :label="t('EquipmentManagement.TaskManagement.deviceList')" prop="deviceIds">
<el-form-item :label="t('EquipmentManagement.TaskManagement.deviceList')" prop="deviceIds" v-show="showAllFilters">
<el-select
v-model="queryParams.deviceIds"
multiple
@ -57,7 +64,7 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.operableUsers')" prop="operator">
<el-form-item :label="t('EquipmentManagement.TaskManagement.operableUsers')" prop="operator" v-show="showAllFilters">
<el-select
v-model="queryParams.operator"
filterable
@ -68,7 +75,7 @@
<el-option v-for="item in users" :key="String(item.id)" :label="item.nickname" :value="String(item.id)" />
</el-select>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.TaskManagement.taskTime')" prop="taskTime">
<el-form-item :label="t('EquipmentManagement.TaskManagement.taskTime')" prop="taskTime" v-show="showAllFilters">
<el-date-picker
v-model="queryParams.taskTime"
value-format="YYYY-MM-DD HH:mm:ss"
@ -78,6 +85,12 @@
class="!w-240px"
/>
</el-form-item>
<el-form-item v-if="filterCount > 2">
<el-button type="text" class="text-primary" @click="toggleFilters">
<Icon :icon="showAllFilters ? 'ep:arrow-up' : 'ep:arrow-down'" class="mr-5px" />
{{ showAllFilters ? t('FactoryModeling.FactoryStructure.collapseText') : t('FactoryModeling.FactoryStructure.expandText') }}
</el-button>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" />
@ -312,9 +325,36 @@ const queryParams = reactive({
projectForm: [] as string[],
deviceIds: [] as string[],
operator: undefined as string | undefined,
taskTime: undefined as string | undefined
taskTime: undefined as string | undefined,
enabled: undefined as boolean | undefined
})
const queryFormRef = ref()
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
inspectCount: 0,
maintainCount: 0,
enabledCount: 0,
disabledCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('EquipmentManagement.TaskManagement.all'), value: counters.allCount, taskType: undefined, enabled: undefined },
{ key: 'inspect', label: t('EquipmentManagement.TaskManagement.taskTypeInspect'), value: counters.inspectCount, taskType: 1, enabled: undefined },
{ key: 'maintain', label: t('EquipmentManagement.TaskManagement.taskTypeMaintain'), value: counters.maintainCount, taskType: 2, enabled: undefined },
{ key: 'enabled', label: t('EquipmentManagement.TaskManagement.enabled'), value: counters.enabledCount, taskType: undefined, enabled: true },
{ key: 'disabled', label: t('EquipmentManagement.TaskManagement.disabled'), value: counters.disabledCount, taskType: undefined, enabled: false }
])
const changeStatus = (key: string, taskType?: number, enabled?: boolean) => {
currentStatusKey.value = key
queryParams.taskType = taskType
queryParams.enabled = enabled
handleQuery()
}
const showAllFilters = ref(false)
const filterCount = 6
const toggleFilters = () => {
showAllFilters.value = !showAllFilters.value
}
const parseIdsValue = (value: any): string[] => {
if (!value) return []
@ -376,6 +416,11 @@ const getList = async () => {
const data = await TaskManagementApi.getTaskManagementPage(buildQueryParams())
list.value = data.list
total.value = data.total
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.inspectCount = Number(data?.inspectCount ?? 0)
counters.maintainCount = Number(data?.maintainCount ?? 0)
counters.enabledCount = Number(data?.enabledCount ?? 0)
counters.disabledCount = Number(data?.disabledCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -389,6 +434,9 @@ const handleQuery = () => {
const resetQuery = () => {
queryFormRef.value.resetFields()
currentStatusKey.value = 'all'
queryParams.taskType = undefined
queryParams.enabled = undefined
handleQuery()
}
@ -502,3 +550,44 @@ onBeforeUnmount(() => {
window.removeEventListener('resize', updatePlanTableMaxHeight)
})
</script>
<style scoped lang="scss">
.task-management__header {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.task-management__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.task-management__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.task-management__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>

@ -252,8 +252,8 @@ const currentDeviceRelationRow = ref<any>()
const currentProductData = ref<any>()
const selectedDeviceRows = ref<any[]>([])
const deviceRelationForm = reactive({
productId: undefined as number | undefined,
devices: [] as { id: number; name: string }[]
productId: undefined as string | undefined,
devices: [] as { id: string; name: string }[]
})
const deviceColumns = computed(() => [
@ -304,7 +304,7 @@ const taskDetailRequestMap = new Map<string, Promise<any[]>>()
const currentDetailRequestId = ref(0)
const taskTableBusy = computed(() => taskLoading.value || taskSelectionLoading.value)
const getRelationName = (item: Record<string, any>, nameKeys: string[], fallbackPrefix: string, id: number) => {
const getRelationName = (item: Record<string, any>, nameKeys: string[], fallbackPrefix: string, id: string) => {
for (const key of nameKeys) {
const value = item[key]
if (value !== undefined && value !== null && String(value).trim() !== '') {
@ -318,11 +318,11 @@ const normalizeRelationList = (
fallbackRows: any[] | undefined,
nameKeys: string[],
fallbackPrefix: string
): { id: number; name: string }[] => {
const fallbackMap = new Map<number, string>()
): { id: string; name: string }[] => {
const fallbackMap = new Map<string, string>()
;(fallbackRows || []).forEach((row) => {
const id = Number(row?.id)
if (!Number.isFinite(id)) return
const id = String(row?.id)
if (!id || id === 'undefined' || id === 'null') return
fallbackMap.set(id, getRelationName(row, nameKeys, fallbackPrefix, id))
})
const parseArray = (source: unknown): any[] => {
@ -342,23 +342,23 @@ const normalizeRelationList = (
return parseArray(value)
.map((item) => {
if (typeof item === 'object' && item !== null) {
const id = Number((item as any).id)
if (!Number.isFinite(id)) return undefined
const id = String((item as any).id)
if (!id || id === 'undefined' || id === 'null') return undefined
return {
id,
name: getRelationName(item as any, nameKeys, fallbackPrefix, id)
}
}
const id = Number(item)
if (!Number.isFinite(id)) return undefined
const id = String(item)
if (!id || id === 'undefined' || id === 'null') return undefined
return {
id,
name: fallbackMap.get(id) || `${fallbackPrefix}ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
.filter((item): item is { id: string; name: string } => Boolean(item))
}
const toDeviceRows = (devices: { id: number; name: string }[]) => {
const toDeviceRows = (devices: { id: string; name: string }[]) => {
return devices.map((item) => ({
id: item.id,
deviceName: item.name,
@ -625,7 +625,7 @@ const openDeviceRelationDialog = async (row: any) => {
deviceRelationLoading.value = true
selectedDeviceRows.value = []
try {
const productData = await ProductApi.getProduct(Number(row.productId))
const productData = await ProductApi.getProduct(String(row.productId))
currentProductData.value = productData
const devices = normalizeRelationList(
(productData as any).devices ?? (productData as any).deviceIds,
@ -633,7 +633,7 @@ const openDeviceRelationDialog = async (row: any) => {
['name', 'deviceName', 'code'],
t('ProductionPlan.TaskSummary.deviceColumnName')
)
deviceRelationForm.productId = Number(row.productId)
deviceRelationForm.productId = String(row.productId)
deviceRelationForm.devices = devices
selectedDeviceRows.value = toDeviceRows(devices)
} finally {
@ -656,7 +656,7 @@ const handleDeviceSelectConfirm = (payload: { ids: (number | string)[]; rows: an
name: item.deviceName || item.name || item.code || `${t('ProductionPlan.TaskSummary.deviceColumnName')}ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
.filter((item): item is { id: string; name: string } => Boolean(item))
selectedDeviceRows.value = payload.rows
}
const refreshDetailListAfterDeviceSaved = async () => {

@ -1,6 +1,13 @@
<template>
<template v-if="!resultVisible">
<ContentWrap>
<div class="work-order__header">
<div v-for="card in statusCards" :key="card.key" class="work-order__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.planType, card.jobStatus)">
<div class="work-order__stat-title">{{ card.label }}</div>
<div class="work-order__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" min-label-width="68px">
<el-form-item :label="t('EquipmentManagement.WorkOrderManagement.planNo')" prop="planNo">
<el-input
@ -288,6 +295,33 @@ const queryParams = reactive({
})
const queryFormRef = ref()
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
inspectCount: 0,
maintainCount: 0,
unworkedCount: 0,
workedCount: 0,
overtimeUnworkedCount: 0,
overtimeWorkedCount: 0,
canceledCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('EquipmentManagement.WorkOrderManagement.all'), value: counters.allCount, planType: undefined, jobStatus: undefined },
{ key: 'inspect', label: t('EquipmentManagement.WorkOrderManagement.planTypeInspect'), value: counters.inspectCount, planType: '1', jobStatus: undefined },
{ key: 'maintain', label: t('EquipmentManagement.WorkOrderManagement.planTypeMaintain'), value: counters.maintainCount, planType: '2', jobStatus: undefined },
{ key: 'unworked', label: t('EquipmentManagement.WorkOrderManagement.unworked'), value: counters.unworkedCount, planType: undefined, jobStatus: '0' },
{ key: 'worked', label: t('EquipmentManagement.WorkOrderManagement.worked'), value: counters.workedCount, planType: undefined, jobStatus: '1' },
{ key: 'overtimeUnworked', label: t('EquipmentManagement.WorkOrderManagement.overtimeUnworked'), value: counters.overtimeUnworkedCount, planType: undefined, jobStatus: '2' },
{ key: 'overtimeWorked', label: t('EquipmentManagement.WorkOrderManagement.overtimeWorked'), value: counters.overtimeWorkedCount, planType: undefined, jobStatus: '3' },
{ key: 'canceled', label: t('EquipmentManagement.WorkOrderManagement.canceled'), value: counters.canceledCount, planType: undefined, jobStatus: '4' }
])
const changeStatus = (key: string, planType?: string, jobStatus?: string) => {
currentStatusKey.value = key
queryParams.planType = planType
queryParams.jobStatus = jobStatus
handleQuery()
}
const showAllFilters = ref(false)
const filterCount = 6
const UNWORKED_JOB_STATUS = '0'
@ -317,6 +351,14 @@ const getList = async () => {
const data = await TicketManagementApi.getTicketManagementPage(buildQueryParams())
list.value = data.list
total.value = data.total
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.inspectCount = Number(data?.inspectCount ?? 0)
counters.maintainCount = Number(data?.maintainCount ?? 0)
counters.unworkedCount = Number(data?.unworkedCount ?? 0)
counters.workedCount = Number(data?.workedCount ?? 0)
counters.overtimeUnworkedCount = Number(data?.overtimeUnworkedCount ?? 0)
counters.overtimeWorkedCount = Number(data?.overtimeWorkedCount ?? 0)
counters.canceledCount = Number(data?.canceledCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -330,6 +372,9 @@ const handleQuery = () => {
const resetQuery = () => {
queryFormRef.value.resetFields()
currentStatusKey.value = 'all'
queryParams.planType = undefined
queryParams.jobStatus = '0'
handleQuery()
}
@ -418,3 +463,44 @@ onBeforeUnmount(() => {
window.removeEventListener("resize", updatePlanTableMaxHeight)
})
</script>
<style scoped lang="scss">
.work-order__header {
display: grid;
grid-template-columns: repeat(8, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.work-order__stat {
padding: 12px 14px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.work-order__stat-title {
color: var(--el-text-color-regular);
font-size: 12px;
}
.work-order__stat-value {
margin-top: 6px;
color: var(--el-text-color-primary);
font-size: 24px;
font-weight: 700;
line-height: 1;
}
</style>

@ -1,6 +1,13 @@
<template>
<ContentWrap v-if="!formVisible">
<!-- 搜索工作栏 -->
<div class="mold-repair__header">
<div v-for="card in statusCards" :key="card.key" class="mold-repair__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.status, card.repairStatus)">
<div class="mold-repair__stat-title">{{ card.label }}</div>
<div class="mold-repair__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" min-label-width="68px">
<el-form-item :label="t('MoldManagement.MoldRepair.repairCode')" prop="repairCode">
<el-input
@ -19,7 +26,7 @@
class="!w-240px" />
</el-form-item>
<el-form-item :label="t('MoldManagement.MoldRepair.moldCode')" prop="moldCode">
<el-form-item :label="t('MoldManagement.MoldRepair.moldCode')" prop="moldCode" v-show="showAllFilters">
<el-input
v-model="queryParams.moldCode"
:placeholder="t('MoldManagement.MoldRepair.placeholderMoldCode')"
@ -79,7 +86,7 @@
<el-option v-for="opt in statusOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item v-if="filterCount > 3">
<el-form-item v-if="filterCount > 2">
<el-button type="text" class="text-primary" @click="toggleFilters">
<Icon :icon="showAllFilters ? 'ep:arrow-up' : 'ep:arrow-down'" class="mr-5px" />
{{ showAllFilters ? t('FactoryModeling.FactoryStructure.collapseText') : t('FactoryModeling.FactoryStructure.expandText') }}
@ -242,10 +249,34 @@ const queryParams = reactive({
acceptedBy: undefined,
confirmBy: undefined,
status: undefined,
repairStatus: undefined,
remark: undefined,
createTime: [],
})
const queryFormRef = ref()
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
pendingCount: 0,
finishedCount: 0,
pendingRepairCount: 0,
passedCount: 0,
failedCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('MoldManagement.MoldRepair.all'), value: counters.allCount, status: undefined, repairStatus: undefined },
{ key: 'pending', label: t('MoldManagement.MoldRepair.statusPending'), value: counters.pendingCount, status: '0', repairStatus: undefined },
{ key: 'finished', label: t('MoldManagement.MoldRepair.statusDone'), value: counters.finishedCount, status: '1', repairStatus: undefined },
{ key: 'pendingRepair', label: t('MoldManagement.MoldRepair.repairResultPending'), value: counters.pendingRepairCount, status: undefined, repairStatus: '0' },
{ key: 'passed', label: t('MoldManagement.MoldRepair.repairResultOk'), value: counters.passedCount, status: undefined, repairStatus: '1' },
{ key: 'failed', label: t('MoldManagement.MoldRepair.repairResultNg'), value: counters.failedCount, status: undefined, repairStatus: '2' }
])
const changeStatus = (key: string, status?: string, repairStatus?: string) => {
currentStatusKey.value = key
queryParams.status = status
queryParams.repairStatus = repairStatus
handleQuery()
}
const exportLoading = ref(false)
const statusOptions = [
@ -318,6 +349,12 @@ const getList = async () => {
const data = await MoldRepairApi.getMoldRepairPage(params)
list.value = data.list
total.value = data.total
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.pendingCount = Number(data?.pendingCount ?? 0)
counters.finishedCount = Number(data?.finishedCount ?? 0)
counters.pendingRepairCount = Number(data?.pendingRepairCount ?? 0)
counters.passedCount = Number(data?.passedCount ?? 0)
counters.failedCount = Number(data?.failedCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -331,6 +368,9 @@ const handleQuery = () => {
const resetQuery = () => {
queryFormRef.value.resetFields()
currentStatusKey.value = 'all'
queryParams.status = undefined
queryParams.repairStatus = undefined
handleQuery()
}
@ -400,3 +440,44 @@ onBeforeUnmount(() => {
window.removeEventListener("resize", updatePlanTableMaxHeight)
})
</script>
<style scoped lang="scss">
.mold-repair__header {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.mold-repair__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.mold-repair__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.mold-repair__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>

@ -1,5 +1,12 @@
<template>
<ContentWrap>
<div class="task-config__header">
<div v-for="card in statusCards" :key="card.key" class="task-config__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.taskType, card.enabled)">
<div class="task-config__stat-title">{{ card.label }}</div>
<div class="task-config__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form
class="-mb-15px"
:model="queryParams"
@ -186,12 +193,34 @@ type PlanOption = {
const planOptions = ref<PlanOption[]>([])
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
inspectCount: 0,
maintainCount: 0,
enabledCount: 0,
disabledCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('EquipmentManagement.TaskManagement.all'), value: counters.allCount, taskType: undefined, enabled: undefined },
{ key: 'inspect', label: t('EquipmentManagement.TaskManagement.taskTypeInspect'), value: counters.inspectCount, taskType: 1, enabled: undefined },
{ key: 'maintain', label: t('EquipmentManagement.TaskManagement.taskTypeMaintain'), value: counters.maintainCount, taskType: 2, enabled: undefined },
{ key: 'enabled', label: t('EquipmentManagement.TaskManagement.enabled'), value: counters.enabledCount, taskType: undefined, enabled: true },
{ key: 'disabled', label: t('EquipmentManagement.TaskManagement.disabled'), value: counters.disabledCount, taskType: undefined, enabled: false }
])
const changeStatus = (key: string, taskType?: number, enabled?: boolean) => {
currentStatusKey.value = key
queryParams.taskType = taskType
queryParams.enabled = enabled
handleQuery()
}
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined as string | undefined,
taskType: undefined as number | undefined,
projectForm: [] as string[]
projectForm: [] as string[],
enabled: undefined as boolean | undefined
})
const queryFormRef = ref()
const showAllFilters = ref(false)
@ -233,6 +262,11 @@ const getList = async () => {
const data = await TaskManagementApi.getTaskManagementPage(buildQueryParams())
list.value = data.list
total.value = data.total
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.inspectCount = Number(data?.inspectCount ?? 0)
counters.maintainCount = Number(data?.maintainCount ?? 0)
counters.enabledCount = Number(data?.enabledCount ?? 0)
counters.disabledCount = Number(data?.disabledCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -246,6 +280,9 @@ const handleQuery = () => {
const resetQuery = () => {
queryFormRef.value.resetFields()
currentStatusKey.value = 'all'
queryParams.taskType = undefined
queryParams.enabled = undefined
handleQuery()
}
@ -358,3 +395,44 @@ onBeforeUnmount(() => {
window.removeEventListener("resize", updatePlanTableMaxHeight)
})
</script>
<style scoped lang="scss">
.task-config__header {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.task-config__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.task-config__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.task-config__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>

@ -1,6 +1,13 @@
<template>
<template v-if="!resultVisible">
<ContentWrap>
<div class="work-order__header">
<div v-for="card in statusCards" :key="card.key" class="work-order__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.planType, card.jobStatus)">
<div class="work-order__stat-title">{{ card.label }}</div>
<div class="work-order__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" min-label-width="68px">
<el-form-item :label="t('MoldManagement.MoldWorkOrderInquiry.planNo')" prop="planNo">
<el-input
@ -209,6 +216,33 @@ const queryParams = reactive({
})
const queryFormRef = ref()
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
inspectCount: 0,
maintainCount: 0,
unworkedCount: 0,
workedCount: 0,
overtimeUnworkedCount: 0,
overtimeWorkedCount: 0,
canceledCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('MoldManagement.MoldWorkOrderInquiry.all'), value: counters.allCount, planType: undefined, jobStatus: undefined },
{ key: 'inspect', label: t('MoldManagement.MoldWorkOrderInquiry.planTypeInspect'), value: counters.inspectCount, planType: '1', jobStatus: undefined },
{ key: 'maintain', label: t('MoldManagement.MoldWorkOrderInquiry.planTypeMaintain'), value: counters.maintainCount, planType: '2', jobStatus: undefined },
{ key: 'unworked', label: t('MoldManagement.MoldWorkOrderInquiry.unworked'), value: counters.unworkedCount, planType: undefined, jobStatus: '0' },
{ key: 'worked', label: t('MoldManagement.MoldWorkOrderInquiry.worked'), value: counters.workedCount, planType: undefined, jobStatus: '1' },
{ key: 'overtimeUnworked', label: t('MoldManagement.MoldWorkOrderInquiry.overtimeUnworked'), value: counters.overtimeUnworkedCount, planType: undefined, jobStatus: '2' },
{ key: 'overtimeWorked', label: t('MoldManagement.MoldWorkOrderInquiry.overtimeWorked'), value: counters.overtimeWorkedCount, planType: undefined, jobStatus: '3' },
{ key: 'canceled', label: t('MoldManagement.MoldWorkOrderInquiry.canceled'), value: counters.canceledCount, planType: undefined, jobStatus: '4' }
])
const changeStatus = (key: string, planType?: string, jobStatus?: string) => {
currentStatusKey.value = key
queryParams.planType = planType
queryParams.jobStatus = jobStatus
handleQuery()
}
const showAllFilters = ref(false)
const filterCount = 6
const UNWORKED_JOB_STATUS = '0'
@ -234,6 +268,14 @@ const getList = async () => {
const data = await TicketManagementApi.getTicketManagementPage(buildQueryParams())
list.value = data.list
total.value = data.total
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.inspectCount = Number(data?.inspectCount ?? 0)
counters.maintainCount = Number(data?.maintainCount ?? 0)
counters.unworkedCount = Number(data?.unworkedCount ?? 0)
counters.workedCount = Number(data?.workedCount ?? 0)
counters.overtimeUnworkedCount = Number(data?.overtimeUnworkedCount ?? 0)
counters.overtimeWorkedCount = Number(data?.overtimeWorkedCount ?? 0)
counters.canceledCount = Number(data?.canceledCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -247,6 +289,9 @@ const handleQuery = () => {
const resetQuery = () => {
queryFormRef.value.resetFields()
currentStatusKey.value = 'all'
queryParams.planType = undefined
queryParams.jobStatus = '0'
handleQuery()
}
@ -338,3 +383,44 @@ onBeforeUnmount(() => {
})
</script>
<style scoped lang="scss">
.work-order__header {
display: grid;
grid-template-columns: repeat(8, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.work-order__stat {
padding: 12px 14px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.work-order__stat-title {
color: var(--el-text-color-regular);
font-size: 12px;
}
.work-order__stat-value {
margin-top: 6px;
color: var(--el-text-color-primary);
font-size: 24px;
font-weight: 700;
line-height: 1;
}
</style>

Loading…
Cancel
Save