bonus-ui/src/views/EquipmentLedger/index.vue

2357 lines
76 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container" v-loading="tableLoading">
<el-row :gutter="24" class="main-content" type="justify">
<el-col :span="collapsed ? 0.5 : 4" class="tree-column" style="">
<div class="card-container" v-if="!collapsed" style="min-width: 100%">
<div class="card-header" v-if="!collapsed">
<el-form
ref="queryFormLeft"
v-show="showSearch"
:model="queryParams"
:rules="queryRules"
@submit.native.prevent
style="width: 100%"
>
<el-form-item prop="name" style="margin-bottom: unset">
<el-input
v-model="treeSearchKey"
placeholder="搜索"
@keyup.enter.native="getDeviceListByTreeSearch"
@input="getDeviceListByTreeSearch"
>
<template #suffix>
<div class="tree-search-buttons">
<el-button
icon="el-icon-search"
size="mini"
type="text"
@click="getDeviceListByTreeSearch"
title="查询"
@keyup.enter.native="getDeviceListByTreeSearch"
style="margin-left: 10px; font-size: 16px; color: #c0c4cc"
/>
<el-button
icon="el-icon-refresh"
size="mini"
@click="resetTreeSearch"
title="重置"
type="text"
style="margin-left: 10px; font-size: 16px; color: #c0c4cc"
/>
</div>
</template>
</el-input>
</el-form-item>
</el-form>
</div>
<!-- 树组件,折叠时隐藏 -->
<el-tree
v-show="!collapsed"
:data="treeData"
:props="defaultProps"
:indent="6"
@node-click="handleNodeClick"
:default-expanded-keys="defaultExpandedKeys"
node-key="id"
class="tree-container"
:loading="treeLoading"
/>
</div>
<div style="position: relative; width: 20px; height: calc(100vh - 130px); margin-left: 5px">
<!-- 右侧折叠/展开按钮 -->
<el-button
icon="el-icon-d-arrow-left"
size="mini"
@click="toggleCollapse"
class="collapse-btn"
:icon="collapsed ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'"
/>
</div>
</el-col>
<!-- 右侧列表和查询区域 -->
<el-col :span="collapsed ? 23 : 20" style="height: calc(100vh - 130px); transition: all 0.3s ease">
<div class="card-container">
<div class="card-header">
<!-- 搜索区域展开/收起按钮 -->
<div class="status-stats">
<div class="stat-card">
<div class="stat-content">
<div class="stat-row">
<div class="stat-value">{{ inStock }}</div>
<img src="@/assets/images/ledger/icon-instock.png" alt="在库数量" class="stat-icon"/>
</div>
<div class="stat-label">在库数量</div>
</div>
</div>
<div class="stat-card">
<div class="stat-content">
<div class="stat-row">
<div class="stat-value">{{ ownUse }}</div>
<img src="@/assets/images/ledger/icon-ownuse.png" alt="自用数量" class="stat-icon"/>
</div>
<div class="stat-label">自用数量</div>
</div>
</div>
<div class="stat-card">
<div class="stat-content">
<div class="stat-row">
<div class="stat-value">{{ share }}</div>
<img src="@/assets/images/ledger/icon-share.png" alt="共享数量" class="stat-icon"/>
</div>
<div class="stat-label">共享数量</div>
</div>
</div>
<div class="stat-card">
<div class="stat-content">
<div class="stat-row">
<div class="stat-value">{{ underRepair }}</div>
<img src="@/assets/images/ledger/icon-maintenance.png" alt="维修数量" class="stat-icon"/>
</div>
<div class="stat-label">维修数量</div>
</div>
</div>
<div class="stat-card">
<div class="stat-content">
<div class="stat-row">
<div class="stat-value">{{ totalValue }}万</div>
<img src="@/assets/images/ledger/icon-instock-value.png" alt="在库资产总价值" class="stat-icon"/>
</div>
<div class="stat-label">在库资产总价值</div>
</div>
</div>
<div class="stat-card">
<div class="stat-content">
<div class="stat-row">
<div class="stat-value">{{ retirementValue }}万</div>
<img src="@/assets/images/ledger/icon-retired-value.png" alt="退役资产总价值" class="stat-icon"/>
</div>
<div class="stat-label">退役资产总价值</div>
</div>
</div>
</div>
</div>
</div>
<div class="status-operation-bar" style="margin-top: 15px;margin-bottom: 2px;">
<el-form
ref="queryForm"
label-width="120px"
v-show="showSearch"
:model="queryParams"
:rules="queryRules"
style="margin-top: 10px;width: 100%;"
>
<!-- 始终显示的查询条件 -->
<el-row :gutter="20">
<!-- <el-col :span="6">
<el-form-item label="所属省份" prop="province">
<el-select
v-model="queryParams.province"
placeholder="请选择所属省份"
clearable
filterable
style="width: 100%"
>
<el-option v-for="item in provinceList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col> -->
<el-col :span="8" v-if="hasChildrenPropertyUnit">
<el-form-item label="产权单位" prop="propertyUnitIds">
<el-cascader
clearable
v-model="queryParams.propertyUnitIds"
placeholder="请选择产权单位"
:options="propertyUnitList"
:props="{ label: 'label', value: 'id', children: 'children', checkStrictly: true }"
style="width: 100%"
@change="handleUnitChange"
/>
<!-- <el-select
v-model="queryParams.propertyUnitId"
placeholder="请选择产权单位"
clearable
filterable
style="width: 100%"
>
<el-option v-for="item in propertyUnitList" :key="item.id" :label="item.label" :value="item.id" />
</el-select> -->
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="装备名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入装备名称" clearable/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="规格型号" prop="specificationModel">
<el-input v-model="queryParams.specificationModel" placeholder="请输入规格型号" clearable/>
</el-form-item>
</el-col>
</el-row>
<!-- 可折叠的查询条件 -->
<div v-if="searchExpanded">
<el-row :gutter="15">
<el-col :span="8">
<el-form-item label="专业" prop="majorId">
<el-select
v-model="queryParams.majorId"
placeholder="请选择专业"
clearable
@change="majorChange"
style="width: 100%"
>
<el-option v-for="item in majorList" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="工序" prop="process">
<el-cascader
clearable
v-model="queryParams.process"
placeholder="请选择工序"
:options="processList"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="装备状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择装备状态" clearable style="width: 100%">
<el-option label="在库" value="1"></el-option>
<el-option label="自用" value="2"></el-option>
<el-option label="共享" value="3"></el-option>
<el-option label="退役" value="4"></el-option>
<el-option label="维修" value="5"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="装备编码" prop="code">
<el-input v-model="queryParams.code" placeholder="请输入装备编码" clearable/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="生产日期" prop="productionDate">
<el-date-picker
v-model="queryParams.productionDate"
type="daterange"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
clearable
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="采购日期" prop="purchaseDate">
<el-date-picker
v-model="queryParams.purchaseDate"
type="daterange"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
clearable
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-col :span="8">
<el-form-item label="资产原值(万元)" prop="originalValue">
<el-input-number
v-model="queryParams.minOriginalValue"
placeholder="资产原值(万元)"
clearable
:controls="false"
:min="0"
style="width: 100%"
/>
<span> - </span>
<el-input-number
v-model="queryParams.maxOriginalValue"
placeholder="资产原值(万元)"
clearable
:controls="false"
:min="0"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="原始编码" prop="originalCode">
<el-input v-model="queryParams.originalCode" placeholder="请输入装备原始编码" clearable/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="使用年限" prop="serviceLife">
<el-input v-model="queryParams.serviceLife" placeholder="请输入使用年限" clearable/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="生产厂商" prop="manufacturer">
<el-select
v-model="queryParams.manufacturer"
placeholder="请选择生产厂商"
clearable
style="width: 100%"
>
<el-option v-for="item in manufacturerList" :key="item.id" :label="item.label" :value="item.id"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="特征项" prop="featureItem">
<el-input v-model="queryParams.featureItem" placeholder="请输入特征项" clearable/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="特征值" prop="featureValue">
<el-input v-model="queryParams.featureValue" placeholder="请输入特征值" clearable/>
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
</div>
<!-- 状态统计和操作按钮 - 固定定位 -->
<div class="status-operation-bar sticky-bar">
<div class="operation-buttons">
<el-button type="text" size="mini" style="float: right" @click="toggleSearch" class="search-toggle-btn">
<i :class="searchExpanded ? 'el-icon-up' : 'el-icon-down'"></i>
{{ searchExpanded ? '收起' : '展开' }}
</el-button>
<el-button type="primary" style="float: right" icon="el-icon-search" size="mini" @click="getDeviceList">
查询
</el-button>
<el-button icon="el-icon-refresh" style="float: right; margin-left: 20px" size="mini" @click="resetQuery">
重置
</el-button>
</div>
</div>
<!-- 装备列表表格 -->
<div class="card-container content-box">
<el-row :gutter="10" class="mb8" style="display: flex; align-items: center;border-bottom: 1px solid #e4e7ed;">
<el-col :span="8">
<div class="card-header" style="border-bottom: 0px;">
<div>
<span class="table-title">装备列表 </span>
<span class="table-count" style="margin-left: 35px;"> 共 {{ total }} 条记录</span>
</div>
</div>
</el-col>
<el-col :span="16">
<right-toolbar
:showSearch.sync="showSearch"
@queryTable="getDeviceList"
:columns="columns2"
></right-toolbar>
<el-dropdown size="mini" style="float: right;margin-right: 10px;" placement="bottom-start" trigger="click" @visible-change="handleVisibleChange" @command="(command) => handleCommand(command)">
<el-button plain size="mini" class="el-dropdown-link">
导出 <i class="el-icon-arrow-up el-icon--right" :class="{ 'rotate': isDropdownOpen }"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="handleDownloadPage">导出当前页</el-dropdown-item>
<el-dropdown-item command="handleDownloadAll">导出全部</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<!-- <span>
<el-button
style="float: right;background-color:#fff;border: 1px solid #d9d9d9;color:rgba(0,0,0,.65)"
type="primary"
size="mini"
@click="exportData"
>
<svg-icon icon-class="download"/> 导出
</el-button>
</span> -->
</el-col>
</el-row>
<el-table
ref="tableRef"
:data="tableData"
:key="Date.now()"
@selection-change="handleSelectionChange"
border
stripe
fit
:auto-resize="true"
height="546"
:cell-style="{ 'white-space': 'normal', 'line-height': '1.4' }"
:header-cell-style="{ 'line-height': '1.4' }"
>
<el-table-column align="center" show-overflow-tooltip type="selection" width="50"/>
<el-table-column align="center" show-overflow-tooltip type="index" fixed="left" label="序号" width="50"/>
<el-table-column
v-for="(item, index) in columns2"
v-if="item.visible"
:key="index"
align="center"
:width="item.width || 'auto'"
:fixed="item.fixed"
:label="item.label"
:prop="item.prop"
>
<template v-slot="{ row }" v-if="item.prop == 'process'">
<span>{{ row.mainProcess }}{{ row.subProcess ? '>' + row.subProcess : '' }}</span>
</template>
<template v-slot="{ row }" v-else-if="item.prop == 'devType'">
<span>
{{ row.mainCategory ? row.mainCategory + '>' : '' }}
{{ row.branch ? row.subCategory : row.subCategory }}
<!-- {{ row.branch }}-->
</span>
</template>
<template v-slot="{ row }" v-else-if="item.prop == 'upDownStatus'">
<el-tag v-if="row.upDownStatus == 1" size="mini">上架</el-tag>
<el-tag v-if="row.upDownStatus == 0" size="mini" type="danger">下架</el-tag>
</template>
<template v-slot="{ row }" v-else-if="item.prop == 'status'">
<el-tag v-if="row.status == 1" type="success" size="mini">在库</el-tag>
<el-tag v-if="row.status == 2" size="mini">自用</el-tag>
<el-tag v-if="row.status == 3" size="mini">共享</el-tag>
<el-tag v-if="row.status == 4" size="mini" type="danger">退役</el-tag>
<el-tag v-if="row.status == 5" size="mini" type="warning">维修</el-tag>
</template>
<template v-slot="{ row }" v-else-if="item.prop == 'appearanceImages'">
<span style="color: #00a288; cursor: pointer" @click="handleView(row, 1)">查看</span>
</template>
<template v-slot="{ row }" v-else-if="item.prop == 'certificates'">
<span style="color: #2CBAB2; cursor: pointer" @click="handleView(row, 2)">查看</span>
</template>
<template v-slot="{ row }" v-else-if="item.prop == 'inspectionReports'">
<span style="color: #2CBAB2; cursor: pointer" @click="handleView(row, 3)">查看</span>
</template>
<template v-slot="{ row }" v-else-if="item.prop == 'purchaseInvoices'">
<span style="color: #2CBAB2; cursor: pointer" @click="handleView(row, 4)">查看</span>
</template>
<template v-slot="{ row }" v-else-if="item.prop == 'fileImgList'">
<span style="color: #2CBAB2; cursor: pointer" @click="handleFileListUpload(row)">报告管理</span>
</template>
<template v-slot="{ row }" v-else-if="/^feature(Item|Value)\d+$/.test(item.prop)">
<span>
{{ getFeatureValue(row, item.prop) }}
</span>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" width="150" label="操作">
<template slot-scope="scope">
<el-button type="text" @click="onHandlePreview(scope.row)">
查看
</el-button>
<el-button type="text" @click="onHandleEdit(scope.row)"> 编辑</el-button>
<el-button type="text" @click="onHandleDelete(scope.row)" style="color: #FF5129">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getDeviceList"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-col>
</el-row>
<!-- 查看弹窗 -->
<el-dialog
:title="`查看装备 - ${formData.name || ''}`"
:visible.sync="previewDialogVisible"
width="70%"
:close-on-click-modal="false"
:before-close="handlePreviewClose"
>
<el-form
ref="previewForm"
:model="formData"
:rules="formRules"
label-width="120px"
size="small"
class="detail-form"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="公司名称" prop="companyName">
<el-input v-model="formData.companyName" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="施工主工序" prop="mainGx">
<el-input v-model="formData.mainGx" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="施工子工序" prop="childGx">
<el-input v-model="formData.childGx" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备大类" prop="devCategory">
<el-input v-model="formData.devCategory" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备小类" prop="devSubcategory">
<el-input v-model="formData.devSubcategory" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备名称" prop="name">
<el-input v-model="formData.name" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格型号" prop="devModel">
<el-input v-model="formData.devModel" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备编号" prop="code">
<el-input v-model="formData.code" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备状态" prop="changeStatus">
<el-select v-model="formData.changeStatus" disabled>
<el-option label="在库" value="1"></el-option>
<el-option label="在用" value="2"></el-option>
<el-option label="共享" value="3"></el-option>
<el-option label="退役" value="4"></el-option>
<el-option label="维修" value="5"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="上下架状态" prop="upDownStatus">
<el-select v-model="formData.upDownStatus" disabled>
<el-option label="上架" value="1"></el-option>
<el-option label="下架" value="0"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="使用到期时间" prop="expireTime">
<el-input v-model="formData.expireTime" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计数单位" prop="unitName">
<el-input v-model="formData.unitName" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产权单位" prop="propertyCompany">
<el-input v-model="formData.propertyCompany" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="资产原值(万元)" prop="buyPrice">
<el-input v-model="formData.buyPrice" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="生产日期" prop="productionDate">
<el-input v-model="formData.productionDate" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="使用年限" prop="workingHours">
<el-input v-model="formData.workingHours" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所在项目" prop="onProject">
<el-input v-model="formData.onProject" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系人" prop="person">
<el-input v-model="formData.person" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式" prop="personPhone">
<el-input v-model="formData.personPhone" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="下次维保日期" prop="nextCheckTime">
<el-input v-model="formData.nextCheckTime" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="生产厂商" prop="brand">
<el-input v-model="formData.brand" disabled/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handlePreviewClose">关闭</el-button>
</div>
</el-dialog>
<!-- 编辑弹窗 -->
<!-- 编辑弹窗 -->
<el-dialog
:title="`编辑装备 - ${formData.name || ''}`"
:visible.sync="editDialogVisible"
width="70%"
:close-on-click-modal="false"
:before-close="handleEditClose"
>
<el-form ref="editForm" :model="formData" :rules="formRules" label-width="120px" size="small" class="detail-form">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="公司名称" prop="companyName">
<el-input v-model="formData.companyName"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="施工主工序" prop="mainGx">
<el-input v-model="formData.mainGx"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="施工子工序" prop="childGx">
<el-input v-model="formData.childGx"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备大类" prop="devCategory">
<el-input v-model="formData.devCategory"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备小类" prop="devSubcategory">
<el-input v-model="formData.devSubcategory"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格型号" prop="devModel">
<el-input v-model="formData.devModel"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备编号" prop="code">
<el-input v-model="formData.code" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="装备状态" prop="changeStatus">
<el-select v-model="formData.changeStatus" placeholder="请选择装备状态">
<el-option label="在库" value="1"></el-option>
<el-option label="在用" value="2"></el-option>
<el-option label="共享" value="3"></el-option>
<el-option label="退役" value="4"></el-option>
<el-option label="维修" value="5"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="上下架状态" prop="upDownStatus">
<el-select v-model="formData.upDownStatus" placeholder="请选择上下架状态">
<el-option label="上架" value="1"></el-option>
<el-option label="下架" value="0"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="使用到期时间" prop="expireTime">
<el-date-picker
v-model="formData.expireTime"
type="date"
placeholder="选择使用到期时间"
value-format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计数单位" prop="unitName">
<el-input v-model="formData.unitName"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产权单位" prop="propertyCompany">
<el-input v-model="formData.propertyCompany"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="资产原值(万元)" prop="buyPrice">
<el-input v-model="formData.buyPrice" type="number"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出厂日期" prop="productionDate">
<el-date-picker
v-model="formData.productionDate"
type="date"
placeholder="选择出厂日期"
value-format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="使用年限" prop="workingHours">
<el-input v-model="formData.workingHours" type="number"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所在项目" prop="onProject">
<el-input v-model="formData.onProject"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系人" prop="person">
<el-input v-model="formData.person"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式" prop="personPhone">
<el-input v-model="formData.personPhone"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="下次维保日期" prop="nextCheckTime">
<el-date-picker
v-model="formData.nextCheckTime"
type="date"
placeholder="选择下次维保日期"
value-format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="生产厂商" prop="brand">
<el-input v-model="formData.brand"/>
</el-form-item>
</el-col>
<el-col :span="12"></el-col>
<!-- 添加特征项按钮 -->
<el-col :span="24" style="text-align: center; margin-top: 10px">
<el-button
type="text"
size="small"
@click="addFeatureItem"
:disabled="formData.propertyVoList.length >= tableColumns"
>
<i class="el-icon-plus"></i> 添加特征项
</el-button>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleEditClose">取消</el-button>
<el-button type="primary" @click="submitEditForm">保存</el-button>
</div>
</el-dialog>
<!-- 弹框 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" append-to-body width="40%">
<el-table :data="dialogList" fit highlight-current-row style="width: 100%" stripe>
<el-table-column type="index" width="55" label="序号" align="center"/>
<el-table-column label="附件名称" prop="fileName" align="left">
<!-- 插槽 -->
<template v-slot="{ row }">
<span style="color: #2CBAB2; cursor: pointer" @click="handleFile(row)">{{ row.fileName }}</span>
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">关 闭</el-button>
</span>
</el-dialog>
<!-- 弹框 -->
<el-dialog
:title="dialogTitle"
:visible.sync="dialogCodeVisible"
width="20%"
@opened="handleDialogOpened"
center
>
<!-- 二维码容器:水平居中 -->
<div class="qrcode-box">
<QrcodeGenerator
ref="qr3"
:value="dialogRow"
errorCorrectionLevel="H"
:size="250"
/>
</div>
<!-- 底部按钮:新增打印、下载 -->
<span slot="footer" class="dialog-footer">
<el-button type="primary" size="mini" @click="downloadQrcode">下载二维码</el-button>
<el-button @click="dialogCodeVisible = false">关 闭</el-button>
</span>
</el-dialog>
<!-- 报告管理 -->
<el-dialog title="报告管理" :visible.sync="openReport" width="550px" class="center-dialog">
<el-table :data="fileDataList" width="100%" stripe>
<el-table-column label="序号" type="index" width="55" align="center"/>
<el-table-column label="报告类型" align="left" header-align="left" prop="dictLabel"
:show-overflow-tooltip="true"
>
<template v-slot="{ row }">
<span v-if="row.type == 1 || row.type == 2 || row.type == 3">
<span style="color: red">*</span>
{{ row.dictLabel }}
</span>
<span v-else>{{ row.dictLabel }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template slot-scope="{ row, $index }">
<div style="display: flex; align-items: center; justify-content: center">
<el-button
v-if="
(row.type == 1 && currentRow.appearanceImages.length > 0) ||
(row.type == 2 && currentRow.certificates.length > 0) ||
(row.type == 3 && currentRow.inspectionReports.length > 0) ||
(row.type == 4 && currentRow.purchaseInvoices.length > 0)
"
size="mini"
type="text"
@click="handleView(currentRow, row.type)"
>
查看
</el-button>
<el-button
v-if="row.type == 5"
size="mini"
type="text"
@click="handleView(currentRow, row.type)"
>
查看
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</el-dialog>
<AddEquip ref="addEquipRef" @getList="getDeviceList"/>
</div>
</template>
<script>
import {
getDeviceTreeAPI,
getDeviceListAPI,
updateDeviceUpDownAPI,
getDeviceStatusCountAPI,
delDevice,
updateDeviceAPI,
getDeviceDetailAPI,
getManufacturerSelectApi
} from '@/api/EquipmentLedger/index.js'
import QrcodeGenerator from '@/components/QrcodeGenerator'
import { firstLevel, secondAndThirdLevel } from '@/api/EquipmentEntryApply'
import { getMaxFeatureAPI, getProvinceListAPI } from '@/api/EquipmentLedger/equ-out.js'
import { deptTreeSelect } from '@/api/system/user'
import AddEquip from '@/views/stockManagement/entryApply/components/AddEquip'
export default {
name: 'EquipmentLedger',
components: { AddEquip, QrcodeGenerator },
data() {
return {
treeSearchKey: '', //用于存储左侧树搜索框内容
showSearch: true,
total: 0,
tableHeight: 500,
collapsed: true,
// 查询参数
queryParams: {
province: undefined, // 省份
propertyUnitId: undefined, // 产权单位
propertyUnitIds: [],
name: undefined, // 装备名称
specificationModel: undefined, // 规格型号
major: undefined, // 专业
process: undefined, // 工序
status: undefined, // 状态
code: undefined, // 编码
productionDate: undefined, // 生产日期
purchaseDate: undefined, // 采购日期
minOriginalValue: undefined, // 资产原值
maxOriginalValue: undefined, // 资产原值
originalCode: undefined, // 装备原始编码
serviceLife: undefined, // 使用年限
manufacturer: undefined, // 厂家
featureItem: undefined, // 特征项
featureValue: undefined, // 特征值
pageNum: 1,
pageSize: 10
},
// 查询表单验证规则
queryRules: {},
// 树形结构配置
defaultProps: {
children: 'children',
label: 'name'
},
treeData: [],
defaultExpandedKeys: [], // 存储默认展开的节点 key
provinceList: [], // 所属省份
propertyUnitList: [], // 产权单位
majorList: [], // 专业
processList: [], // 工序
serviceLifeList: [], // 使用年限
manufacturerList: [], // 生产厂家
columns2: [],
columns: [
{ key: 1, label: `产权单位`, prop: 'propertyUnit', visible: true, width: 150 }, // 自动适配
{ key: 2, label: `专业`, prop: 'major', visible: true, width: 150 },
{ key: 3, label: `工序`, prop: 'process', visible: true, width: 180 },
{ key: 4, label: `装备类目`, prop: 'devType', visible: true, width: 180 },
{ key: 5, label: `装备名称`, prop: 'name', visible: true, fixed: 'left', width: 150 }, // 自定义宽度
{ key: 8, label: `装备状态`, prop: 'status', visible: true, fixed: 'left', width: 100 },
{ key: 6, label: `规格型号`, prop: 'specificationModel', visible: true, width: 150 },
{ key: 7, label: `装备编码`, prop: 'code', visible: true, width: 150 },
{ key: 9, label: `使用年限`, prop: 'serviceLife', visible: true, width: 100 },
{ key: 10, label: `使用项目`, prop: 'usingProject', visible: true, width: 180 },
{ key: 11, label: `使用到期时间`, prop: 'expirationTime', visible: true, width: 120 },
{ key: 12, label: `使用次数`, prop: 'usageCount', visible: true, width: 100 },
{ key: 13, label: `维修次数`, prop: 'repairCount', visible: true, width: 100 },
{ key: 14, label: `装备原始编码`, prop: 'originalCode', visible: true, width: 150 },
{ key: 16, label: `生产厂家`, prop: 'manufacturer', visible: true, width: 150 },
{ key: 17, label: `出厂日期`, prop: 'productionDate', visible: true, width: 120 },
{ key: 18, label: `采购日期`, prop: 'purchaseDate', visible: true, width: 120 },
{ key: 19, label: `资产原值(万元)`, prop: 'originalValue', visible: true, width: 120 },
{ key: 19, label: `资产净值(万元)`, prop: 'value', visible: true, width: 120 },
{ key: 20, label: `最大使用年限(年)`, prop: 'maxServiceLifeYears', visible: true, width: 120 },
{ key: 21, label: `下次维保日期`, prop: 'nextMaintenanceDate', visible: true, width: 120 },
{ key: 22, label: `相关配套资料`, prop: 'fileImgList', visible: true, width: 150 },
// 特征项/值列:内容短,指定窄宽度
{ key: 26, label: `特征项1`, prop: 'featureItem1', visible: true, width: 100 },
{ key: 27, label: `特征值1`, prop: 'featureValue1', visible: true, width: 100 },
{ key: 28, label: `特征项2`, prop: 'featureItem2', visible: true, width: 100 },
{ key: 29, label: `特征值2`, prop: 'featureValue2', visible: true, width: 100 },
// 其余特征项/值列同理,按需指定宽度
{ key: 30, label: `特征项3`, prop: 'featureItem3', visible: true, width: 100 },
{ key: 31, label: `特征值3`, prop: 'featureValue3', visible: true, width: 100 },
{ key: 32, label: `特征项4`, prop: 'featureItem4', visible: true, width: 100 },
{ key: 33, label: `特征值4`, prop: 'featureValue4', visible: true, width: 100 },
{ key: 34, label: `特征项5`, prop: 'featureItem5', visible: true, width: 100 },
{ key: 35, label: `特征值5`, prop: 'featureValue5', visible: true, width: 100 },
{ key: 36, label: `特征项6`, prop: 'featureItem6', visible: true, width: 100 },
{ key: 37, label: `特征值6`, prop: 'featureValue6', visible: true, width: 100 },
{ key: 38, label: `特征项7`, prop: 'featureItem7', visible: true, width: 100 },
{ key: 39, label: `特征值7`, prop: 'featureValue7', visible: true, width: 100 },
{ key: 40, label: `特征项8`, prop: 'featureItem8', visible: true, width: 100 },
{ key: 41, label: `特征值8`, prop: 'featureValue8', visible: true, width: 100 },
{ key: 42, label: `特征项9`, prop: 'featureItem9', visible: true, width: 100 },
{ key: 43, label: `特征值9`, prop: 'featureValue9', visible: true, width: 100 }
],
tableData: [],
tableColumns: 0,
selectedRows: [],
// 状态统计数据
inStock: 0,
ownUse: 0,
share: 0,
underRepair: 0,
totalValue: 0, // 在库资产总价值
retirementValue: 0, // 退役资产总价值
// 加载状态
treeLoading: false,
tableLoading: false,
// 弹窗相关
previewDialogVisible: false,
editDialogVisible: false,
showFieldSetting: false, // 字段设置弹窗控制
// 搜索区域展开状态
searchExpanded: false,
selectedColumns: [],
// 表单数据
formData: {
maId: '',
companyName: '',
mainGx: '',
childGx: '',
devCategory: '',
devSubcategory: '',
name: '',
devModel: '',
code: '',
changeStatus: '',
upDownStatus: '',
expireTime: '',
unitName: '',
propertyCompany: '',
buyPrice: '',
productionDate: '',
workingHours: '',
onProject: '',
person: '',
personPhone: '',
nextCheckTime: '',
brand: '',
propertyVoList: []
},
// 表单验证规则
formRules: {
name: [{ required: true, message: '请输入装备名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入装备编号', trigger: 'blur' }],
changeStatus: [{ required: true, message: '请选择装备状态', trigger: 'change' }]
},
maxFeatureCount: 5, // 最大特征项数量限制
dialogVisible: false,
dialogCodeVisible: false,
dialogTitle: '',
dialogList: [],
dialogRow: {},
currentRow: {},
openReport: false,
fileDataList: [
{ dictLabel: '装备外观', type: '1' },
{ dictLabel: '合格证', type: '2' },
{ dictLabel: '检测证书', type: '3' },
{ dictLabel: '采购发票', type: '4' },
{ dictLabel: '装备二维码', type: '5' }
],
isDropdownOpen: false,
}
},
computed: {
hasChildrenPropertyUnit() {
const check = (list = []) => {
return list.some(item => {
return (
Array.isArray(item.children) &&
item.children.length > 0
) || check(item.children)
})
}
return check(this.propertyUnitList)
}
},
methods: {
handleUnitChange(value) {
if (value.length === 0) {
this.queryParams.propertyUnitId = undefined
return
}
this.queryParams.propertyUnitId = value[value.length - 1]
},
// 初始化:获取前两级节点的所有 key
initDefaultExpandedKeys() {
const keys = []
const traverse = (data, level) => {
data.forEach((node) => {
if (level <= 1) {
// 只展开第1级和第2级
keys.push(node.id) // 假设你的节点唯一标识是 `id`
if (node.children && node.children.length > 0) {
traverse(node.children, level + 1)
}
}
})
}
traverse(this.treeData, 1)
this.defaultExpandedKeys = keys
},
// 获取省份
getProvinceList() {
getProvinceListAPI().then((res) => {
this.provinceList = res.data
})
},
// 获取产权单位
async getDeptTreeSelect() {
const res = await deptTreeSelect()
this.propertyUnitList = this.filterTree(res.data)
console.log('🚀 ~ this.propertyUnitList:', this.propertyUnitList)
},
// 弹框完全打开后触发二维码生成
handleDialogOpened() {
// 确保 DOM 渲染完成,使用 $nextTick
this.$nextTick(() => {
this.$refs.qr3?.refreshQrcode()
})
},
// 调用子组件下载方法
downloadQrcode() {
// 自定义下载文件名(用装备编码/名称)
const fileName = `装备二维码_${new Date().getTime()}.png`
this.$refs.qr3?.downloadQrcode(fileName)
},
// 调用子组件打印方法
printQrcode() {
this.$refs.qr3?.printQrcode()
},
filterTree(nodes) {
return nodes
.map((node) => {
if (node.children) {
node.children = this.filterTree(node.children)
}
return node
})
.filter((node) => node.status !== '1')
},
// 获取厂家
getManufacturerSelectList() {
getManufacturerSelectApi().then((res) => {
console.log('🚀 ~ getManufacturerSelectList ~ res:', res)
if (res.code === 200) {
this.manufacturerList = res.data
}
})
},
getFirstLevel() {
firstLevel().then((res) => {
if (res.code === 200) {
this.majorList = res.data
}
})
},
majorChange(item) {
if (!item) {
this.processList = []
this.queryParams.process = ''
} else {
secondAndThirdLevel({ firstLevelId: item }).then((res) => {
if (res.code === 200) {
this.processList = this.convertToSubTree(res.data)
}
})
}
},
convertToSubTree(list) {
const map = {}
const tree = []
// 构建节点映射
list.forEach((item) => {
map[item.value] = {
value: item.value.toString(),
label: item.label
}
})
// 构建树形关系
list.forEach((item) => {
const current = map[item.value]
const parent = map[item.parentId]
if (parent) {
if (!parent.children) {
parent.children = []
}
parent.children.push(current)
} else {
tree.push(current)
}
})
return tree
},
// 切换折叠状态
toggleCollapse() {
this.collapsed = !this.collapsed
// 可以在这里添加折叠/展开的动画效果或其他逻辑
},
/**
* 切换搜索区域展开/收起状态
*/
toggleSearch() {
this.searchExpanded = !this.searchExpanded
this.searchExpanded ? (this.tableHeight = 320) : (this.tableHeight = 500)
},
/**
* 树形节点点击事件
*/
handleNodeClick(data) {
this.queryParams.typeId = data.id
this.getDeviceList()
},
/**
* 判断是否可选择
*/
isSelectable(row) {
return row.changeStatus === 1
},
/**
* 出库按钮
*/
onHandleOut() {
this.$router.push('/equipment/euq-out')
},
/**
* 重置查询条件
*/
resetQuery() {
this.$refs.queryForm.resetFields()
this.queryParams.minOriginalValue = ''
this.queryParams.maxOriginalValue = ''
this.queryParams.propertyUnitId = undefined
this.queryParams.pageNum = 1
this.getDeviceList()
},
/**
* 退库按钮
*/
onHandleIn() {
this.$router.push('/equipment-ledger-store/index')
},
/**
* 维修按钮
*/
onHandleRepair() {
this.$router.push('/equipment/equ-repair')
},
/**
* 退役按钮
*/
onHandleRetire() {
this.$router.push('/equipment/equ-retire')
},
onHandleReturn() {
this.$router.push('/equipment/equStore')
},
// 导出按钮下拉菜单触发
handleCommand(command) {
switch (command) {
case 'handleDownloadAll':
this.exportData()
break
case 'handleDownloadPage':
this.exportDataPage()
break
default:
break
}
},
handleVisibleChange(visible) {
this.isDropdownOpen = visible;
},
/**
* 导出数据
*/
exportData() {
this.$message.info('正在导出数据,请稍候...')
// 导出逻辑实现
try {
let fileName = `装备台账_${new Date().getTime()}.xLsx`
let url = '/material-mall/device/export'
const params = { ...this.queryParams }
params.pageNum=null
params.pageSize=null
console.log('🚀 ~ 导出 ~ params:', params)
this.download(url, params, fileName)
} catch (error) {
console.log('导出数据失败', error)
}
},
exportDataPage() {
this.$message.info('正在导出数据,请稍候...')
// 导出逻辑实现
try {
let fileName = `装备台账_${new Date().getTime()}.xLsx`
let url = '/material-mall/device/export'
const params = { ...this.queryParams }
console.log('🚀 ~ 导出 ~ params:', params)
this.download(url, params, fileName)
} catch (error) {
console.log('导出数据失败', error)
}
},
/**
* 获取左侧树形结构数据
*/
async getDeviceTree() {
try {
this.treeLoading = true
const res = await getDeviceTreeAPI()
this.treeData = res.data ? [res.data] : []
} catch (error) {
this.$message.error('获取树形结构失败:' + (error.message || '未知错误'))
console.error(error)
} finally {
this.treeLoading = false
}
},
/**
* 收集第一级和第二级节点的ID第三级默认不展开
*/
collectFirstAndSecondLevelKeys(nodes) {
if (!nodes || !nodes.length) return
nodes.forEach((firstLevelNode) => {
// 展开第一级节点
if (firstLevelNode.id) {
this.defaultExpandedKeys.push(firstLevelNode.id)
console.log(`展开第一级节点: ${firstLevelNode.name}, ID: ${firstLevelNode.id}`)
}
// 检查是否有子节点(第二级节点)
if (firstLevelNode.children && firstLevelNode.children.length) {
// 遍历所有第二级节点
firstLevelNode.children.forEach((secondLevelNode) => {
if (secondLevelNode.id) {
this.defaultExpandedKeys.push(secondLevelNode.id)
console.log(`展开第二级节点: ${secondLevelNode.name}, ID: ${secondLevelNode.id}`)
// 第三级及更深层级默认不展开(保持折叠状态)
}
})
}
})
},
/**
* 左侧树查询:匹配节点显示自身及下一级,但默认只显示到第二级
*/
async getDeviceListByTreeSearch() {
if (!this.treeSearchKey.trim) return
try {
// 1. 获取输入的查询关键词(去空格)
const searchKey = this.treeSearchKey ? this.treeSearchKey.trim() : ''
// 如果没有搜索关键词,则重置为原始树(显示到第二级)
if (!searchKey) {
await this.resetTreeSearch()
return
}
// 2. 重新获取原始树形数据
this.treeLoading = true
const res = await getDeviceTreeAPI()
const originalTreeData = res.data ? [res.data] : []
// 3. 筛选逻辑:匹配节点显示自身及下一级
const filteredTreeData = this.filterTreeWithNextLevel(originalTreeData, searchKey)
// 4. 收集所有匹配节点的ID用于默认展开
const expandedIds = []
this.collectMatchedNodeIds(filteredTreeData, searchKey, expandedIds)
// 5. 更新树形数据和默认展开节点
this.treeData = filteredTreeData
this.defaultExpandedKeys = expandedIds
// 6. 无匹配结果时提示
if (filteredTreeData.length === 0 || this.isTreeEmpty(filteredTreeData)) {
/* this.$message.info('未找到匹配的类型分支'); */
} else {
// this.$message.success(`找到 ${expandedIds.length} 个匹配项`);
}
} catch (error) {
// this.$message.error('树形查询失败:' + (error.message || '未知错误'));
console.error(error)
} finally {
this.treeLoading = false
}
},
/**
* 辅助方法:筛选节点并包含下一级
*/
filterTreeWithNextLevel(treeData, key) {
if (!treeData || !treeData.length) return []
return treeData
.map((node) => {
// 深拷贝节点
const newNode = { ...node }
const isNodeMatch = key ? node.name.includes(key) : true
if (isNodeMatch) {
// 匹配节点:保留所有直接子节点
if (newNode.children && newNode.children.length) {
// 保留第二级,但第三级及更深层级保持原样(默认不展开)
newNode.children = [...newNode.children]
}
return newNode
} else {
// 不匹配节点:检查子节点是否有匹配的
if (newNode.children && newNode.children.length) {
const filteredChildren = this.filterTreeWithNextLevel(newNode.children, key)
if (filteredChildren.length > 0) {
newNode.children = filteredChildren
return newNode
}
}
return null
}
})
.filter((node) => node !== null)
},
/**
* 辅助方法收集所有匹配关键词的节点ID用于默认展开
* @param {Array} treeData - 筛选后的树形数据
* @param {String} key - 查询关键词
* @param {Array} expandedIds - 存储展开节点ID的数组
*/
collectMatchedNodeIds(treeData, key, expandedIds) {
if (!treeData || !treeData.length) return
treeData.forEach((node) => {
// 若当前节点匹配关键词记录其ID用于展开
if (key ? node.name.includes(key) : true) {
if (node.id) expandedIds.push(node.id)
}
// 递归处理子级,确保深层子级的匹配节点也被收集
if (node.children && node.children.length) {
this.collectMatchedNodeIds(node.children, key, expandedIds)
}
})
},
/**
* 辅助方法:检查树是否为空(没有任何有效节点)
*/
isTreeEmpty(treeData) {
if (!treeData || !treeData.length) return true
// 递归检查所有节点
for (const node of treeData) {
if (node && node.id) {
return false // 发现有效节点
}
if (node.children && node.children.length) {
if (!this.isTreeEmpty(node.children)) {
return false // 子节点中有有效节点
}
}
}
return true
},
/**
* 左侧树重置:仅恢复树形原始状态(不联动右侧列表)
*/
/**
* 左侧树重置:恢复树形原始状态,默认显示到第二级
*/
async resetTreeSearch() {
try {
// 1. 清空左侧表单输入和查询参数
if (this.$refs.queryFormLeft) {
this.$refs.queryFormLeft.resetFields()
}
this.treeSearchKey = '' // 手动清空装备名称查询值
this.queryParams.typeId = undefined // 清空可能存在的节点ID缓存
// 2. 恢复原始树形数据,默认显示到第二级
this.treeLoading = true
const res = await getDeviceTreeAPI()
this.treeData = res.data ? [res.data] : []
// 重置默认展开的节点ID数组
this.defaultExpandedKeys = []
// 只展开第一级和第二级节点
// this.collectFirstAndSecondLevelKeys(this.treeData);
this.initDefaultExpandedKeys()
// this.$message.success('树形查询已重置');
} catch (error) {
// this.$message.error('树形重置失败:' + (error.message || '未知错误'));
console.error(error)
} finally {
this.treeLoading = false
}
},
/**
* 获取右侧列表数据
*/
async getDeviceList() {
try {
this.tableLoading = true
// 获取特征值数量
const featureResult = await getMaxFeatureAPI()
this.tableColumns = featureResult.data || 0
this.queryParams.startProductionDate = this.queryParams.productionDate ? this.queryParams.productionDate[0] : ''
this.queryParams.endProductionDate = this.queryParams.productionDate ? this.queryParams.productionDate[1] : ''
this.queryParams.startPurchaseDate = this.queryParams.purchaseDate ? this.queryParams.purchaseDate[0] : ''
this.queryParams.endPurchaseDate = this.queryParams.purchaseDate ? this.queryParams.purchaseDate[1] : ''
this.queryParams.subProcessId = (this.queryParams.process && this.queryParams.process[1]) || ''
delete this.queryParams.productionDate
delete this.queryParams.purchaseDate
// 获取设备列表
const listResult = await getDeviceListAPI(this.queryParams)
this.tableData = listResult.data?.rows || []
this.total = listResult.data?.total || 0
let maxLength = 0
this.tableData.forEach((item) => {
if (item.propertyVoList && item.propertyVoList.length > maxLength) {
maxLength = item.propertyVoList.length
}
})
maxLength = maxLength > 0 ? maxLength * 2 : 0
this.columns2 = this.columns.slice(0, this.columns.length - 18 + maxLength)
setTimeout(() => {
this.$refs.tableRef.doLayout()
}, 200)
} catch (error) {
// this.$message.error('获取设备列表失败:' + (error.message || '未知错误'))
console.error(error)
} finally {
this.tableLoading = false
}
},
/**
* 查看按钮
*/
async onHandlePreview(row) {
// this.formData = row
// this.previewDialogVisible = true
this.$router.push({ path: '/equipment/details', query: { id: row.maId } })
},
/**
* 关闭查看弹窗
*/
handlePreviewClose() {
this.previewDialogVisible = false
this.resetFormData()
},
/**
* 编辑按钮
*/
async onHandleEdit(row) {
console.log('🚀 ~ row:', row)
this.$refs.addEquipRef.openDialog(row.maId, 'edit')
},
/**
* 关闭编辑弹窗
*/
handleEditClose() {
this.editDialogVisible = false
this.resetFormData()
},
/**
* 重置表单数据
*/
resetFormData() {
this.formData = {
maId: '',
companyName: '',
mainGx: '',
childGx: '',
devCategory: '',
devSubcategory: '',
name: '',
devModel: '',
code: '',
changeStatus: '',
upDownStatus: '',
expireTime: '',
unitName: '',
propertyCompany: '',
buyPrice: '',
productionDate: '',
workingHours: '',
onProject: '',
person: '',
personPhone: '',
nextCheckTime: '',
brand: '',
propertyVoList: []
}
},
/**
* 添加特征项
*/
addFeatureItem() {
if (!this.formData.propertyVoList) {
this.formData.propertyVoList = []
}
// 检查是否已达到最大数量限制
if (this.formData.propertyVoList.length >= this.maxFeatureCount) {
// this.$message.warning(`最多只能添加${this.maxFeatureCount}个特征项`)
return
}
this.formData.propertyVoList.push({
propertyName: '',
propertyValue: ''
})
},
/**
* 删除特征项
*/
removeFeatureItem(index) {
this.$confirm('确定要删除该特征项吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true,
showCancelButton: this.formData.propertyVoList.length > 1 // 至少保留一个特征项
})
.then(() => {
this.formData.propertyVoList.splice(index, 1)
})
.catch(() => {
// 取消删除,不做处理
})
},
/**
* 提交编辑表单
*/
async submitEditForm() {
this.$refs.editForm.validate(async(valid) => {
if (valid) {
try {
const res = await updateDeviceAPI(this.formData)
if (res.code === 200) {
this.$message.success('编辑成功')
this.handleEditClose()
this.getDeviceList()
} else {
this.$message.error(res.msg || '编辑失败')
}
} catch (error) {
this.$message.error('编辑操作失败:' + (error.message || '未知错误'))
console.error(error)
}
}
})
},
/**
* 删除按钮
*/
onHandleDelete(row) {
this.$confirm('确定要删除该装备吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
try {
const res = await delDevice(row.maId)
if (res.code === 200) {
this.$message.success('删除成功')
this.getDeviceList()
}
} catch (error) {
this.$message.error('删除失败:' + (error.message || '未知错误'))
}
})
},
/**
* 装备上架按钮
*/
async onHandleDeviceUp() {
if (this.selectedRows.length === 0) {
this.$message.warning('请选择要上架的装备')
return
}
try {
const res = await updateDeviceUpDownAPI({
devIds: this.selectedRows.map((item) => item.maId).join(','),
status: 1
})
this.selectedRows = []
if (res.code === 200) {
this.$message.success('装备上架成功')
this.getDeviceList()
} else {
this.$message.error(res.msg || '上架失败')
}
} catch (error) {
this.$message.error('上架操作失败:' + (error.message || '未知错误'))
console.error(error)
}
},
/**
* 装备下架按钮
*/
async onHandleDeviceDown() {
if (this.selectedRows.length === 0) {
this.$message.warning('请选择要下架的装备')
return
}
try {
const res = await updateDeviceUpDownAPI({
devIds: this.selectedRows.map((item) => item.maId).join(','),
status: 0
})
this.selectedRows = []
if (res.code === 200) {
this.$message.success('装备下架成功')
this.getDeviceList()
} else {
this.$message.error(res.msg || '下架失败')
}
} catch (error) {
this.$message.error('下架操作失败:' + (error.message || '未知错误'))
console.error(error)
}
},
/**
* 多选框选中数据
*/
handleSelectionChange(selection) {
this.selectedRows = selection
},
/**
* 装备状态数量统计
*/
async getDeviceStatusCount() {
try {
const res = await getDeviceStatusCountAPI()
const { inStock, ownUse, share, underRepair, totalValue, retirementValue } = res.data || {}
this.inStock = inStock || 0
this.ownUse = ownUse || 0
this.share = share || 0
this.underRepair = underRepair || 0
this.totalValue = totalValue || 0
this.retirementValue = retirementValue || 0
} catch (error) {
this.$message.error('获取状态统计失败:' + (error.message || '未知错误'))
console.error(error)
}
},
getFeatureValue(row, prop) {
const match = prop.match(/feature(Item|Value)(\d+)/)
if (!match) return '-'
const type = match[1] // 'Item' or 'Value'
const index = Number(match[2]) - 1
const list = row.propertyVoList || []
if (!list[index]) return '-'
return type === 'Item' ? list[index].propertyName : list[index].propertyValue
},
// 查看
handleView(row, type) {
if (type == 1) {
this.dialogTitle = '装备外观'
this.dialogList = row.appearanceImages || []
this.dialogVisible = true
} else if (type == 2) {
this.dialogTitle = '合格证'
this.dialogList = row.certificates || []
this.dialogVisible = true
} else if (type == 3) {
this.dialogTitle = '定期检验报告'
this.dialogList = row.inspectionReports || []
this.dialogVisible = true
} else if (type == 4) {
this.dialogTitle = '采购发票'
this.dialogList = row.purchaseInvoices || []
this.dialogVisible = true
} else if (type == 5) {
this.dialogTitle = '装备二维码'
const propertyList = row.propertyVoList || []
const property = propertyList
// 过滤条件item 存在 且 propertyValue 有值(避免空值干扰)
.filter(item => item && item.propertyValue)
// 拼接单个属性为 "名称:值" 格式
.map(item => `${item.propertyName}:${item.propertyValue}`)
// 用分隔符连接(推荐用中文逗号/竖线,可自定义)
.join(',')
//3.装备小类6.生产厂家7.出厂日期8.特征属性
this.dialogRow =
`装备名称:${row.name}\r\n` +
`装备编码:${row.code}\r\n` +
`规格型号:${row.specificationModel}\r\n` +
`产权单位:${row.propertyUnit}\r\n` +
`装备小类:${row.subCategory}\r\n` +
`生产厂家:${row.manufacturer}\r\n` +
`出厂日期:${row.productionDate}\r\n` +
`特征属性:{${property}}`
this.dialogCodeVisible = true
}
},
handleFile(row) {
// 打开文件预览
window.open(row.fileUrl, '_blank')
},
handleFileListUpload(row) {
this.currentRow = row
this.openReport = true
}
},
async created() {
await this.getDeptTreeSelect().catch(() => {
})
const params = this.$route.query
this.queryParams.propertyUnitId = params?.deptId || ''
const ids = []
if (params?.parentId) {
ids.push(params.parentId)
}
if (params?.deptId) {
ids.push(params.deptId)
}
this.queryParams.propertyUnitIds = [...ids.map(Number)]
this.getProvinceList()
this.getFirstLevel()
this.getManufacturerSelectList()
// 并行加载初始数据
await Promise.all([this.getDeviceTree(), this.getDeviceStatusCount()])
// 最后加载列表数据
await this.getDeviceList()
this.initDefaultExpandedKeys()
}
}
</script>
<style scoped lang="scss">
.app-container {
padding: 12px;
background-color: #f5f7fa;
}
/* 搜索区域收起/展开按钮 */
.search-toggle-btn {
color: #2CBAB2;
padding: 5px 10px;
font-size: 16px;
font-weight: 300;
}
/* 固定操作栏样式 */
.sticky-bar {
position: sticky;
top: 0;
z-index: 10;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
/* 字段设置弹窗样式 */
.field-setting-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
max-height: 400px;
overflow-y: auto;
padding-right: 10px;
}
.field-setting-group .el-checkbox {
width: calc(50% - 10px);
margin-bottom: 10px;
}
/* 原有样式保持不变 */
.page-header {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e4e7ed;
}
.page-title {
font-size: 20px;
color: #1f2d3d;
margin-bottom: 5px;
font-weight: 500;
}
.page-desc {
font-size: 14px;
color: #8392a5;
}
.card-container {
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
overflow: hidden;
}
.card-container-btn {
/* 使用 clip-path 创建三角形形状 */
clip-path: polygon(0 0, 100% 50%, 0 100%);
width: 40px;
height: 70px;
background-color: red;
position: relative;
cursor: pointer;
transition: all 0.3s ease;
}
.content-box {
height: calc(100vh - 408px);
}
.card-header {
padding: 14px;
border-bottom: 1px solid #e4e7ed;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
font-size: 15px;
color: #1f2d3d;
font-weight: 500;
}
.table-count {
font-size: 12px;
color: #8392a5;
}
.tree-container {
height: calc(100vh - 180px);
overflow: auto;
padding: 15px;
}
.tree-container::-webkit-scrollbar {
display: none;
}
.query-buttons {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 10px;
}
.query-btn {
background-color: #409eff;
border-color: #409eff;
}
.reset-btn {
background-color: #f5f7fa;
color: #606266;
border-color: #dcdfe6;
}
.export-btn {
background-color: #67c23a;
border-color: #67c23a;
}
.status-operation-bar {
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
padding: 15px 20px;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.status-stats {
display: flex;
gap: 20px;
width: 100%;
flex-wrap: nowrap;
}
.stat-card {
flex: 1;
background: linear-gradient(180deg, rgba(52, 226, 199, 0.1) 0%, rgba(52, 226, 199, 0) 100%);
border-radius: 8px;
padding: 10px 16px;
display: flex;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.stat-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.stat-content {
width: 100%;
display: flex;
flex-direction: column;
}
.stat-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.stat-value {
font-size: 18px;
font-weight: 600;
color: #1f2d3d;
line-height: 1.2;
}
.stat-label {
font-size: 12px;
color: #606266;
line-height: 1.2;
}
.stat-icon {
width: 22px;
height: 22px;
object-fit: contain;
flex-shrink: 0;
}
.stat-item {
display: flex;
align-items: center;
}
.operation-buttons {
width: 100%;
}
.operation-btn {
display: inline-flex;
align-items: center;
gap: 4px;
}
.pagination-container {
padding: 15px 20px;
display: flex;
justify-content: flex-end;
align-items: center;
}
.opt-btn {
padding: 5px 8px;
margin: 0 2px;
}
.view-btn {
color: #00a288;
}
.edit-btn {
color: #00a288;
}
.delete-btn {
color: #00a288;
}
/* 弹窗表单样式 */
.detail-form {
max-height: 60vh;
overflow-y: auto;
padding-right: 10px;
}
/* 滚动条美化 */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* 特征值输入框和删除按钮容器 */
.feature-value-container {
display: flex;
align-items: center;
gap: 8px;
}
.delete-feature-btn {
padding: 0;
height: 100%;
display: flex;
align-items: center;
}
/* 调整删除按钮位置 */
::v-deep .el-form-item__content {
display: flex;
align-items: center;
}
.col-center {
display: flex;
align-items: center; /* 垂直居中 */
/* 如果需要整列高度充满父容器可添加height: 100%; */
}
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 90px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 50%;
}
.card-container {
position: relative; /* 新增为按钮绝对定位提供参考 */
}
.collapse-btn {
/* 修改将按钮定位在右侧中间 */
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
background: white;
color: #606266;
padding: 2px;
z-index: 10; /* 确保按钮在最上层 */
}
/* 优化按钮悬停效果 */
.collapse-btn:hover {
background: white;
color: #409eff;
}
/* 折叠状态的样式和过渡动画 */
.tree-column {
transition: all 0.3s ease;
display: flex;
flex-direction: row;
}
::v-deep .el-table {
// 启用斑马纹
&.el-table--striped .el-table__body {
tr.el-table__row--striped td {
background-color: #f6fbfa !important; // 浅紫色
}
}
.el-table__header {
background: #e9f0ee;
th {
background: #e9f0ee !important;
color: #606266;
font-weight: 600;
height: 50px;
}
}
&.el-table--striped .el-table__body tr.el-table__row:hover > td.el-table__cell {
background-color: #ccf1e9 !important;
}
}
.el-col-1 {
width: 1.5% !important;
}
.el-button--primary {
background-color: #2CBAB2;
border-color: #2CBAB2;
}
.el-checkbox__input.is-checked .el-checkbox__inner, .el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #2CBAB2;
border-color: #2CBAB2;
}
.el-tag {
padding: 13px 7px;
line-height: 0;
}
.el-button--text {
color: #2CBAB2;
}
/* 弹框垂直居中 */
.center-dialog {
::v-deep .el-dialog {
margin: 0 auto;
top: 40%;
transform: translateY(-50%);
}
}
::v-deep.el-tag.el-tag--info {
background-color: #F5F5F5;
border-color: #B3B3B3;
color: #B3B3B3;
}
::v-deep.el-tag.el-tag--warn {
background-color: rgba(255, 171, 41, 0.1);;
border: #FFAB29;
color: #FFAB29;
}
::v-deep.el-tag.el-tag--success {
background-color: rgba(52, 226, 199, 0.1);
border-color: #34E2C7;
color: #34E2C7;
}
.el-select-dropdown__item.hover, .el-select-dropdown__item:hover {
background-color: rgba(52, 226, 199, 0.1);
}
/* 二维码容器水平居中 */
.qrcode-box {
text-align: center; /* 核心让inline-block的二维码居中 */
padding: 10px 0;
}
/* 底部按钮布局优化 */
.dialog-footer {
display: flex;
justify-content: center; /* 按钮水平居中 */
gap: 10px; /* 按钮间距 */
}
.cell-content-two-lines {
}
.el-table .cell {
white-space: normal !important;
overflow: visible !important;
text-overflow: unset !important;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2; /* 强制最多显示2行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis; /* 两行后末尾加省略号 */
word-break: break-all; /* 英文/数字换行 */
word-wrap: break-word; /* 中文换行 */
height: 36px; /* 两行文本的高度line-height:1.4 → 18px*2=36px可按需调整 */
margin: 2px 0; /* 上下边距,避免内容贴边 */
}
.app-container{
// height: 100vh;
::v-deep.el-loading-mask{
background-color:#E5EEF5;
}
::v-deep.el-loading-spinner{
position: absolute;
top: 40%;
left: 44%;
width: 12%;
height: 15%;
border-radius: 10px;
background-image: url('../../../public/lodingCar.gif');
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;
}
::v-deep.el-loading-spinner>svg{
display: none;
}
}
.btn-icon {
width: 11px;
height: 11px;
margin-right: 6px;
vertical-align: top;
color: rgba(0,0,0,.65);
}
.el-icon-arrow-up {
transition: transform 0.3s ease;
}
.el-icon-arrow-up.rotate {
transform: rotate(180deg);
}
</style>