|
|
<template> <div class="app-container row-container"> <!--工具栏--> <div class="head-container"> <div v-if="crud.props.searchToggle" class="head-search"> <!-- 搜索 --> <el-input v-model="query.search" size="small" clearable placeholder="输入资产名称/编码搜索" prefix-icon="el-icon-search" style="width: 220px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> <rrOperation /> </div> <crudOperation :permission="permission"> <template v-slot:right> <!-- @click="doExport(crud.selections)" :disabled="crud.selections.length === 0" --> <el-button :loading="crud.downloadLoading" size="mini" @click="showSyncDialog"> <i class="iconfont icon-zidonggengxin" /> 同步 </el-button> </template> </crudOperation> </div> <div class="container-wrap"> <span class="right-top-line" /> <span class="left-bottom-line" /> <el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" height="calc(100vh - 330px)" :data="crud.data" @row-dblclick="handleRowDblClick"> <el-table-column prop="mark" label="资产编号" min-width="150px" :show-overflow-tooltip="true" /> <el-table-column prop="name" label="资产名称" min-width="180px" :show-overflow-tooltip="true" /> <el-table-column prop="capitaltyepname" label="资产类型" width="80px" :show-overflow-tooltip="true" /> <el-table-column prop="startprice" label="价值" width="80px" align="right" :show-overflow-tooltip="true" :formatter="(row) => formatMoney(row.startprice)" /> <el-table-column prop="unitname" label="计量单位" width="80px" align="center" :show-overflow-tooltip="true" /> <el-table-column prop="departmentname" label="使用部门" :show-overflow-tooltip="true" /> <el-table-column prop="resourcename" label="使用人" :show-overflow-tooltip="true" /> <!-- <el-table-column prop="blongsubcompanyname" label="所属单位" min-width="140px" :show-overflow-tooltip="true" /> --> <el-table-column prop="blongdepartmentname" label="所属部门" min-width="140px" :show-overflow-tooltip="true" /> <el-table-column prop="glrname" label="管理人" :show-overflow-tooltip="true" /> <el-table-column prop="cfdname" label="存放地" min-width="140px" :show-overflow-tooltip="true" /> <!-- <el-table-column prop="rzrq" label="更新时间" width="180px"> <template slot-scope="scope"> <div>{{ scope.row.rzrq | parseTime }}</div> </template> </el-table-column> --> <el-table-column prop="is_whitelist" label="是否白名单" align="center"> <template slot-scope="scope"> <el-tag v-if="scope.row.is_whitelist" type="success">白名单</el-tag> <span v-else>-</span> </template> </el-table-column> </el-table> <!--分页组件--> <pagination v-if="crud.data.length!==0" /> </div>
<!-- 同步对话框 --> <el-dialog :visible.sync="syncDialogVisible" title="资产同步" width="600px" append-to-body :close-on-click-modal="false" :modal-append-to-body="false" > <div v-if="!syncComplete" style="display: flex; justify-content: flex-start; align-items: center;"> <input ref="fileInput" type="file" accept=".xlsx" style="display: none" @change="handleFileChange" > <el-button size="small" type="primary" @click="triggerFileSelect">上传数据</el-button> <div v-if="selectedFile" class="file-tip">已选择文件: {{ selectedFile.name }}</div> <div v-else class="file-tip">请上传.xlsx格式的文件</div> </div> <div v-if="syncLoading" class="sync-loading"> <div class="sync-loading-content"> <i class="el-icon-loading sync-loading-icon" /> <div class="sync-status">{{ syncStatusText }}</div> </div> </div> <div v-else-if="syncComplete" class="sync-result"> <i class="el-icon-warning sync-icon" /> <div class="sync-text"> <!-- 已完成同步,<p class="sync-new">新增<span>{{ newCount }}</span>条</p>,<p class="sync-update">更新<span>{{ updateCount }}</span>条</p> --> {{ syncResultText }} </div> </div> <div slot="footer" class="dialog-footer"> <el-button v-if="!syncComplete" :disabled="syncLoading" @click="syncDialogVisible = false">取消</el-button> <el-button v-if="!syncComplete" type="primary" :disabled="syncLoading" @click="startSync">开始同步</el-button> </div> </el-dialog>
<el-dialog class="detailBook" append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="handleCloseDialog" :visible="detailDialogVisible" title="资产详情"> <span class="dialog-right-top" /> <span class="dialog-left-bottom" /> <div class="setting-dialog"> <div class="detail-tab tab-content"> <!-- <ul class="tab-nav"> <li :class="{'active-tab-nav': tabIndex === 0}" @click="changeDetailTab(0)">基础信息</li> </ul> --> <!-- v-if="tabIndex===0" --> <div class="detail-check"> <div style="flex:1"> <ul class="book-detail" style="max-height: 680px; overflow: auto;"> <li><span>资产编号:</span>{{ selectedAsset && selectedAsset.mark }}</li> <li><span>资产名称:</span>{{ selectedAsset && selectedAsset.name }}</li> <li><span>资产类型:</span>{{ selectedAsset && selectedAsset.capitaltyepname || '——' }}</li> <li><span>规格型号:</span>{{ selectedAsset && selectedAsset.capitalspec || '——' }}</li> <!-- <li><span>设备用途:</span>{{ selectedAsset && selectedAsset.sbyt || '——' }}</li> --> <li><span>价值类型:</span>{{ selectedAsset && selectedAsset.jzlx || '——' }}</li> <li><span>价值:</span>{{ selectedAsset ? formatMoney(selectedAsset?.startprice) : '——' }}</li> <li><span>数量:</span>{{ selectedAsset && selectedAsset.capitalnum }}</li> <li><span>实际数量:</span>{{ selectedAsset && selectedAsset.sjsl }}</li> <li><span>计量单位:</span>{{ selectedAsset && selectedAsset.unitname }}</li> <li><span>使用部门:</span>{{ selectedAsset && selectedAsset.departmentname }}</li> <li><span>使用人:</span>{{ selectedAsset && selectedAsset.resourcename }}</li> <!-- <li><span>所属单位:</span>{{ selectedAsset &&selectedAsset.blongsubcompanyname }}</li> --> <li><span>所属部门:</span>{{ selectedAsset && selectedAsset.blongdepartmentname }}</li> <li><span>管理人:</span>{{ selectedAsset && selectedAsset.glrname }}</li> <li><span>存放地:</span>{{ selectedAsset && selectedAsset.cfdname }}</li> <li><span>供应商:</span>{{ selectedAsset && selectedAsset.gys }}</li> <li><span>项目名称:</span>{{ selectedAsset && selectedAsset.xmmc }}</li> <!-- <li><span>无线电设备用途:</span>{{ selectedAsset && selectedAsset.wxdsbyt }}</li> --> <li><span>领用日期:</span>{{ selectedAsset && selectedAsset.deprestartdate | parseTime }}</li> <!-- <li><span>购置日期:</span>{{ selectedAsset &&selectedAsset.gzrq | parseTime }}</li> --> <li><span>投入使用日期:</span>{{ selectedAsset && selectedAsset.trsyrq | parseTime }}</li> <li><span>入账日期:</span>{{ selectedAsset && selectedAsset.rzrq | parseTime }}</li> <li><span>保修截至日期:</span>{{ selectedAsset && selectedAsset.bxjzrq | parseTime }}</li> <!-- <li v-if="selectedAsset && selectedAsset.rzrq !== null"><span>更新时间:</span>{{ selectedAsset &&selectedAsset.rzrq | parseTime }}</li> <li v-else><span>更新时间:</span>——</li> --> </ul> <div v-if="selectedAsset && selectedAsset.is_whitelist" class="book-detail white-detail"> <div class="white-title"> <span class="small-line" /> <p>已加入白名单</p> <span class="big-line" /> </div> <ul> <li class="white-item"><span>加入时间:</span>{{ selectedAsset && selectedAsset.whiteCreateTime | parseTime }}</li> <li class="white-item"><span>操作人:</span>{{ selectedAsset && selectedAsset.whiteLastname || '——' }}</li> <li class="white-item"><span>备注:</span>{{ selectedAsset && selectedAsset.whiteRemark || '——' }}</li> </ul> </div> </div> <!-- <img src="http://192.168.99.72:15000/api/minio/getImg?bucket=2&fileName=2b532fd6792f4dbe91d6a1dbf15e4738.jpg" alt="资产图片"> --> <!-- <div :class="selectedAsset && selectedAsset.imageUrl ? 'asset-image asset-img' : 'asset-image default-img'"> <img :src="selectedAsset && selectedAsset.imageUrl || require('@/assets/images/system/default-img2.png')" alt="资产图片"> </div> -->
<!-- 资产图片列表(竖向滚动 + 删除按钮) --> <div class="asset-image-wrapper"> <div v-if="assetImageList.length > 0" class="asset-image-scroll"> <div v-for="(item, index) in assetImageList" :key="item.id || index" class="image-item"> <!-- <img :src="item.imgUrl" alt="资产图片" class="preview-img" @click="openPreview(item.imgUrl)" > --> <el-image class="preview-img" :src="item.imgUrl" fit="contain" :preview-src-list="assetImageList.map(img => img.imgUrl)" :preview-initial-index="index" /> <div class="delete-btn" @click="deleteAssetImage(item.id, index)"> <i class="el-icon-close" /> </div> </div> </div> <div v-else class="default-img"> <img :src="require('@/assets/images/system/default-img2.png')" alt="默认图片"> </div> </div> </div> </div> </div> </el-dialog> </div></template>
<script>import crudAsset, { FetchAssetInfoDetailsByMark, FetchDeleteAssetInfoPhotoById } from '@/api/asset/index'import CRUD, { presenter, header, crud } from '@crud/crud'import rrOperation from '@crud/RR.operation'import crudOperation from '@crud/CRUD.operation'import pagination from '@crud/Pagination'import { mapGetters } from 'vuex'import { upload } from '@/utils/upload'
// http://192.168.99.72:15000/api/asset/initAssetInfo?pageNo=1&pageSize=10
export default { name: 'Inventory', components: { pagination, crudOperation, rrOperation }, cruds() { return CRUD({ title: '资产清单', idField: 'mark', url: 'api/asset/initAssetInfo', crudMethod: { ...crudAsset }, optShow: { add: false, edit: false, del: false, reset: true, download: false, group: false }}) }, mixins: [presenter(), header(), crud()], data() { return { permission: { add: [], edit: [], del: [] }, syncDialogVisible: false, syncLoading: false, syncProgress: 0, syncStatusText: '', syncComplete: false, syncResultText: '', selectedFile: null, detailDialogVisible: false, selectedAsset: null, newCount: 0, updateCount: 0, tabIndex: 0, assetImageList: [] } }, computed: { ...mapGetters([ 'baseApi' ]) }, created() { }, mounted() { }, methods: { [CRUD.HOOK.beforeRefresh]() { if (this.crud.query.search) { this.crud.query.search = this.crud.query.search.trim() } // 保存原始参数
this.originalPage = this.crud.page.page this.originalSize = this.crud.page.size // 添加pageNo和pageSize参数
this.crud.params.pageNo = this.crud.page.page this.crud.params.pageSize = this.crud.page.size // 清空page和size参数
this.crud.params.page = undefined this.crud.params.size = undefined }, [CRUD.HOOK.afterRefresh](crud) { // 恢复原始参数
if (this.originalPage !== undefined) { this.crud.page.page = this.originalPage this.crud.page.size = this.originalSize // 移除pageNo和pageSize参数
delete this.crud.params.pageNo delete this.crud.params.pageSize delete this.crud.params.page delete this.crud.params.size } console.log(crud.data) }, // 新增前
[CRUD.HOOK.beforeToAdd](crud, form) { }, // 编辑前
[CRUD.HOOK.beforeToEdit](crud, form) { }, // 提交前做的操作
[CRUD.HOOK.afterValidateCU](crud) { return true }, formatMoney(money) { if (!money && money !== 0) return '——' return Number(money).toLocaleString('en-US') }, // 显示同步对话框
showSyncDialog() { this.syncDialogVisible = true this.selectedFile = null this.syncLoading = false this.syncProgress = 0 this.syncStatusText = '' this.syncComplete = false this.syncResultText = '' // 重置文件输入框
if (this.$refs.fileInput) { this.$refs.fileInput.value = '' } }, // 触发文件选择
triggerFileSelect() { this.$refs.fileInput.click() }, // 处理文件选择
handleFileChange(event) { const file = event.target.files[0] if (file) { this.selectedFile = file } }, // 开始同步
startSync() { if (!this.selectedFile) { this.$message({ message: '请选择要上传的文件', type: 'warning', offset: 8 }) return }
this.syncLoading = true this.syncStatusText = '正在同步数据...'
// 构建表单数据
const formData = new FormData() formData.append('file', this.selectedFile) // 注意:upload是隐藏了token这块的,如果后续需要,需要在upload.js中放开headers
upload(this.baseApi + '/api/asset/importTextInfo', this.selectedFile).then(res => { console.log(res) if (res.data.code === 200) { this.syncLoading = false this.syncComplete = true this.syncResultText = res.data.data // 刷新表格数据
this.crud.refresh() } else { this.$message({ message: res.data.data || '同步失败', type: 'error', offset: 8 }) this.syncLoading = false } }).catch(error => { console.error('同步失败:', error) this.$message({ message: '同步失败,请重试', type: 'error', offset: 8 }) this.syncLoading = false }) }, // 格式化进度显示
formatProgress(percentage) { return `${percentage}%` }, // 处理行双击事件
handleRowDblClick(row) { this.tabIndex = 0 this.detailDialogVisible = true // 清空上一次的图片
this.selectedAsset = {} this.assetImageList = [] const params = { mark: row.mark } FetchAssetInfoDetailsByMark(params).then(res => { this.selectedAsset = { ...(res || {}), capitaltyepname: row.capitaltyepname, resourcename: row.resourcename, is_whitelist: row.is_whitelist, whiteCreateTime: row.whiteCreateTime, whiteLastname: row.whiteLastname, whiteRemark: row.whiteRemark } const photoList = res.assetInfoPhoto || [] this.assetImageList = photoList.map(item => ({ id: item.id, photoUrl: item.photoUrl, imgUrl: this.baseApi + '/api/minio/getImg?bucket=2&fileName=' + item.photoUrl }))
// this.assetImageList = [
// 'https://qiniu.aiyxlib.com/1604467087428',
// 'https://qiniu.aiyxlib.com/1605059837392',
// 'https://qiniu.aiyxlib.com/1605060069983',
// 'https://qiniu.aiyxlib.com/1605060069983',
// 'https://qiniu.aiyxlib.com/1605060269830',
// 'https://qiniu.aiyxlib.com/1606124577077',
// 'https://qiniu.aiyxlib.com/1605060069983'
// ]
console.log(' this.assetImageList', this.assetImageList) }) }, // 删除资产图片
deleteAssetImage(photoId, index) { this.$confirm('确定要删除这张图片吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { console.log('photoId', [photoId]) FetchDeleteAssetInfoPhotoById([photoId]).then(res => { console.log('res', res) if (res.data !== null) { this.assetImageList.splice(index, 1) this.$message({ message: '删除成功', type: 'success', offset: 8 }) } else { this.$message({ message: res.message, type: 'error', offset: 8 }) } }).catch(() => { this.$message({ message: '删除失败', type: 'error', offset: 8 }) }) }).catch(() => {}) }, changeDetailTab(index) { this.tabIndex = index }, handleCloseDialog() { this.detailDialogVisible = false this.tabIndex = 0 this.selectedAsset = null this.assetImageList = [] // 关闭清空图片列表
} }}</script>
<style lang="scss" scoped>.sync-loading { display: flex; justify-content: center; align-items: center; min-height: 200px;}.sync-loading-content { text-align: center;}.sync-loading-icon { font-size: 48px; color: #409EFF;}.sync-status { margin-top: 15px; text-align: center; color: #606266; font-size: 14px;}.file-tip { margin-left: 10px; font-size: 12px; color: #606266;}.sync-result{ display: flex; justify-content: center; align-items: center; margin-top: 40px; font-size: 22px; text-align: center; .sync-text{ margin-left: 10px; p{ display: inline-block; span{ display: inline-block; padding: 0 4px; } } }}::v-deep .el-dialog__footer{ padding: 0 0 20px 0 !important;}.asset-detail-content { display: flex; gap: 20px;}
.asset-info { flex: 1;}
// .asset-carousel {
// width: 400px;
// height: 200px;
// ::v-deep .el-carousel__item {
// display: flex;
// align-items: center;
// justify-content: center;
// background: #f5f7fa;
// }
// }
// .carousel-img {
// max-width: 100%;
// max-height: 200px;
// object-fit: contain;
// }
// .asset-image {
// display: flex;
// align-items: center;
// justify-content: center;
// width: 400px;
// height: 200px;
// }
// // .asset-img {
// // // width: 100%;
// // // height: 200px;
// // object-fit: cover;
// // border-radius: 4px;
// // img {
// // display: block;
// // width: 400px;
// // }
// // }
// .default-img{
// width: 200px;
// height: 200px;
// display: flex;
// align-items: center;
// justify-content: center;
// background-color: #f5f7fa;
// border: 4px solid #dcdfe6;
// border-radius: 4px;
// img {
// width: 100%;
// height: 100%;
// object-fit: cover;
// }
// }
// 资产图片列表样式(竖向滚动)
.asset-image-wrapper { width: 400px; height: 660px; /* 固定高度 */ overflow: hidden;}
.asset-image-scroll { width: 100%; height: 100%; overflow-y: auto; /* 竖向滚动条 */ padding-right: 5px; display: flex; flex-direction: column; gap: 10px;}
.image-item { position: relative; width: 100%; height: 150px; background: #f5f7fa; border-radius: 4px; overflow: hidden; flex-shrink: 0;}
.preview-img { width: 100%; height: 100%; object-fit: contain; background: #fff;}
// 删除按钮
.delete-btn { position: absolute; top: 5px; right: 5px; width: 22px; height: 22px; background: rgba(0, 0, 0, 0.6); color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 10; transition: 0.2s;
&:hover { background: #f56c6c; } i { font-size: 14px; }}
// 默认图片
.default-img { width: 100%; height: 200px; display: flex; align-items: center; justify-content: center; background-color: #f5f7fa; border: 2px solid #dcdfe6; border-radius: 4px;
img { width: 180px; height: 180px; object-fit: cover; }}
.detailBook { ::v-deep .el-dialog { width: 1000px !important; .el-dialog__body{ padding: 0 0 30px 0; ::-webkit-scrollbar{ width: 5px !important; height: 5px !important;
} ::-webkit-scrollbar-thumb{ background-color: #f5f7fa !important; } } } .detail-check{ display: flex; justify-content: space-between; align-items: flex-start; padding: 12px 30px; background-color: #F6F8FC; overflow: hidden; } .book-detail{ display: flex; flex-direction: column; justify-content: flex-start; li{ line-height: 30px; span{ display: inline-block; width: 120px; text-align: right; margin-right: 10px; color: #0C0E1E; } i{ font-style: normal; padding: 0 8px; } &.row-li{ width: 100% } &.white-item { color: #606266; span { color: #0348F3 !important; } } } &.white-detail{ border-radius: 4px; background-color: #EEF5FE; margin-top: 4px; & .white-title{ display: flex; justify-content: flex-start; align-items: center; font-size: 12px; color: #606266; padding: 6px; p{ padding: 0 10px; } .small-line{ display: inline-block; width: 40px; border-bottom: 1px solid #a9aeb8; } .big-line{ display: inline-block; flex: 1; // height: 1px;
border-bottom: 1px solid #a9aeb8; // background-color: #a9aeb8;
} } } } ::v-deep .dialog-footer{ margin-top: 30px; }}
// .el-table{
// ::v-deep .el-tag{
// margin: 0 !important;
// &.el-tag--small{
// color: #0348f3 !important;
// background-color: #eef5fe !important;
// border-color: #eef5fe !important;
// }
// &.el-tag--success {
// background-color: #e7faf0 !important;
// border-color: #d0f5e0!important;
// color: #13ce66!important;
// }
// &.el-tag--info {
// background-color: #f4f4f5 !important;
// border-color: #f4f4f5!important;
// color: #909399!important;
// }
// }
// }
</style>
|