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

686 lines
22 KiB

<template>
<div class="app-container">
<div class="venue-header">
<h4 @click="handleToGrids"><i class="iconfont icon-shuju" />书架总览</h4>
<span class="bookshelf-area">{{ floorName }} - {{ regionName }}</span>
<p><i class="iconfont icon-gongsi" />{{ user.fonds.fondsName }}</p>
</div>
<div class="venue-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: 20px;">
<el-option v-for="item in layerOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-button v-permission="permission.add" size="mini" @click="crud.toAdd">
<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" />
<!-- <div style="display: flex;justify-content: flex-start; align-items: center;"> -->
<swiper
ref="swiperTitle"
class="swiper-title"
:options="swiperOptionTitle"
:auto-update="true"
:auto-destroy="true"
:delete-instance-on-destroy="true"
:cleanup-styles-on-destroy="true"
>
<swiper-slide
v-for="(item, index) of tabListData"
ref="swiperSlideItem"
:key="'name' + index"
:iname="item.name"
class="swiper-slide-title"
>
<div
class="tab-name"
:class="{ active: index === swiperActiveIndex }"
@click="handleSlidClickFun(index)"
>
{{ item.name }}
</div>
</swiper-slide>
</swiper>
<div class="tag-info">
<p>错序:1</p>
<p>错架:1</p>
<p>在架:20</p>
</div>
<!-- </div> -->
<swiper
ref="swiperContent"
class="swiper-content"
:options="swiperOptionContent"
:auto-update="true"
:auto-destroy="true"
:delete-instance-on-destroy="true"
:cleanup-styles-on-destroy="true"
>
<swiper-slide
v-for="(item, index) of tabListData"
:key="'content' + index"
class="swiper-slide-content"
>
<ul class="cabinet-row">
<li
v-for="(cell,i) in booShelfGrid"
:key="i"
class="cabinet-cell"
:style="cellStyle"
:class="{ active: i === cellIndex }"
@click="handleCellCurrent(cell,i)"
@mouseenter="showPopover(i)"
@mouseleave="hidePopover"
>
<span class="cell-name">{{ removeAreaPrefix(cell.gridName) }}</span>
<!-- v-if="popoverIndex === i" -->
<el-popover
v-if="popoverIndex === i"
ref="popover"
:visible="popoverVisible[i]"
width="400"
:style="popoverStyles[i]"
trigger="manual"
>
<div slot="reference" class="popover-content">
<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>
</swiper-slide>
</swiper>
</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>
<!-- form -->
<el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="盘点单号" prop="taskName">
<el-input v-model="form.taskName" />
</el-form-item>
<el-form-item label="盘点类型" prop="taskType">
<el-input v-model="form.taskType" />
</el-form-item>
<el-form-item label="目标位置" prop="location">
<el-input v-model="form.location" />
</el-form-item>
<el-form-item label="目标数量" prop="number">
<el-input v-model="form.number" />
</el-form-item>
<el-row>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" style="width: 572px;" :rows="4" />
</el-form-item>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">保存</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { FetchInitShelfGridByShelfId, FetchBookShelfDetails } from '@/api/shelf/index'
import crudRegion from '@/api/area/index'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation'
import { mapGetters } from 'vuex'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
const defaultForm = { id: null, taskType: null, taskName: null, location: null, number: null, remark: null }
export default {
name: 'DataScreening',
components: { swiper, swiperSlide, crudOperation },
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(), form(defaultForm), crud()],
data() {
const _this = this
return {
floorName: null,
regionName: null,
bookShelfDetails: null,
booShelfGrid: null,
cellInfo: {
gridName: null,
startSortmark: null,
endSortmark: null,
cameraId: null
},
callNumVisible: false,
layerNum: 0,
rackNum: 0,
swiperActiveIndex: 0,
cellIndex: null,
swiperOptionContent: {
slidesPerView: 'auto',
on: {
slideChangeTransitionStart: function() {
_this.cellIndex = null
_this.swiperActiveIndex = this.activeIndex
console.log('activeIndexffff', this.swiperActiveIndex)
_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']
},
rules: {
taskName: [
{ required: true, message: '请输入盘点单号', trigger: 'blur' }
],
taskType: [
{ required: true, message: '请输入盘点类型', trigger: 'blur' }
],
location: [
{ required: true, message: '请输入目标位置', trigger: 'blur' }
],
number: [
{ required: true, message: '请输入目标数量', trigger: 'blur' }
]
},
popoverIndex: 1,
popoverVisible: [],
popoverStyles: []
}
},
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 = '70px'
const w = '100%/' + this.rackNum
return { width: `calc(${w} - 4px )`, height: `calc(${h} - 2px)` }
}
},
async created() {
if (localStorage.getItem('dataScreenRegion')) {
const dataScreenRegion = JSON.parse(localStorage.getItem('dataScreenRegion'))
this.floorName = dataScreenRegion.floorName
this.regionName = dataScreenRegion.regionName
// 单面/双面
this.tabListData = dataScreenRegion.rowType === 1
? dataScreenRegion.toward === 1
? [{ name: 'A面' }]
: [{ name: 'B面' }]
: [{ name: 'A面' }, { name: 'B面' }]
FetchBookShelfDetails({ 'shelfId': dataScreenRegion.id }).then(res => {
this.layerNum = res.shelfFloor
this.rackNum = res.shelfShelf
this.bookShelfDetails = res
this.getInitShelfGridByShelfId(this.bookShelfDetails.toward)
}).catch(() => {
})
}
},
methods: {
handleToGrids() {
this.$router.push({ path: '/dataScreening/gird', query: { }})
},
[CRUD.HOOK.beforeRefresh]() {
},
[CRUD.HOOK.afterRefresh](crud) {
},
// 提交前的验证
[CRUD.HOOK.afterValidateCU](crud) {
return true
},
removeAreaPrefix(gridNames) {
const index = gridNames.indexOf('区')
if (index !== -1) {
return gridNames.substring(index + 1)
}
return gridNames
},
getInitShelfGridByShelfId(toward) {
// 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)
this.popoverVisible = Array(this.booShelfGrid.length).fill(false)
// this.popoverStyles = new Array(this.booShelfGrid.length).fill({ position: 'absolute', left: '20%', top: '48px' })
}).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
},
handleSlidClickFun(index) {
this.cellIndex = null
this.handleSlideToFun(index)
if (localStorage.getItem('bookShelfDetails')) {
this.getInitShelfGridByShelfId(index + 1)
}
},
handleSlideToFun(index) {
this.swiperActiveIndex = index
this.swiperContent.slideTo(index, 500, false)
this.swiperTitle.slideTo(index, 500, false)
},
handleCellCurrent(item, index) {
console.log('index', index)
this.cellIndex = index
this.cellInfo = {
id: item.id,
gridName: item.gridName,
startSortmark: item.startSortmark,
endSortmark: item.endSortmark,
cameraId: item.cameraId,
check: item.isCheck,
order: item.isOrder
}
},
showPopover(index) {
this.popoverIndex = index
// 显示对应的 popover
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列)
if (columnIndex === this.rackNum - 1) {
lastColumnIndexes.push(i)
// 更新最后一列的样式
this.$set(this.popoverStyles, i, { position: 'absolute', left: '-140px', top: '48px' })
}
// 如果是倒数第二列(第4列)
if (columnIndex === this.rackNum - 2) {
secondLastColumnIndexes.push(i)
// 更新倒数第二列的样式
this.$set(this.popoverStyles, i, { position: 'absolute', left: '-20px', top: '48px' })
}
}
},
hidePopover() {
this.popoverIndex = null
// 隐藏所有的popover
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;
}
.venue-content{
position: relative;
}
.crud-opts{
position: absolute;
right: 20px;
top: 10px;
}
.venue-left{
flex: 1;
margin-right: 0 !important;
.venue-preview{
height: 633px !important;
}
}
.venue-right{
display: flex;
flex-direction: column;
width: 400px;
padding: 50px 10px 20px 10px !important;
.lib-right-item{
position: relative;
padding-bottom: 10px;
margin-bottom: 10px;
border: 1px solid #E8F2FF;
border-radius: 4px;
h4{
padding: 6px 10px;
background-color: #E8F2FF;
color: #000;
line-height: 30px;
border-bottom: 1px solid #edeff3;
}
.refresh-date{
position: absolute;
right: 14px;
top: 10px;
font-size: 12px;
line-height: 30px;
}
}
}
.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;
}
}
}
}
.swiper-title{
::v-deep .swiper-wrapper{
margin: 10px 0;
border-bottom: 1px solid #EDEFF3;
}
}
.swiper-slide-title {
width: auto !important;
margin-right: 20px;
cursor: pointer;
.tab-name {
padding: 10px;
&.active {
color: #0348F3;
border-bottom: 3px solid #0348F3;
}
}
}
.swiper-content{
height: 610px;
}
.tag-info{
position: absolute;
right: 20px;
top: 34px;
display: flex;
justify-content: flex-start;
padding-left: 100px;
p{
margin-left: 20px;
margin-top: -4px;
font-size: 14px;
}
}
.cabinet-row .cabinet-cell{
background: url('~@/assets/images/shelf02.png') repeat-x left top;
background-size: 100% 100%;
border: none;
overflow: inherit;
cursor: pointer;
&::before{
content: "";
position: absolute;
left: 0;
top: 0;
width: 6px;
height: 68px;
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: 68px;
background: url('~@/assets/images/shelf01.png') no-repeat left top;
background-size: 100% 100%;
}
span.cell-name{
position: initial !important;
transform: none;
line-height: 68px;
}
}
::v-deep .cabinet-row .cabinet-cell span.el-popover__reference-wrapper{
position: absolute !important;
left: 50% !important;
top: 20px !important;
transform: none;
width: 300px;
height: 210px;
background:rgba(0,0,0,.8);
color: #fff;
border-radius: 6px;
z-index: 99999999;
.popover-content{
.tooltip-top{
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
line-height: 40px;
padding: 0 10px;
border-bottom: 1px solid #fff;
}
.tooltip-top i{
font-style: normal;
font-size: 12px;
}
ul{
padding: 10px;
}
ul li{
display: flex;
justify-content: flex-start;
align-items: center;
line-height: 36px;
font-style: normal;
}
ul li p{
width: 80px;
font-weight: bold;
text-align: right;
}
ul li em{
width: 100px;
display: block;
text-align: right;
font-style: normal;
}
ul li i{
font-style: normal;
font-weight: bold;
padding: 0 10px;
color: #0348f3;
}
ul li em.percentage{
width: auto;
}
}
}
</style>