无线电资产管理
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

691 lines
23 KiB

<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>