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

545 lines
21 KiB

7 months ago
7 months ago
7 months ago
6 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
6 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
6 months ago
7 months ago
6 months ago
7 months ago
7 months ago
  1. <template>
  2. <div class="app-container">
  3. <div class="venue-header">
  4. <h4><i class="iconfont icon-shujia" />架位列表</h4>
  5. <span class="bookshelf-area">{{ floorName }} - {{ regionName }}</span>
  6. <p><i class="iconfont icon-gongsi" />{{ user.fonds.fondsName }}</p>
  7. </div>
  8. <div class="bookshelf-main">
  9. <div class="bookshelf-top">
  10. <ul class="bookshelf-info">
  11. <li><p>书架名称</p><span>{{ bookShelfDetails && bookShelfDetails.shelfName }}</span></li>
  12. <li><p>书架规格</p><span>{{ bookShelfDetails && bookShelfDetails.shelfFloor + ' X ' + bookShelfDetails.shelfShelf }}</span></li>
  13. <li><p>/双面</p><span>{{ bookShelfDetails && bookShelfDetails.rowType === 1 ? '单面' :'双面' }}</span></li>
  14. <li><p>倒架规则</p><span>{{ bookShelfDetails && bookShelfDetails.shelfRule === 1 ? '无序' :'有序' }}</span></li>
  15. <li><p>错架判断</p><span>{{ bookShelfDetails && bookShelfDetails.shelfErrorJudge === 1 ? '书架' :'格子' }}</span></li>
  16. <!-- <li><p>前端测试用-架号顺序</p><span>{{ bookShelfDetails && bookShelfDetails.shelfType === 1 ? '始终最左边为第1架(S型排架)' : (bookShelfDetails.shelfType === 2 ? 'A面最左为第1架(B面最左为最后1架)' : 'B面最左为第1架(A面最左为最后1架)') }}</span></li>
  17. <li><p>前端测试用-层号顺序</p><span>{{ bookShelfDetails && bookShelfDetails.floorType === 1 ? '最顶层为第一层(从上至下)' :'最底层为第一层(从下至上)' }}</span></li> -->
  18. </ul>
  19. <div class="bookshelf-button">
  20. <el-button size="mini" @click="doExport">
  21. <i class="iconfont icon-daochu" />
  22. 导出层位编码
  23. </el-button>
  24. <el-button size="mini" :disabled="!cellInfo.cameraId">
  25. <i class="iconfont icon-yulan" />
  26. {{ cellInfo.cameraId ? '摄像头预览' : '未绑定摄像头' }}
  27. </el-button>
  28. </div>
  29. </div>
  30. <div class="bookshelf-layer-info">
  31. <div class="bookshelf-left">
  32. <swiper
  33. ref="swiperTitle"
  34. class="swiper-title"
  35. :options="swiperOptionTitle"
  36. :auto-update="true"
  37. :auto-destroy="true"
  38. :delete-instance-on-destroy="true"
  39. :cleanup-styles-on-destroy="true"
  40. >
  41. <swiper-slide
  42. v-for="(item, index) of tabListData"
  43. ref="swiperSlideItem"
  44. :key="'name' + index"
  45. :iname="item.name"
  46. class="swiper-slide-title"
  47. >
  48. <div
  49. class="tab-name"
  50. :class="{ active: index === swiperActiveIndex }"
  51. @click="handleSlidClickFun(index)"
  52. >
  53. {{ item.name }}
  54. </div>
  55. </swiper-slide>
  56. </swiper>
  57. <swiper
  58. ref="swiperContent"
  59. class="swiper-content"
  60. :options="swiperOptionContent"
  61. :auto-update="true"
  62. :auto-destroy="true"
  63. :delete-instance-on-destroy="true"
  64. :cleanup-styles-on-destroy="true"
  65. >
  66. <swiper-slide
  67. v-for="(item, index) of tabListData"
  68. :key="'content' + index"
  69. class="swiper-slide-content"
  70. >
  71. <ul class="cabinet-row">
  72. <li v-for="(item,index) in booShelfGrid" :key="index" class="cabinet-cell" :style="cellStyle" :class="{ active: index === cellIndex }" @click="handleCellCurrent(item,index)">
  73. <span>{{ item.gridName }}</span>
  74. </li>
  75. </ul>
  76. </swiper-slide>
  77. </swiper>
  78. </div>
  79. <div class="bookshelf-right-info">
  80. <div class="layer-status">
  81. <span v-if="cellInfo.startSortmark && cellInfo.endSortmark && checkValue === 'true'" class="row-state end-state">正常盘点</span>
  82. <span v-if="!cellInfo.startSortmark && !cellInfo.endSortmark && checkValue === 'true'" class="row-state soon-state">待初始化</span>
  83. <span v-if="checkValue === 'false'" class="row-state cancel-state">停止盘点</span>
  84. <span v-if="bookSortValue === 'false'" class="row-state other-state">无序倒架</span>
  85. <span v-else class="row-state ing-state">有序倒架</span>
  86. </div>
  87. <h5 class="layer-name">{{ cellInfo.gridName }}</h5>
  88. <div class="layer-code-sort">
  89. <ul>
  90. <!-- I247.58/586 -->
  91. <li><p>起始索书号</p><span>{{ cellInfo.startSortmark ? cellInfo.startSortmark : '-' }}</span></li>
  92. <li><p>结束索书号</p><span>{{ cellInfo.endSortmark? cellInfo.endSortmark : '-' }}</span></li>
  93. </ul>
  94. <el-button size="mini" class="edit-callNumber" @click="handleEditGridNum">
  95. <!-- <i class="iconfont icon-yulan" /> -->
  96. <i>&nbsp;&nbsp;&nbsp;</i>
  97. <i>索书号</i>
  98. </el-button>
  99. </div>
  100. <ul class="layer-handle">
  101. <li>
  102. <p>层位盘点开关</p>
  103. <el-switch
  104. v-model="checkValue"
  105. active-color="#13ce66"
  106. inactive-color="#ff4949"
  107. active-value="true"
  108. inactive-value="false"
  109. @change="changeCheckSwitch"
  110. />
  111. </li>
  112. <li>
  113. <p>图书有序检查</p>
  114. <el-switch
  115. v-model="bookSortValue"
  116. active-color="#13ce66"
  117. inactive-color="#ff4949"
  118. active-value="true"
  119. inactive-value="false"
  120. @change="changeBookSortSwitch"
  121. />
  122. </li>
  123. </ul>
  124. </div>
  125. </div>
  126. </div>
  127. <!-- 编辑索书号 -->
  128. <el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :visible="callNumVisible" title="编辑索书号范围" :before-close="handleClose">
  129. <span class="dialog-right-top" />
  130. <span class="dialog-left-bottom" />
  131. <div class="setting-dialog">
  132. <el-form ref="form" :rules="rules" :model="form" size="small" label-width="100px">
  133. <el-form-item label="所属架位" prop="gridName">
  134. <el-input v-model="form.gridName" disabled style="width: 580px;" />
  135. </el-form-item>
  136. <el-form-item label="起始索书号" prop="startSortmark">
  137. <el-input v-model="form.startSortmark" style="width: 580px;" />
  138. </el-form-item>
  139. <el-form-item label="结束索书号" prop="endSortmark">
  140. <el-input v-model="form.endSortmark" style="width: 580px;" />
  141. </el-form-item>
  142. </el-form>
  143. <div slot="footer" class="dialog-footer">
  144. <el-button type="text" @click.native="handleClose">取消</el-button>
  145. <el-button type="primary" @click.native="handleSaveCallNum">保存</el-button>
  146. </div>
  147. </div>
  148. </el-dialog>
  149. </div>
  150. </template>
  151. <script>
  152. // https://blog.csdn.net/qq_37236395/article/details/119737898
  153. import { FetchInitShelfGridByShelfId, FetcheEditSortmarkByGrid, FetchChangeOrderByGrid, FetchChangeCheckByGrid } from '@/api/shelf/index'
  154. import { mapGetters } from 'vuex'
  155. import { swiper, swiperSlide } from 'vue-awesome-swiper'
  156. import 'swiper/dist/css/swiper.css'
  157. import { parseTime, saveAs, getBlob } from '@/utils/index'
  158. import qs from 'qs'
  159. export default {
  160. name: 'BookshelfPosition',
  161. components: { swiper, swiperSlide },
  162. data() {
  163. const _this = this
  164. return {
  165. floorName: null,
  166. regionName: null,
  167. bookShelfDetails: null,
  168. booShelfGrid: null,
  169. cellInfo: {
  170. gridName: null,
  171. startSortmark: null,
  172. endSortmark: null,
  173. cameraId: null
  174. },
  175. callNumVisible: false,
  176. layerNum: 0,
  177. rackNum: 0,
  178. checkValue: 'true',
  179. bookSortValue: 'true',
  180. swiperActiveIndex: 0,
  181. cellIndex: null,
  182. swiperOptionContent: {
  183. slidesPerView: 'auto',
  184. on: {
  185. slideChangeTransitionStart: function() {
  186. _this.cellIndex = null
  187. _this.swiperActiveIndex = this.activeIndex
  188. console.log('activeIndexffff', this.swiperActiveIndex)
  189. _this.swiperTitle.slideTo(this.activeIndex, 500, false)
  190. }
  191. }
  192. },
  193. swiperOptionTitle: {
  194. slidesPerView: 'auto',
  195. freeMode: true
  196. },
  197. tabListData: [],
  198. form: {
  199. id: null,
  200. gridName: null,
  201. startSortmark: null,
  202. endSortmark: null,
  203. check: null,
  204. order: null,
  205. cameraId: null
  206. },
  207. rules: {
  208. gridName: [
  209. { required: true, message: '所属架位不可为空', trigger: 'blur' }
  210. ],
  211. startSortmark: [
  212. { required: true, message: '起始索书号不可为空', trigger: 'blur' }
  213. ],
  214. endSortmark: [
  215. { required: true, message: '结束索书号不可为空', trigger: 'blur' }
  216. ]
  217. }
  218. }
  219. },
  220. computed: {
  221. ...mapGetters([
  222. 'user',
  223. 'baseApi'
  224. ]),
  225. swiperContent() {
  226. return this.$refs.swiperContent.$el.swiper
  227. },
  228. swiperTitle() {
  229. return this.$refs.swiperTitle.$el.swiper
  230. },
  231. cellStyle: function() {
  232. const h = '100%/' + this.layerNum
  233. const w = '100%/' + this.rackNum
  234. return { width: `calc(${w} - 4px )`, height: `calc(${h} - 2px)` }
  235. }
  236. },
  237. watch: {
  238. '$route'(val, from) { // 监听到路由(参数)改变
  239. if (this.$route.query) {
  240. this.floorName = this.$route.query.floorName
  241. this.regionName = this.$route.query.regionName
  242. }
  243. }
  244. },
  245. async created() {
  246. if (this.$route.query) {
  247. this.floorName = this.$route.query.floorName
  248. this.regionName = this.$route.query.regionName
  249. }
  250. if (localStorage.getItem('bookShelfDetails')) {
  251. this.bookShelfDetails = JSON.parse(localStorage.getItem('bookShelfDetails'))
  252. this.layerNum = this.bookShelfDetails.shelfFloor
  253. this.rackNum = this.bookShelfDetails.shelfShelf
  254. // 单面/双面
  255. this.tabListData = this.bookShelfDetails.rowType === 1
  256. ? this.bookShelfDetails.toward === 1
  257. ? [{ name: 'A面' }]
  258. : [{ name: 'B面' }]
  259. : [{ name: 'A面' }, { name: 'B面' }]
  260. // 层数据
  261. this.getInitShelfGridByShelfId(this.bookShelfDetails.toward)
  262. }
  263. },
  264. mounted() {
  265. },
  266. methods: {
  267. getInitShelfGridByShelfId(toward) {
  268. // rowType 1 单 2 双
  269. // toward 1 A面 2 B面
  270. // shelfType 1 '始终最左边为第1架(S型排架)'
  271. // shelfType 2 'A面最左为第1架(B面最左为最后1架)'
  272. // shelfType 3 'B面最左为第1架(A面最左为最后1架)'
  273. // floorType 1 '最顶层为第一层(从上至下)'
  274. // floorType 2 '最底层为第一层(从下至上)'
  275. FetchInitShelfGridByShelfId({ 'shelfId': this.bookShelfDetails.id, 'toward': toward }).then(res => {
  276. const sortFunction = toward === 1 ? {
  277. 1: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
  278. 2: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
  279. 3: { 1: 'sortBookshelvesRightTop', 2: 'sortBookshelvesRightBottom' }
  280. } : {
  281. 1: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' },
  282. 2: { 1: 'sortBookshelvesRightTop', 2: 'sortBookshelvesRightBottom' },
  283. 3: { 1: 'sortBookshelvesLeftTop', 2: 'sortBookshelvesLeftBottom' }
  284. }
  285. const shelfType = this.bookShelfDetails.shelfType
  286. const floorType = this.bookShelfDetails.floorType
  287. const sortMethod = sortFunction[shelfType][floorType]
  288. this.booShelfGrid = this[sortMethod](res)
  289. }).catch(() => {
  290. })
  291. },
  292. // 最左为第一架, 最顶层为第一层 从上往下
  293. sortBookshelvesLeftTop(data) {
  294. const sortedData = []
  295. const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
  296. const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.slice(-1))))
  297. for (let i = 1; i <= maxFloor; i++) {
  298. for (let j = 1; j <= maxShelf; j++) {
  299. const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.slice(-1)) === j)
  300. if (currentShelf) {
  301. sortedData.push(currentShelf)
  302. }
  303. }
  304. }
  305. return sortedData
  306. },
  307. // 最右为第一架,最左为最后一架, 最顶层为第一层 从上往下
  308. sortBookshelvesRightTop(data) {
  309. const sortedData = []
  310. // 获取最大的楼层数
  311. const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
  312. const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.match(/\d+$/)[0])))
  313. for (let i = 1; i <= maxFloor; i++) {
  314. // 从最大的书架层数开始,向下排序
  315. for (let j = maxShelf; j >= 1; j--) {
  316. const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.match(/\d+$/)[0]) === j)
  317. if (currentShelf) {
  318. sortedData.push(currentShelf)
  319. }
  320. }
  321. }
  322. return sortedData
  323. },
  324. // 最左为第一架, 最底层为第一层 从下往上
  325. sortBookshelvesLeftBottom(data) {
  326. const sortedData = []
  327. // 获取最大的楼层数
  328. const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
  329. // 获取最大的书架层数
  330. const maxShelf = Math.max(...data.map(item => parseInt(item.gridShelf.slice(-1))))
  331. for (let i = maxFloor; i >= 1; i--) {
  332. for (let j = 1; j <= maxShelf; j++) {
  333. const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.slice(-1)) === j)
  334. if (currentShelf) {
  335. sortedData.push(currentShelf)
  336. }
  337. }
  338. }
  339. return sortedData
  340. },
  341. // 最左为最后一架, 最底层为第一层 从下往上
  342. sortBookshelvesRightBottom(data) {
  343. const sortedData = []
  344. // 获取最大的楼层数
  345. const maxFloor = Math.max(...data.map(item => parseInt(item.gridFloor)))
  346. const maxShelfPerFloor = data.map(item => parseInt(item.gridShelf.match(/\d+$/)[0]))
  347. .reduce((acc, curr, index, arr) => {
  348. const floor = parseInt(data[index].gridFloor)
  349. if (!acc[floor]) acc[floor] = 1
  350. if (acc[floor] < curr) acc[floor] = curr
  351. return acc
  352. }, {})
  353. // 从最大的楼层开始向下遍历
  354. for (let i = maxFloor; i >= 1; i--) {
  355. // 从最大的书架编号开始向左遍历
  356. for (let j = maxShelfPerFloor[i] || 1; j >= 1; j--) {
  357. const currentShelf = data.find(item => parseInt(item.gridFloor) === i && parseInt(item.gridShelf.match(/\d+$/)[0]) === j)
  358. if (currentShelf) {
  359. sortedData.push(currentShelf)
  360. }
  361. }
  362. }
  363. return sortedData
  364. },
  365. handleSlidClickFun(index) {
  366. this.cellIndex = null
  367. this.handleSlideToFun(index)
  368. if (localStorage.getItem('bookShelfDetails')) {
  369. this.getInitShelfGridByShelfId(index + 1)
  370. }
  371. },
  372. handleSlideToFun(index) {
  373. this.swiperActiveIndex = index
  374. this.swiperContent.slideTo(index, 500, false)
  375. this.swiperTitle.slideTo(index, 500, false)
  376. },
  377. handleCellCurrent(item, index) {
  378. this.cellIndex = index
  379. this.cellInfo = {
  380. id: item.id,
  381. gridName: item.gridName,
  382. startSortmark: item.startSortmark,
  383. endSortmark: item.endSortmark,
  384. cameraId: item.cameraId,
  385. check: item.isCheck,
  386. order: item.isOrder
  387. }
  388. this.checkValue = `${item.isCheck}`
  389. this.bookSortValue = `${item.isOrder}`
  390. },
  391. handleEditGridNum() {
  392. console.log(this.cellIndex)
  393. if (this.cellIndex !== null) {
  394. this.callNumVisible = true
  395. this.form = this.cellInfo
  396. console.log(this.cellInfo)
  397. } else {
  398. this.$message({ message: '请选择需要操作得层位', type: 'error', offset: 8 })
  399. }
  400. },
  401. handleSaveCallNum() {
  402. if (this.$refs['form']) {
  403. this.$refs['form'].validate((valid) => {
  404. if (valid) {
  405. console.log(this.form)
  406. FetcheEditSortmarkByGrid(this.form).then(res => {
  407. console.log(res)
  408. if (res) {
  409. this.$message({ message: '编辑索书号范围成功', type: 'success', offset: 8 })
  410. this.callNumVisible = false
  411. }
  412. }).catch(() => {
  413. })
  414. }
  415. })
  416. }
  417. },
  418. handleClose() {
  419. this.$refs['form'].resetFields()
  420. this.callNumVisible = false
  421. },
  422. // 盘点
  423. changeCheckSwitch(data) {
  424. console.log(data)
  425. if (this.cellIndex !== null) {
  426. this.$confirm('此操作将开启/关闭该层位的盘点功能' + '<span>你是否还要继续?</span>', '提示', {
  427. confirmButtonText: '继续',
  428. cancelButtonText: '取消',
  429. type: 'warning',
  430. dangerouslyUseHTMLString: true
  431. }).then(() => {
  432. this.cellInfo.check = JSON.parse(data)
  433. console.log(this.cellInfo)
  434. FetchChangeCheckByGrid(this.cellInfo).then(res => {
  435. this.$message({ message: '修改层位盘点状态成功', type: 'success', offset: 8 })
  436. if (localStorage.getItem('bookShelfDetails')) {
  437. this.getInitShelfGridByShelfId(this.swiperActiveIndex + 1)
  438. }
  439. }).catch(() => {
  440. this.checkValue = data === 'true' ? 'false' : 'true'
  441. })
  442. }).catch(() => {
  443. this.checkValue = data === 'true' ? 'false' : 'true'
  444. })
  445. } else {
  446. this.$message({ message: '请选择需要操作得层位', type: 'error', offset: 8 })
  447. this.checkValue = data === 'true' ? 'false' : 'true'
  448. return false
  449. }
  450. },
  451. changeBookSortSwitch(data) {
  452. console.log(data)
  453. if (this.cellIndex !== null) {
  454. this.$confirm('此操作将开启/关闭该层位的图书顺序检查' + '<span>你是否还要继续?</span>', '提示', {
  455. confirmButtonText: '继续',
  456. cancelButtonText: '取消',
  457. type: 'warning',
  458. dangerouslyUseHTMLString: true
  459. }).then(() => {
  460. this.cellInfo.order = JSON.parse(data)
  461. FetchChangeOrderByGrid(this.cellInfo).then(res => {
  462. this.$message({ message: '修改图书有序检查状态成功', type: 'success', offset: 8 })
  463. if (localStorage.getItem('bookShelfDetails')) {
  464. this.getInitShelfGridByShelfId(this.swiperActiveIndex + 1)
  465. }
  466. }).catch(() => {
  467. this.bookSortValue = data === 'true' ? 'false' : 'true'
  468. })
  469. }).catch(() => {
  470. this.bookSortValue = data === 'true' ? 'false' : 'true'
  471. })
  472. } else {
  473. this.$message({ message: '请选择需要操作得层位', type: 'error', offset: 8 })
  474. this.bookSortValue = data === 'true' ? 'false' : 'true'
  475. return false
  476. }
  477. },
  478. doExport() {
  479. console.log(this.bookShelfDetails)
  480. this.$confirm('此操作将导出所选数据' + '<span>你是否还要继续?</span>', '提示', {
  481. confirmButtonText: '继续',
  482. cancelButtonText: '取消',
  483. type: 'warning',
  484. dangerouslyUseHTMLString: true
  485. }).then(() => {
  486. const params = {
  487. 'shelfId': this.bookShelfDetails.id,
  488. 'toward': this.bookShelfDetails.toward
  489. }
  490. console.log(params)
  491. const fileName = '层位编码-' + parseTime(new Date()) + '.xlsx'
  492. getBlob(this.baseApi + '/api/bookShelf/exportShelfGridLabel' + '?' + qs.stringify(params, { indices: false }), function(blob) {
  493. saveAs(blob, fileName)
  494. })
  495. }).catch(() => {
  496. })
  497. }
  498. }
  499. }
  500. </script>
  501. <style lang="scss" scoped>
  502. .venue-header{
  503. h4{
  504. flex: 1;
  505. }
  506. .bookshelf-area{
  507. padding-right: 30px;
  508. font-weight: bold;
  509. color: #0348F3;
  510. }
  511. }
  512. .swiper-title{
  513. ::v-deep .swiper-wrapper{
  514. margin: 10px 0;
  515. border-bottom: 1px solid #EDEFF3;
  516. }
  517. }
  518. .swiper-slide-title {
  519. width: auto !important;
  520. margin-right: 20px;
  521. cursor: pointer;
  522. .tab-name {
  523. padding: 10px;
  524. &.active {
  525. color: #0348F3;
  526. border-bottom: 3px solid #0348F3;
  527. }
  528. }
  529. }
  530. .swiper-content{
  531. height: 544px;
  532. }
  533. .swiper-slide-content {
  534. // padding: 0 10px;
  535. // margin: 0 10px 0 0;
  536. }
  537. </style>