| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853 |
- <template>
- <el-row :gutter="20">
- <!-- 左侧部门树 -->
- <el-col :span="4" :xs="24">
- <ContentWrap class="h-1/1" v-if="treeShow">
- <DeptTree @node-click="handleDeptNodeClick" />
- </ContentWrap>
- </el-col>
- <el-col :span="contentSpan" :xs="24">
- <ContentWrap>
- <!-- 搜索工作栏 -->
- <el-form
- class="-mb-15px"
- :model="queryParams"
- ref="queryFormRef"
- :inline="true"
- label-width="68px"
- >
- <el-form-item :label="t('iotDevice.code')" prop="deviceCode" style="margin-left: 25px">
- <el-input
- v-model="queryParams.deviceCode"
- :placeholder="t('iotDevice.codeHolder')"
- clearable
- @keyup.enter="handleQuery"
- class="!w-200px"
- />
- </el-form-item>
- <el-form-item :label="t('iotDevice.name')" prop="deviceName">
- <el-input
- v-model="queryParams.deviceName"
- :placeholder="t('iotDevice.nameHolder')"
- clearable
- @keyup.enter="handleQuery"
- class="!w-200px"
- />
- </el-form-item>
- <el-form-item>
- <el-button @click="handleQuery"
- ><Icon icon="ep:search" class="mr-5px" />
- {{ t('file.search') }}</el-button
- >
- <el-button @click="resetQuery"
- ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('file.reset') }}</el-button
- >
- <el-button
- type="success"
- plain
- @click="handleExport"
- :loading="exportLoading"
- v-hasPermi="['rq:iot-device:export']"
- >
- <Icon icon="ep:download" class="mr-5px" /> 导出
- </el-button>
- </el-form-item>
- </el-form>
- </ContentWrap>
- <!-- 列表 -->
- <ContentWrap>
- <el-table v-loading="loading" :data="list" :stripe="true" style="width: 100%" @header-dragend="handleHeaderDragEnd"
- :show-overflow-tooltip="false" :header-cell-style="tableHeaderStyle" ref="tableRef">
- <el-table-column :label="t('monitor.serial')" width="60" align="center" fixed="left">
- <template #default="scope">
- {{ scope.$index + 1 }}
- </template>
- </el-table-column>
- <el-table-column :label="t('iotDevice.code')" align="center" prop="deviceCode" min-width="120"
- :show-overflow-tooltip="true" fixed="left"/>
- <el-table-column :label="t('iotDevice.name')" align="center" prop="deviceName" :min-width="deviceNameMinWidth"
- :width="columnWidths['deviceName']" :show-overflow-tooltip="true"
- :max-width="deviceNameMaxWidth" fixed="left">
- <template #default="scope">
- <el-link :underline="false" type="primary" @click="handleDetail(scope.row.id)">
- {{ scope.row.deviceName }}
- </el-link>
- </template>
- </el-table-column>
- <el-table-column :label="t('bomList.serviceDue')" align="center" min-width="120">
- <template #default="scope">
- <template v-if="hasMaintenancePlan(scope.row.mainDistance)">
- <span :class="getDistanceClass(scope.row.mainDistance)">
- {{ scope.row.mainDistance }}
- </span>
- </template>
- <span v-else>无保养计划</span>
- </template>
- </el-table-column>
- <el-table-column :label="t('iotDevice.dept')" align="center" prop="deptName" min-width="120"/>
- <el-table-column :label="t('monitor.status')" align="center" prop="deviceStatus" min-width="100">
- <template #default="scope">
- <dict-tag :type="DICT_TYPE.PMS_DEVICE_STATUS" :value="scope.row.deviceStatus" />
- </template>
- </el-table-column>
- <!-- 动态分组列 - 修改后结构 -->
- <template v-for="group in dynamicColumns" :key="group.header">
- <el-table-column :label="group.header" align="center" :header-cell-class-name="`group-header-${group.groupIndex}`" min-width="auto">
- <!-- 循环分组内的所有类型 -->
- <template v-for="type in group.types" :key="type">
- <!-- 类型标签列 -->
- <el-table-column :label="getTypeLabel(type)" align="center" min-width="auto">
- <!-- 根据类型渲染对应字段 -->
- <!-- 1. 时间类型字段 -->
- <template v-if="type === 'time'">
- <el-table-column
- :label="t('mainPlan.lastMaintenanceOperationTime')"
- :width="getDynamicColumnWidth(group, type, 'lastRunningTime')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- {{ scope.row.groupBomMap?.[group.header]?.lastRunningTime ?? '-' }}
- </template>
- </el-table-column>
- <el-table-column
- :label="t('mainPlan.nextMaintenanceH')"
- :width="getDynamicColumnWidth(group, type, 'nextMaintenanceH')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- {{ (scope.row.groupBomMap?.[group.header]?.lastRunningTime || 0) +
- (scope.row.groupBomMap?.[group.header]?.nextRunningTime || 0) }}
- </template>
- </el-table-column>
- <el-table-column
- :label="t('operationFillForm.sumTime')"
- :width="getDynamicColumnWidth(group, type, 'totalRunTime')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- {{ scope.row.groupBomMap?.[group.header]?.totalRunTime ?? '-' }}
- </template>
- </el-table-column>
- <el-table-column
- :label="t('mainPlan.remainH')"
- :width="getDynamicColumnWidth(group, type, 'remainH')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- <span :class="getNewRemainingClass(scope.row.groupBomMap?.[group.header], type)">
- {{ getNewRemainingValue(scope.row.groupBomMap?.[group.header], type) }}
- </span>
- </template>
- </el-table-column>
- </template>
- <!-- 2. 日期类型字段 -->
- <template v-else-if="type === 'date'">
- <el-table-column
- :label="t('mainPlan.lastMaintenanceNaturalDate')"
- :width="getDynamicColumnWidth(group, type, 'lastNaturalDate')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- {{ formatDate(scope.row.groupBomMap?.[group.header]?.lastNaturalDate) }}
- </template>
- </el-table-column>
- <el-table-column
- :label="t('mainPlan.nextMaintDate')"
- :width="getDynamicColumnWidth(group, type, 'nextMaintDate')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- {{
- formatDate(
- (scope.row.groupBomMap?.[group.header]?.lastNaturalDate || 0) +
- (scope.row.groupBomMap?.[group.header]?.nextNaturalDate || 0) * 86400000
- )
- }}
- </template>
- </el-table-column>
- <el-table-column
- :label="t('main.currentDate')"
- :width="getDynamicColumnWidth(group, type, 'currentDate')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default>
- {{ currentDateFormatted }}
- </template>
- </el-table-column>
- <el-table-column
- :label="t('mainPlan.remainDay')"
- :width="getDynamicColumnWidth(group, type, 'remainDay')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- <span :class="getNewRemainingClass(scope.row.groupBomMap?.[group.header], type)">
- {{ getNewRemainingValue(scope.row.groupBomMap?.[group.header], type) }}
- </span>
- </template>
- </el-table-column>
- </template>
- <!-- 3. 里程类型字段 -->
- <template v-else-if="type === 'mileage'">
- <el-table-column
- :label="t('mainPlan.lastMaintenanceMileage')"
- :width="getDynamicColumnWidth(group, type, 'lastRunningKilometers')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- {{ scope.row.groupBomMap?.[group.header]?.lastRunningKilometers ?? '-' }}
- </template>
- </el-table-column>
- <el-table-column
- :label="t('mainPlan.nextMaintenanceKm')"
- :width="getDynamicColumnWidth(group, type, 'nextMaintenanceKm')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- {{ (scope.row.groupBomMap?.[group.header]?.lastRunningKilometers || 0) +
- (scope.row.groupBomMap?.[group.header]?.nextRunningKilometers || 0) }}
- </template>
- </el-table-column>
- <el-table-column
- :label="t('operationFillForm.sumKil')"
- :width="getDynamicColumnWidth(group, type, 'totalMileage')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- {{ scope.row.groupBomMap?.[group.header]?.totalMileage ?? '-' }}
- </template>
- </el-table-column>
- <el-table-column
- :label="t('mainPlan.remainKm')"
- :width="getDynamicColumnWidth(group, type, 'remainKm')"
- :show-overflow-tooltip="true"
- align="center">
- <template #default="scope">
- <span :class="getNewRemainingClass(scope.row.groupBomMap?.[group.header], type)">
- {{ getNewRemainingValue(scope.row.groupBomMap?.[group.header], type) }}
- </span>
- </template>
- </el-table-column>
- </template>
- </el-table-column>
- </template>
- </el-table-column>
- </template>
- <el-table-column :label="t('monitor.operation')" align="center" min-width="120" fixed="right">
- <template #default="scope">
- <el-button
- link
- type="primary"
- @click="openBomForm(scope.row)"
- v-hasPermi="['rq:iot-device:query']"
- v-if="hasMaintenancePlan(scope.row.mainDistance)"
- >
- {{ t('monitor.details') }}
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- <!-- 分页 -->
- <Pagination
- :total="total"
- v-model:page="queryParams.pageNo"
- v-model:limit="queryParams.pageSize"
- @pagination="getList"
- />
- </ContentWrap>
- </el-col>
- </el-row>
- <DeviceAlarmBomList ref="modelFormRef" :flag = "flag" />
- </template>
- <script setup lang="ts">
- import download from '@/utils/download'
- import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
- import { IotMainWorkOrderApi, IotMainWorkOrderVO } from '@/api/pms/iotmainworkorder'
- import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
- import DeptTree from '@/views/system/user/DeptTree.vue'
- import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
- import DeviceAlarmBomList from "@/views/pms/iotmainworkorder/DeviceAlarmBomList.vue";
- import dayjs from 'dayjs' // 引入 dayjs 库
- /** 保养查询 列表 保养项分组 */
- defineOptions({ name: 'IotMaintenanceSearch' })
- const message = useMessage() // 消息弹窗
- const { t } = useI18n() // 国际化
- const { push } = useRouter() // 路由跳转
- const modelFormRef = ref()
- const loading = ref(true) // 列表的加载中
- const list = ref<IotDeviceVO[]>([]) // 列表的数据
- const total = ref(0) // 列表的总页数
- const queryParams = reactive({
- pageNo: 1,
- pageSize: 10,
- deviceCode: undefined,
- deviceName: undefined,
- brand: undefined,
- model: undefined,
- deptId: undefined,
- deviceStatus: undefined,
- assetProperty: undefined,
- picUrl: undefined,
- remark: undefined,
- manufacturerId: undefined,
- supplierId: undefined,
- manDate: [],
- nameplate: undefined,
- expires: undefined,
- plPrice: undefined,
- plDate: [],
- plYear: undefined,
- plStartDate: [],
- plMonthed: undefined,
- plAmounted: undefined,
- remainAmount: undefined,
- infoId: undefined,
- infoType: undefined,
- infoName: undefined,
- infoRemark: undefined,
- infoUrl: undefined,
- templateJson: undefined,
- creator: undefined,
- setFlag: ''
- })
- const dynamicColumns = ref<any[]>([]) // 动态列配置
- const queryFormRef = ref() // 搜索的表单
- const flag = ref() // 查询保养计划或保养工单的标识
- const exportLoading = ref(false) // 导出的加载中
- const contentSpan = ref(20)
- const treeShow = ref(true)
- const currentDateFormatted = ref(dayjs().format('YYYY-MM-DD')) // 当前日期格式化
- const tableRef = ref(); // 表格引用
- const columnWidths = ref<Record<string, string>>({}); // 列宽存储
- // 设备名称列的最小和最大宽度
- const deviceNameMinWidth = 150; // 最小宽度
- const deviceNameMaxWidth = 400; // 最大宽度,防止过长破坏布局
- // 表头样式
- const tableHeaderStyle = () => {
- return {
- borderBottom: '2px solid #606266',
- borderRight: '2px solid #606266',
- backgroundColor: '#f5f7fa',
- fontWeight: 'bold',
- whiteSpace: 'nowrap'
- }
- }
- // 处理列宽拖动事件
- const handleHeaderDragEnd = (newWidth: number, oldWidth: number, column: any) => {
- if (column.property) {
- columnWidths.value[column.property] = `${newWidth}px`;
- } else if (column.label) {
- // 处理没有property的列(如动态列)
- columnWidths.value[column.label] = `${newWidth}px`;
- }
- }
- /** 计算文本宽度辅助函数 */
- const getTextWidth = (text: string, fontSize = 14): number => {
- const span = document.createElement('span');
- span.style.visibility = 'hidden';
- span.style.position = 'absolute';
- span.style.whiteSpace = 'nowrap';
- span.style.fontSize = `${fontSize}px`;
- span.innerText = text;
- document.body.appendChild(span);
- const width = span.offsetWidth;
- document.body.removeChild(span);
- return width;
- }
- /** 计算列宽主函数 */
- const calculateColumnWidths = () => {
- const MIN_WIDTH = 100; // 最小列宽
- const PADDING = 20; // 内边距
- const MAX_DEVICE_NAME_WIDTH = deviceNameMaxWidth; // 设备名称最大宽度
- // 静态列配置
- const staticColumns = [
- { prop: 'serial', label: t('monitor.serial') },
- { prop: 'deviceCode', label: t('iotDevice.code') },
- { prop: 'deviceName', label: t('iotDevice.name') },
- { prop: 'mainDistance', label: t('bomList.serviceDue') },
- { prop: 'deptName', label: t('iotDevice.dept') },
- { prop: 'deviceStatus', label: t('monitor.status') },
- { prop: 'operation', label: t('monitor.operation') }
- ];
- const newWidths: Record<string, string> = {};
- // 计算静态列宽度
- staticColumns.forEach(col => {
- const headerWidth = getTextWidth(col.label);
- let maxContentWidth = 0;
- list.value.forEach(item => {
- let value = '';
- if (col.prop === 'serial') {
- value = (list.value.indexOf(item) + 1).toString();
- } else if (col.prop === 'deviceName') {
- // 设备名称特殊处理,考虑链接内容
- value = item.deviceName || '';
- } else {
- value = item[col.prop]?.toString() || '';
- }
- const contentWidth = getTextWidth(value);
- if (contentWidth > maxContentWidth) maxContentWidth = contentWidth;
- });
- // 对设备名称列应用最大宽度限制
- if (col.prop === 'deviceName') {
- maxContentWidth = Math.min(maxContentWidth, MAX_DEVICE_NAME_WIDTH);
- }
- const finalWidth = Math.max(headerWidth, maxContentWidth, MIN_WIDTH) + PADDING;
- newWidths[col.prop] = `${finalWidth}px`;
- });
- // 计算动态列宽度
- dynamicColumns.value.forEach((group, groupIndex) => {
- group.types.forEach(type => {
- const typeLabel = getTypeLabel(type);
- const typeHeaderWidth = getTextWidth(typeLabel);
- // 根据类型定义子列
- const subColumns = [];
- if (type === 'time') {
- subColumns.push(
- { key: 'lastRunningTime', label: t('mainPlan.lastMaintenanceOperationTime') },
- { key: 'nextMaintenanceH', label: t('mainPlan.nextMaintenanceH') },
- { key: 'totalRunTime', label: t('operationFillForm.sumTime') },
- { key: 'remainH', label: t('mainPlan.remainH') }
- );
- } else if (type === 'date') {
- subColumns.push(
- { key: 'lastNaturalDate', label: t('mainPlan.lastMaintenanceNaturalDate') },
- { key: 'nextMaintDate', label: t('mainPlan.nextMaintDate') },
- { key: 'currentDate', label: t('main.currentDate') },
- { key: 'remainDay', label: t('mainPlan.remainDay') }
- );
- } else if (type === 'mileage') {
- subColumns.push(
- { key: 'lastRunningKilometers', label: t('mainPlan.lastMaintenanceMileage') },
- { key: 'nextMaintenanceKm', label: t('mainPlan.nextMaintenanceKm') },
- { key: 'totalMileage', label: t('operationFillForm.sumKil') },
- { key: 'remainKm', label: t('mainPlan.remainKm') }
- );
- }
- // 计算每个子列的宽度
- subColumns.forEach(subCol => {
- const fullKey = `${group.header}-${type}-${subCol.key}`;
- const headerWidth = getTextWidth(subCol.label);
- let maxContentWidth = 0;
- list.value.forEach(item => {
- const groupData = item.groupBomMap?.[group.header];
- let value = '';
- if (subCol.key === 'currentDate') {
- value = currentDateFormatted.value;
- } else if (groupData) {
- // 特殊处理计算字段
- if (subCol.key === 'nextMaintenanceH') {
- value = ((groupData.lastRunningTime || 0) + (groupData.nextRunningTime || 0)).toString();
- }
- else if (subCol.key === 'remainH') {
- value = getNewRemainingValue(groupData, 'time').toString();
- }
- else if (subCol.key === 'nextMaintDate') {
- const nextDate = (groupData.lastNaturalDate || 0) +
- (groupData.nextNaturalDate || 0) * 86400000;
- value = formatDate(nextDate);
- }
- else if (subCol.key === 'remainDay') {
- value = getNewRemainingValue(groupData, 'date').toString();
- }
- else if (subCol.key === 'nextMaintenanceKm') {
- value = ((groupData.lastRunningKilometers || 0) +
- (groupData.nextRunningKilometers || 0)).toString();
- }
- else if (subCol.key === 'remainKm') {
- value = getNewRemainingValue(groupData, 'mileage').toString();
- }
- // 其他字段直接取值
- else {
- value = groupData[subCol.key]?.toString() || '';
- }
- } else {
- value = '-'; // 无数据时显示短横线
- }
- const contentWidth = getTextWidth(value);
- if (contentWidth > maxContentWidth) maxContentWidth = contentWidth;
- });
- const finalWidth = Math.max(headerWidth, maxContentWidth, MIN_WIDTH) + PADDING;
- newWidths[fullKey] = `${finalWidth}px`;
- });
- });
- });
- columnWidths.value = newWidths;
- }
- /** 获取动态列宽 */
- const getDynamicColumnWidth = (group: any, type: string, key: string) => {
- const fullKey = `${group.header}-${type}-${key}`;
- return columnWidths.value[fullKey] || 'auto';
- }
- /** 查询列表 */
- const getList = async () => {
- loading.value = true
- try {
- const data = await IotMainWorkOrderApi.maintenanceSearch(queryParams)
- list.value = data.list.map((item: any) => {
- // 构建分组映射
- const groupBomMap: Record<string, any> = {}
- if (item.groupBomDistances?.length > 0) {
- item.groupBomDistances.forEach((group: any) => {
- // 提取分组名称
- const groupName = group.name?.split('->')[0] || group.name
- groupBomMap[groupName] = group
- })
- }
- return { ...item, groupBomMap }
- })
- total.value = data.total
- // 生成动态列配置
- generateDynamicColumns(data.list)
- // 计算列宽
- nextTick(() => {
- calculateColumnWidths();
- tableRef.value?.doLayout(); // 重新布局表格
- });
- } finally {
- loading.value = false
- }
- }
- /** 生成动态列配置 */
- const generateDynamicColumns = (dataList: any[]) => {
- // 使用Map收集分组和类型:Map<分组名称, Set<类型>>
- const groupTypeMap = new Map<string, Set<string>>()
- dataList.forEach(item => {
- if (item.groupBomDistances?.length > 0) {
- item.groupBomDistances.forEach((group: any) => {
- const groupName = group.name?.split('->')[0] || group.name
- if (!groupName) return
- // 确定分组类型
- let type = ''
- if (group.lastRunningTime != null && group.nextRunningTime != null) {
- type = 'time'
- } else if (group.lastNaturalDate != null && group.nextNaturalDate != null) {
- type = 'date'
- } else if (group.lastRunningKilometers != null && group.nextRunningKilometers != null) {
- type = 'mileage'
- }
- if (!type) return
- if (!groupTypeMap.has(groupName)) {
- groupTypeMap.set(groupName, new Set())
- }
- groupTypeMap.get(groupName)?.add(type)
- })
- }
- })
- // 转换为需要的结构并按固定顺序排序类型
- dynamicColumns.value = Array.from(groupTypeMap.entries()).map(([header, typeSet], index) => ({
- header,
- types: Array.from(typeSet).sort((a, b) => {
- // 固定顺序:time -> date -> mileage
- const order = { 'time': 1, 'date': 2, 'mileage': 3 }
- return (order[a] || 4) - (order[b] || 5)
- }),
- groupIndex: index % 4 // 循环使用4种样式
- }))
- // 计算动态列宽度
- nextTick(calculateColumnWidths);
- }
- /** 获取类型标签 */
- const getTypeLabel = (type: string) => {
- const labels = {
- 'time': t('main.maintenanceRuntime'),
- 'date': t('main.maintenanceDate'),
- 'mileage': t('main.maintenanceMileage')
- }
- return labels[type] || type
- }
- /** 格式化日期 */
- const formatDate = (timestamp: number | null) => {
- if (!timestamp) return '-'
- const date = new Date(timestamp)
- return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
- }
- /** 新的剩余值计算方法 */
- const getNewRemainingValue = (group: any, type: string) => {
- if (!group) return '-'
- switch (type) {
- case 'time':
- // 剩余时长 = nextRunningTime - (totalRunTime - lastRunningTime)
- const totalRunTime = group.totalRunTime || 0
- const lastRunningTime = group.lastRunningTime || 0
- const nextRunningTime = group.nextRunningTime || 0
- return (nextRunningTime - (totalRunTime - lastRunningTime)).toFixed(2)
- case 'mileage':
- // 剩余里程 = nextRunningKilometers - (totalMileage - lastRunningKilometers)
- const totalMileage = group.totalMileage || 0
- const lastRunningKilometers = group.lastRunningKilometers || 0
- const nextRunningKilometers = group.nextRunningKilometers || 0
- return (nextRunningKilometers - (totalMileage - lastRunningKilometers)).toFixed(2)
- case 'date':
- // 计算剩余天数
- if (!group.lastNaturalDate || !group.nextNaturalDate) return '-'
- try {
- // 上次保养日期:将时间戳转换为 Day.js 对象
- const lastNaturalDate = dayjs(group.lastNaturalDate)
- // 计算下次保养日期
- const nextMaintenanceDate = lastNaturalDate.add(group.nextNaturalDate, 'day')
- // 计算剩余天数(当前日期到下次保养日期的天数差)
- return nextMaintenanceDate.diff(dayjs(), 'day')
- } catch (e) {
- console.error('日期计算错误:', e)
- return '-'
- }
- default:
- return '-'
- }
- }
- /** 获取剩余值 */
- const getRemainingValue = (group: any, type: string) => {
- if (!group) return '-'
- switch (type) {
- case 'time':
- return group.nextRunningTime ?? '-'
- case 'date':
- return group.nextNaturalDate ?? '-'
- case 'mileage':
- return group.nextRunningKilometers ?? '-'
- default:
- return '-'
- }
- }
- /** 新的剩余值样式 */
- const getNewRemainingClass = (group: any, type: string) => {
- const value = getNewRemainingValue(group, type)
- if (value === '-' || value === null) return ''
- const numValue = typeof value === 'string' ? parseFloat(value) : value
- return numValue < 0 ? 'negative-distance' :
- numValue > 0 ? 'positive-distance' : ''
- }
- /** 获取剩余值样式 */
- const getRemainingClass = (group: any, type: string) => {
- const value = getRemainingValue(group, type)
- if (value === '-' || value === null) return ''
- const numValue = typeof value === 'string' ? parseFloat(value) : value
- return numValue < 0 ? 'negative-distance' :
- numValue > 0 ? 'positive-distance' : ''
- }
- /** 判断是否有分组数据 */
- const hasGroupBomDistances = (row: any) => {
- return row.groupBomDistances?.length > 0
- }
- /** 处理部门被点击 */
- const handleDeptNodeClick = async (row) => {
- queryParams.deptId = row.id
- await getList()
- }
- /** 搜索按钮操作 */
- const handleQuery = () => {
- queryParams.pageNo = 1
- getList()
- }
- /** 重置按钮操作 */
- const resetQuery = () => {
- queryFormRef.value.resetFields()
- handleQuery()
- }
- const getDistanceClass = (distance: number | string | null) => {
- if (distance === null || distance === undefined) return '';
- // 如果是数字类型,直接处理
- if (typeof distance === 'number') {
- return distance < 0 ? 'negative-distance' :
- distance > 0 ? 'positive-distance' : '';
- }
- // 如果是字符串,提取数字部分
- if (typeof distance === 'string') {
- // 使用正则提取数字部分(包括负号、小数点和科学计数法)
- const numericPart = distance.match(/[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?/)?.[0];
- // 如果提取到数字部分,转换为数值
- if (numericPart) {
- const num = parseFloat(numericPart);
- return num < 0 ? 'negative-distance' :
- num > 0 ? 'positive-distance' : '';
- }
- }
- return '';
- };
- // 判断是否有保养计划
- const hasMaintenancePlan = (mainDistance: any) => {
- // 检查:非null、非undefined、非空字符串
- return mainDistance != null && mainDistance !== '';
- };
- const handleDetail = (id: number) => {
- push({ name: 'DeviceDetailInfo', params: { id } })
- }
- const drawerVisible = ref<boolean>(false)
- const showDrawer = ref()
- const openBomForm = async (row) => {
- if (row.workOrderId) {
- flag.value = 'workOrder';
- modelFormRef.value.open(row.workOrderId, flag.value, row.id)
- } else if (row.planId) {
- flag.value = 'plan';
- modelFormRef.value.open(row.planId, flag.value, row.id)
- }
- }
- /** 导出按钮操作 */
- const handleExport = async () => {
- try {
- // 导出的二次确认
- await message.exportConfirm()
- // 发起导出
- exportLoading.value = true
- const data = await IotDeviceApi.exportIotDevice(queryParams)
- download.excel(data, '设备台账.xls')
- } catch {
- } finally {
- exportLoading.value = false
- }
- }
- const { wsCache } = useCache()
- /** 初始化 **/
- onMounted(() => {
- getList()
- })
- </script>
- <style scoped>
- /* 正数样式 - 淡绿色 */
- .positive-distance {
- color: #67c23a; /* element-plus 成功色 */
- background-color: rgba(103, 194, 58, 0.1); /* 10% 透明度的淡绿色背景 */
- padding: 2px 8px;
- border-radius: 4px;
- display: inline-block;
- }
- /* 负数样式 - 淡红色 */
- .negative-distance {
- color: #f56c6c; /* element-plus 危险色 */
- background-color: rgba(245, 108, 108, 0.1); /* 10% 透明度的淡红色背景 */
- padding: 2px 8px;
- border-radius: 4px;
- display: inline-block;
- }
- /* 分组表头样式 - 使用::v-deep穿透 */
- :deep(.group-header-0) {
- background-color: #f0f9ff !important; /* 淡蓝色 */
- border-right: 2px solid #606266 !important;
- }
- :deep(.group-header-1) {
- background-color: #fdf6ec !important; /* 淡橙色 */
- border-right: 2px solid #606266 !important;
- }
- :deep(.group-header-2) {
- background-color: #f0f4ff !important; /* 淡紫色 */
- border-right: 2px solid #606266 !important;
- }
- :deep(.group-header-3) {
- background-color: #e6f7ff !important; /* 淡青色 */
- border-right: 2px solid #606266 !important;
- }
- /* 表头边框加深 */
- :deep(.el-table th) {
- border-bottom: 2px solid #606266 !important;
- border-right: 2px solid #606266 !important;
- }
- /* 内容不换行 */
- :deep(.el-table .cell) {
- white-space: nowrap !important;
- }
- /* 表头文字不换行 */
- :deep(.el-table__header .cell) {
- white-space: nowrap !important;
- overflow: visible !important;
- text-overflow: unset !important;
- }
- /* 列宽自适应调整 */
- :deep(.el-table) {
- table-layout: auto !important;
- }
- /* 分组列之间的分隔线 */
- :deep(.el-table__header .el-table__cell) {
- overflow: visible !important;
- border-right: 2px solid #606266 !important;
- }
- /* 最后列去除右边框 */
- :deep(.el-table__header .el-table__cell:last-child) {
- border-right: none !important;
- }
- /* 设备名称列特殊处理 */
- :deep(.el-table__body .device-name-cell .cell) {
- overflow: hidden;
- text-overflow: ellipsis;
- }
- </style>
|