图书馆综合管理系统
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.
 
 
 
 

605 lines
21 KiB

<template>
<div class="app-container">
<div class="venue-header dataScreening-header">
<h4><i class="iconfont icon-shuju" />书架总览</h4>
<div class="bookshelf-area">
<!-- <span> {{ floorName }} - {{ regionName }}</span> -->
<router-link :to="{ path: '/check/check/dataScreening', query: {floorTabIndex: floorTabIndex }}">
{{ floorName }}
</router-link>
<span>/</span>
<router-link :to="{ path: '/dataScreening/regions', query: {regionTabIndex: regionTabIndex }}">
{{ regionName }}
</router-link>
<div class="double-click-btn"><span>点击左侧位置返回</span></div>
</div>
<p><i class="iconfont icon-gongsi" />{{ user.fonds.fondsName }}</p>
</div>
<div class="venue-content dataScreening-content">
<crudOperation :permission="permission">
<template v-slot:middle>
<el-select v-model="layerVal" clearable size="small" placeholder="楼层" class="filter-item" style="width: 100px; margin-right: 10px;">
<el-option v-for="item in layerOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-button v-permission="permission.add" class="check-btn" size="mini" @click="toAdd(4)">
<i class="iconfont icon-shengchengpandiandan" />
书架盘点
</el-button>
</template>
<template v-slot:right>
<el-button :loading="crud.downloadLoading" size="mini" @click="doExport(crud.selections)">
<i class="iconfont icon-daochu" />
导出
</el-button>
</template>
</crudOperation>
<div class="venue-left">
<div class="container-right tab-content">
<span class="right-top-line" />
<span class="left-bottom-line" />
<ul class="tab-nav">
<li v-for="(item,index) in tabListData" :key="index" :class="{ 'active-tab-nav': tabIndex == index }" @click="changeActiveTab(index)">{{ item.name }}<i /></li>
<!-- 最右侧装饰img -->
<span class="tab-right-img" />
</ul>
<div class="tag-info">
<p class="tag-sort">错序:<i class="iconfont icon-zhuangtai2" />1</p>
<p class="tag-place">错架:<i class="iconfont icon-zhuangtai2" />1</p>
<p class="tag-all">在架:<i class="iconfont icon-zhuangtai2" />20</p>
</div>
<!-- </div> -->
<div class="shelf-top" :style="rowStyle">
<p v-for="(item,index) in reversedRackNum" :key="index" :style="{width: `calc(142px)`}"><span>{{ item + '架' }}</span></p>
</div>
<ul v-loading="listLoading" class="data-shelf-row" :style="rowStyle">
<!-- :class="{ active: i === cellIndex }" -->
<!-- { active: columnIndex === i % rackNum} -->
<li
v-for="(cell,i) in booShelfGrid"
:key="i"
:class="['data-shelf-cell',{'active': isActiveColumn(i)}]"
:style="cellStyle"
@dblclick="handleCellCurrent(cell,i)"
@mouseenter="showPopover(i)"
@mouseleave="hidePopover"
>
<div class="mask" />
<div class="tag-info">
<p class="tag-sort"><i class="iconfont icon-zhuangtai2" />1</p>
<p class="tag-place"><i class="iconfont icon-zhuangtai2" />1</p>
<p class="tag-all"><i class="iconfont icon-zhuangtai2" />20</p>
</div>
<el-popover
v-if="popoverIndex === i"
ref="popover"
:visible="popoverVisible[i]"
width="400"
:style="popoverStyles[i]"
trigger="manual"
>
<div slot="reference" class="popover-content-set">
<div class="tooltip-top">
<h4>本架概况</h4>
<i class="update-time">2024-11-28 09:46</i>
</div>
<ul>
<li><p>架位</p><em class="percentage"><i style="color: #fff;">{{ removeAreaPrefix(cell.gridName) }}</i></em></li>
<li><p>在架</p><em><i>15000</i>册</em></li>
<li><p>错架</p><em><i>300</i>层</em> <em class="percentage">(2.00%)</em></li>
<li><p>错序</p><em><i>0</i>层</em><em class="percentage">(0.00%)</em></li>
</ul>
</div>
</el-popover>
</li>
</ul>
</div>
</div>
<div class="venue-right">
<div class="lib-right-item lib-info">
<h4>书架概况</h4>
<ul class="data-right-list">
<li><p>书架</p><span><i>001排</i></span></li>
<li><p>规格</p><span><i>双面 6 x 5</i></span></li>
</ul>
</div>
<div class="lib-right-item">
<h4>书架盘点概况</h4>
<div class="refresh-date">2024-11-28 09:46</div>
<ul class="data-right-list">
<li><p>在架</p><span><i>15000</i>册</span></li>
<li><p>错架</p><span><i>300</i>层</span> <span class="percentage">(2.00%)</span></li>
<li><p>错序</p><span><i>0</i>层</span><span class="percentage">0.00%</span></li>
</ul>
</div>
</div>
</div>
<eForm ref="eform" />
</div>
</template>
<script>
import { FetchInitShelfGridByShelfId, FetchBookShelfDetails } from '@/api/shelf/index'
import crudRegion from '@/api/area/index'
import CRUD, { presenter, header, crud } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import { mapGetters } from 'vuex'
import eForm from './module/form'
export default {
name: 'DataScreening',
components: { crudOperation, eForm },
cruds() {
return CRUD({ title: '架位总览', url: 'api/libraryRegion/initLibraryRegionList', crudMethod: { ...crudRegion }, sort: [], optShow: {
add: false,
edit: false,
del: false,
download: false,
group: false,
reset: false
},
queryOnPresenterCreated: false
})
},
mixins: [presenter(), header(), crud()],
data() {
const _this = this
return {
listLoading: false,
tabIndex: 0,
floorTabIndex: 0,
regionTabIndex: 0,
floorName: null,
regionName: null,
rowType: null,
bookShelfDetails: null,
booShelfGrid: [],
cellInfo: {
gridName: null,
startSortmark: null,
endSortmark: null,
cameraId: null
},
callNumVisible: false,
layerNum: 0,
rackNum: 0,
swiperActiveIndex: 0,
cellIndex: null,
columnIndex: null,
swiperOptionContent: {
slidesPerView: 'auto',
on: {
slideChangeTransitionStart: function() {
_this.cellIndex = null
_this.swiperActiveIndex = this.activeIndex
_this.swiperTitle.slideTo(this.activeIndex, 500, false)
}
}
},
swiperOptionTitle: {
slidesPerView: 'auto',
freeMode: true
},
layerVal: '001排',
layerOptions: [{ id: 1, name: '001排' }],
tabListData: [],
permission: {
add: ['admin', 'floor:add'],
edit: ['admin', 'floor:edit'],
del: ['admin', 'floor:del']
},
popoverIndex: null,
popoverVisible: [],
popoverStyles: [],
activeColumns: {}
}
},
computed: {
...mapGetters([
'user',
'baseApi'
]),
swiperContent() {
return this.$refs.swiperContent.$el.swiper
},
swiperTitle() {
return this.$refs.swiperTitle.$el.swiper
},
// cellStyle: function() {
// // const h = '100%/' + this.layerNum
// // const w = '100%/' + this.rackNum
// const h = '76px'
// const w = '100%/' + this.rackNum
// return { width: `calc(${w} )`, height: `calc(${h})` }
// },
cellStyle: function() {
const h = '76px'
// const w = '100%/' + this.rackNum
const w = '146px'
return { width: `calc(${w} )`, height: `calc(${h})` }
},
rowStyle: function() {
const w = 146 * this.rackNum + 'px'
return { width: `calc(${w})` }
},
reversedRackNum() {
if (this.booShelfGrid && this.booShelfGrid.length > 0) {
if (parseInt(this.booShelfGrid[0].gridShelf) === this.rackNum) {
return Array.from({ length: this.rackNum }, (_, i) => this.rackNum - i).map(x => x.toString())
} else {
return Array.from({ length: this.rackNum }, (_, i) => i + 1).map(x => x.toString())
}
} else {
return []
}
}
},
async created() {
if (localStorage.getItem('dataScreenFloorTableIndex')) {
this.floorTabIndex = localStorage.getItem('dataScreenFloorTableIndex')
}
if (localStorage.getItem('dataScreenRegionTableIndex')) {
this.regionTabIndex = localStorage.getItem('dataScreenRegionTableIndex')
}
if (localStorage.getItem('dataScreenRegion')) {
const dataScreenRegion = JSON.parse(localStorage.getItem('dataScreenRegion'))
this.floorName = dataScreenRegion.floorName
this.regionName = dataScreenRegion.regionName
this.rowType = dataScreenRegion.rowType
// 单面/双面
this.tabListData = dataScreenRegion.rowType === 1
? dataScreenRegion.toward === 1
? [{ name: 'A面' }]
: [{ name: 'B面' }]
: [{ name: 'A面' }, { name: 'B面' }]
this.tabIndex = this.$route.query.tabIndex ? this.$route.query.tabIndex : 0
FetchBookShelfDetails({ 'shelfId': dataScreenRegion.id }).then(res => {
this
this.layerNum = res.shelfFloor
this.rackNum = res.shelfShelf
this.bookShelfDetails = res
if (this.$route.query.tabIndex) {
this.getInitShelfGridByShelfId(this.$route.query.tabIndex + 1)
} else {
this.getInitShelfGridByShelfId(this.bookShelfDetails.toward)
}
}).catch(() => {
})
}
},
methods: {
[CRUD.HOOK.beforeRefresh]() {
},
[CRUD.HOOK.afterRefresh](crud) {
},
// 提交前的验证
[CRUD.HOOK.afterValidateCU](crud) {
return true
},
toAdd(type) {
this.$refs.eform.formVisible = true
this.$refs.eform.form.shelfId = this.bookShelfDetails.id
this.$refs.eform.form.stockRegion = this.floorName + this.regionName + this.bookShelfDetails.shelfName
this.$refs.eform.setData(type)
},
removeAreaPrefix(gridNames) {
return gridNames.replace(/\d*区|\d*层/g, '')
},
getInitShelfGridByShelfId(toward) {
this.listLoading = true
// rowType 1 单 2 双
// toward 1 A面 2 B面
// shelfType 1 '始终最左边为第1架(S型排架)'
// shelfType 2 'A面最左为第1架(B面最左为最后1架)'
// shelfType 3 'B面最左为第1架(A面最左为最后1架)'
// floorType 1 '最顶层为第一层(从上至下)'
// floorType 2 '最底层为第一层(从下至上)'
FetchInitShelfGridByShelfId({ 'shelfId': this.bookShelfDetails.id, 'toward': toward }).then(res => {
const sortFunction = toward === 1 ? {
1: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
2: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
3: { 1: 'sortBookshelvesRightTop', 2: 'sortBookshelvesRightBottom' }
} : {
1: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
2: { 1: 'sortBookshelvesRightTop', 2: 'sortBookshelvesRightBottom' },
3: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' }
}
const shelfType = this.bookShelfDetails.shelfType
const floorType = this.bookShelfDetails.floorType
const sortMethod = sortFunction[shelfType][floorType]
this.booShelfGrid = this[sortMethod](res)
console.log('booShelfGrid', this.booShelfGrid)
this.popoverVisible = Array(this.booShelfGrid.length).fill(false)
setTimeout(() => {
this.listLoading = false
}, 1000)
}).catch(() => {
})
},
// 最左为第一架, 最顶层为第一层 从上往下
sortBookshelvesLeftTop(data) {
const sortedData = []
const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.slice(-1))))
for (let i = 1; i <= maxFloor; i++) {
for (let j = 1; j <= maxShelf; j++) {
const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.slice(-1)) === j)
if (currentShelf) {
sortedData.push(currentShelf)
}
}
}
return sortedData
},
// 最右为第一架,最左为最后一架, 最顶层为第一层 从上往下
sortBookshelvesRightTop(data) {
const sortedData = []
// 获取最大的楼层数
const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.match(/\d+$/)[0])))
for (let i = 1; i <= maxFloor; i++) {
// 从最大的书架层数开始,向下排序
for (let j = maxShelf; j >= 1; j--) {
const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.match(/\d+$/)[0]) === j)
if (currentShelf) {
sortedData.push(currentShelf)
}
}
}
return sortedData
},
// 最左为第一架, 最底层为第一层 从下往上
sortBookshelvesLeftBottom(data) {
const sortedData = []
// 获取最大的楼层数
const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
// 获取最大的书架层数
const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.slice(-1))))
for (let i = maxFloor; i >= 1; i--) {
for (let j = 1; j <= maxShelf; j++) {
const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.slice(-1)) === j)
if (currentShelf) {
sortedData.push(currentShelf)
}
}
}
return sortedData
},
// 最左为最后一架, 最底层为第一层 从下往上
sortBookshelvesRightBottom(data) {
const sortedData = []
// 获取最大的楼层数
const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
const maxShelfPerFloor = data.map(item => parseInt(item.gridShelf.match(/\d+$/)[0]))
.reduce((acc, curr, index, arr) => {
const floor = parseInt(data[index].gridFloor)
if (!acc[floor]) acc[floor] = 1
if (acc[floor] < curr) acc[floor] = curr
return acc
}, {})
// 从最大的楼层开始向下遍历
for (let i = maxFloor; i >= 1; i--) {
// 从最大的书架编号开始向左遍历
for (let j = maxShelfPerFloor[i] || 1; j >= 1; j--) {
const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.match(/\d+$/)[0]) === j)
if (currentShelf) {
sortedData.push(currentShelf)
}
}
}
return sortedData
},
changeActiveTab(index) {
this.tabIndex = index
this.cellIndex = null
this.getInitShelfGridByShelfId(index + 1)
},
handleCellCurrent(item, index) {
console.log('index', index)
console.log('item', item)
this.cellIndex = index
this.cellInfo = {
id: item.id,
gridName: item.gridName,
gridRow: item.gridRow,
gridShelf: item.gridShelf,
shelfId: item.shelfId,
floorName: this.floorName,
regionName: this.regionName,
rowType: this.rowType,
toward: item.toward
}
this.currentShelfAllGrid = this.booShelfGrid.filter(gird => gird.gridShelf === item.gridShelf)
this.handleToGrids(this.cellInfo, this.currentShelfAllGrid)
},
handleToGrids(data, currentShelfAllGrid) {
this.$router.push({ path: '/dataScreening/gird' })
localStorage.setItem('dataScreenShelf', JSON.stringify(data))
localStorage.setItem('dataScreenShelfAllGrid', JSON.stringify(currentShelfAllGrid))
localStorage.setItem('dataScreenShelfTableIndex', this.tabIndex)
},
showPopover(index) {
this.popoverIndex = index
if (!this.popoverVisible[index]) {
this.$set(this.popoverVisible, index, true)
}
const lastColumnIndexes = []
const secondLastColumnIndexes = []
for (let i = 0; i < this.booShelfGrid.length; i++) {
const columnIndex = i % this.rackNum
// 如果是最后一列(第5列)
console.log('this.booShelfGrid.length', this.booShelfGrid.length)
if (this.rackNum !== 1) {
if (columnIndex === this.rackNum - 1) {
lastColumnIndexes.push(i)
// 更新最后一列的样式
this.$set(this.popoverStyles, i, { position: 'absolute', left: '-180px', top: '20px' })
}
} else {
this.$set(this.popoverStyles, i, { position: 'absolute', left: '60px', top: '20px' })
}
// 如果是倒数第二列(第4列)
if (columnIndex === this.rackNum - 2) {
secondLastColumnIndexes.push(i)
// 更新倒数第二列的样式
// this.$set(this.popoverStyles, i, { position: 'absolute', left: '-20px', top: '20px' })
}
}
const columnIndex = index % this.rackNum
this.activeColumns[columnIndex] = true
},
isActiveColumn(index) {
const columnIndex = index % this.rackNum
return this.activeColumns[columnIndex] === true
},
hidePopover() {
this.activeColumns = {}
this.popoverIndex = null
this.popoverVisible.forEach((isVisible, index) => {
if (isVisible) {
this.$set(this.popoverVisible, index, false)
}
})
}
}
}
</script>
<style lang="scss" scoped>
.container-right{
min-height: calc(100vh - 232px) !important;
}
.data-right-list {
padding-top: 10px;
li{
display: flex;
justify-content: flex-start;
align-items: center;
line-height: 36px;
p{
width: 80px;
font-weight: bold;
text-align: right;
}
span{
width: 140px;
display: block;
text-align: right;
i{
font-style: normal;
font-weight: bold;
padding: 0 10px;
color: #0348f3;
}
&.percentage{
width: auto;
}
}
}
}
.tag-info{
position: absolute;
right: 20px;
top: 8px;
p{
margin-left: 20px;
}
}
.shelf-top{
display: flex;
justify-content: space-around;
align-items: center;
text-align: center;
margin-top: 20px;
p{
font-size: 14px;
color: #fff;
height: 36px;
line-height: 30px;
background: url('~@/assets/images/shelf04.png') no-repeat center top;
background-size: auto 100%;
}
}
.data-shelf-row{
display: flex;
flex: 1;
flex-wrap: wrap;
text-align: center;
// margin-top: -2px;
// padding-top: 40px;
.data-shelf-cell{
position: relative;
font-size: 14px;
color: #0C0E1E;
background: url('~@/assets/images/shelf02.png') repeat-x left top;
background-size: 20% 100%;
border-radius: 3px;
cursor: pointer;
.tag-info{
right: 12px;
top: 44px;
p{
margin-left: 10px;
}
}
&::before{
content: "";
position: absolute;
left: 0;
top: 0;
width: 6px;
height: 76px;
background: url('~@/assets/images/shelf01.png') no-repeat left top;
background-size: 100% 100%;
}
&::after{
content: "";
position: absolute;
right: -4px;
top: 0;
width: 6px;
height: 76px;
background: url('~@/assets/images/shelf01.png') no-repeat left top;
background-size: 100% 100%;
}
.cell-name{
display: block;
width: 100%;
line-height: 76px;
}
&.active{
.mask{
position: absolute;
top: 0;
left: 6px;
width: 98%;
height: 100%;
background-color: rgba(255,0,0,.3);
}
}
}
}
::v-deep .data-shelf-row span.el-popover__reference-wrapper{
position: absolute !important;
left: 60% !important;
top: 48px !important;
width: 250px;
// height: 210px;
background:rgba(0,0,0,1);
color: #fff;
border-radius: 6px;
z-index: 99999999;
}
</style>