Browse Source

江夏查询机

master
xuhuajiao 1 year ago
parent
commit
d34a033dd0
  1. 8
      .env.production
  2. 197
      .eslintrc.js
  3. 23
      .gitignore
  4. 32
      README.md
  5. 11
      babel.config.js
  6. 57
      package.json
  7. BIN
      public/favicon.ico
  8. 19
      public/index.html
  9. 4
      public/static/config.js
  10. 16
      src/App.vue
  11. 130
      src/api/inquiryMachine.js
  12. BIN
      src/assets/fonts/HuXiaoBoNanShenTi.otf
  13. BIN
      src/assets/fonts/YouSheBiaoTiHei.ttf
  14. BIN
      src/assets/fonts/ZhenyanGB.ttf
  15. 19
      src/assets/fonts/fonts.css
  16. 627
      src/assets/iconfont/iconfont.css
  17. 1
      src/assets/iconfont/iconfont.js
  18. 1080
      src/assets/iconfont/iconfont.json
  19. BIN
      src/assets/iconfont/iconfont.ttf
  20. BIN
      src/assets/iconfont/iconfont.woff
  21. BIN
      src/assets/iconfont/iconfont.woff2
  22. BIN
      src/assets/images/bg.png
  23. BIN
      src/assets/images/border.png
  24. BIN
      src/assets/images/default-img-bg.jpg
  25. BIN
      src/assets/images/default-img.png
  26. BIN
      src/assets/images/default-img22.png
  27. BIN
      src/assets/images/img10.png
  28. BIN
      src/assets/images/img3.png
  29. BIN
      src/assets/images/img5.png
  30. BIN
      src/assets/images/img6.png
  31. BIN
      src/assets/images/img7.png
  32. BIN
      src/assets/images/img8.png
  33. BIN
      src/assets/images/img9.png
  34. BIN
      src/assets/images/index/320 180 down.png
  35. BIN
      src/assets/images/index/320 180 up.png
  36. BIN
      src/assets/images/index/400 620 left.png
  37. BIN
      src/assets/images/index/400 620 right.png
  38. BIN
      src/assets/images/index/760 180 down.png
  39. BIN
      src/assets/images/index/760 180 up.png
  40. BIN
      src/assets/images/index/activities.png
  41. BIN
      src/assets/images/index/bg.png
  42. BIN
      src/assets/images/index/brief.png
  43. BIN
      src/assets/images/index/digital.png
  44. BIN
      src/assets/images/index/guide.png
  45. BIN
      src/assets/images/index/logo.png
  46. BIN
      src/assets/images/index/newbook.png
  47. BIN
      src/assets/images/index/search.png
  48. BIN
      src/assets/images/index/title.png
  49. BIN
      src/assets/images/list-title.png
  50. BIN
      src/assets/images/local-bg.png
  51. BIN
      src/assets/images/shad2.png
  52. BIN
      src/assets/images/shelf01.png
  53. BIN
      src/assets/images/shelf02.png
  54. BIN
      src/assets/images/shelf03.png
  55. BIN
      src/assets/images/shelf04.png
  56. BIN
      src/assets/images/shelf05.png
  57. BIN
      src/assets/images/shelf06.png
  58. BIN
      src/assets/images/top-bg.png
  59. BIN
      src/assets/images/top.png
  60. BIN
      src/assets/logo.png
  61. 829
      src/assets/styles/index.scss
  62. 180
      src/assets/styles/style.scss
  63. 147
      src/common/flexible.js
  64. 61
      src/common/scrollMixins.js
  65. 39
      src/main.js
  66. 54
      src/router/index.js
  67. 15
      src/store/index.js
  68. 92
      src/utils/index.js
  69. 55
      src/utils/request.js
  70. 26
      src/utils/resizeMixins.js
  71. 181
      src/views/bookList.vue
  72. 546
      src/views/girdList.vue
  73. 163
      src/views/index.vue
  74. 344
      src/views/mixins/index.js
  75. 195
      src/views/module/bookDetails.vue
  76. 337
      src/views/module/canvasPreview.vue
  77. 145
      src/views/module/ranking.vue
  78. 133
      src/views/module/search.vue
  79. 141
      src/views/module/shelfRanking.vue
  80. 195
      src/views/regionsList.vue
  81. 629
      src/views/shelfList.vue
  82. 69
      vue.config.js

8
.env.production

@ -0,0 +1,8 @@
ENV = 'production'
# 如果使用 Nginx 代理后端接口,那么此处需要改为 '/',文件查看 Docker 部署篇,Nginx 配置
# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http
VUE_APP_BASE_API = 'http://192.168.3.220:12010'
# 如果接口是 http 形式, wss 需要改为 ws
VUE_APP_WS_API = 'ws://192.168.3.220:12011'

197
.eslintrc.js

@ -0,0 +1,197 @@
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: {
browser: true,
node: true,
es6: true,
},
extends: ['plugin:vue/recommended', 'eslint:recommended'],
rules: {
"vue/max-attributes-per-line": [2, {
"singleline": 10,
"multiline": {
"max": 1,
"allowFirstLine": false
}
}],
"vue/singleline-html-element-content-newline": "off",
"vue/multiline-html-element-content-newline":"off",
"vue/name-property-casing": ["error", "PascalCase"],
"vue/no-v-html": "off",
'accessor-pairs': 2,
'arrow-spacing': [2, {
'before': true,
'after': true
}],
'block-spacing': [2, 'always'],
'brace-style': [2, '1tbs', {
'allowSingleLine': true
}],
'camelcase': [0, {
'properties': 'always'
}],
'comma-dangle': [2, 'never'],
'comma-spacing': [2, {
'before': false,
'after': true
}],
'comma-style': [2, 'last'],
'constructor-super': 2,
'curly': [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
'eqeqeq': ["error", "always", {"null": "ignore"}],
'generator-star-spacing': [2, {
'before': true,
'after': true
}],
'handle-callback-err': [2, '^(err|error)$'],
'indent': [2, 2, {
'SwitchCase': 1
}],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [2, {
'beforeColon': false,
'afterColon': true
}],
'keyword-spacing': [2, {
'before': true,
'after': true
}],
'new-cap': [2, {
'newIsCap': true,
'capIsNew': false
}],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [2, {
'allowLoop': false,
'allowSwitch': false
}],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [2, {
'max': 1
}],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [2, {
'defaultAssignment': false
}],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [2, {
'vars': 'all',
'args': 'none'
}],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [2, {
'initialized': 'never'
}],
'operator-linebreak': [2, 'after', {
'overrides': {
'?': 'before',
':': 'before'
}
}],
'padded-blocks': [2, 'never'],
'quotes': [2, 'single', {
'avoidEscape': true,
'allowTemplateLiterals': true
}],
'semi': [2, 'never'],
'semi-spacing': [2, {
'before': false,
'after': true
}],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [2, {
'words': true,
'nonwords': false
}],
'spaced-comment': [2, 'always', {
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
'yoda': [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [2, 'always', {
objectsInObjects: false
}],
'array-bracket-spacing': [2, 'never']
}
}

23
.gitignore

@ -0,0 +1,23 @@
.DS_Store
node_modules
dist/
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
package-lock.json

32
README.md

@ -0,0 +1,32 @@
江夏区图书馆自助查询机
```bash
# install dependency
npm install
# develop
npm run serve
# 构建生产环境
npm run build
```
```bash
## 项目结构
├── src # 源代码
│ ├── api # 所有请求
│ ├── assets # 主题 字体等静态资源
│ ├── router # 路由
│ ├── views # views 所有页面
│ │ ├── module
│ │ ├── index # 首页
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等 注意:Vue.prototype.libcode使用
├── .env.xxx # 环境变量配置
├── .eslintrc.js # eslint 配置项
├── .babelrc # babel-loader 配置
├── .travis.yml # 自动化CI配置
├── vue.config.js # vue-cli 配置
├── postcss.config.js # postcss 配置
└── package.json # package.json
```

11
babel.config.js

@ -0,0 +1,11 @@
const plugins = ['@vue/babel-plugin-transform-vue-jsx', '@babel/plugin-proposal-optional-chaining', '@babel/plugin-proposal-nullish-coalescing-operator']
// 生产环境移除console
if (process.env.NODE_ENV === 'production') {
plugins.push('transform-remove-console')
}
module.exports = {
plugins: plugins,
presets: [
'@vue/cli-plugin-babel/preset'
]
}

57
package.json

@ -0,0 +1,57 @@
{
"name": "intelligence-inquiry-machine",
"version": "1.0.0",
"description": "江夏区图书馆自助查询机",
"scripts": {
"serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
"build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@babel/core": "^7.19.0",
"@babel/preset-env": "^7.19.0",
"@jiaminghi/data-view": "^2.7.3",
"@types/echarts": "^4.4.3",
"axios": "^0.27.2",
"core-js": "^3.6.4",
"echarts": "^4.6.0",
"element-ui": "^2.15.9",
"fabric": "2.5",
"swiper": "^8.4.2",
"vue": "^2.6.11",
"vue-awesome": "^4.0.2",
"vue-awesome-swiper": "^3.1.3",
"vue-router": "^3.1.5",
"vuex": "^3.1.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.2.0",
"@vue/cli-plugin-eslint": "^4.2.0",
"@vue/cli-service": "^4.2.0",
"babel-eslint": "^10.0.3",
"babel-plugin-transform-remove-console": "^6.9.4",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.1.2",
"sass": "^1.25.0",
"sass-loader": "^8.0.2",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}

BIN
public/favicon.ico

19
public/index.html

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="referrer" content="no-referrer">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<script type="text/javascript" src="/static/config.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

4
public/static/config.js

@ -0,0 +1,4 @@
window.g = {
AXIOS_TIMEOUT: 10000,
ApiUrl: 'http://192.168.3.220:12010' // 配置服务器地址
}

16
src/App.vue

@ -0,0 +1,16 @@
<template>
<div id="app">
<router-view />
</div>
</template>
<style lang="scss">
body {
margin: 0;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</style>

130
src/api/inquiryMachine.js

@ -0,0 +1,130 @@
import request from '@/utils/request'
import qs from 'qs'
// 根据题名著者出版社ISBN查询图书
export function FetchFindBooksByQuery(params) {
return request({
url: 'api/queryDeviceAPI/findBooksByQuery' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 获取热搜词
export function FetchFindHotSearch(params) {
return request({
url: 'api/queryDeviceAPI/findHotSearch' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 初始化盘点概况
export function FetchInitStockInfo(params) {
return request({
url: 'api/queryDeviceAPI/initStockInfo' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 根据机构代码获取楼层列表
export function FetchLibraryFloorListByFondsNo(params) {
return request({
url: 'api/queryDeviceAPI/getLibraryFloorListByFondsNo' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 根据楼层获取区域列表
export function FetchRegionListByFloorId(params) {
return request({
url: 'api/queryDeviceAPI/getRegionListByFloorId' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 根据区域获取书架列表
export function FetchShelfListByRegionId(params) {
return request({
url: 'api/queryDeviceAPI/getShelfListByRegionId' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 书架列表
export function FetchBookShelfDetails(params) {
return request({
url: 'api/queryDeviceAPI/getBookShelfDetails' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 根据书架查看所有层架位
export function FetchShelfGridAllByShelfIdText(params) {
return request({
url: 'api/queryDeviceAPI/getShelfGridAllByShelfIdText' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 根据书架查看所有层架位(参与盘点)
export function FetchShelfGridAllByShelfId(params) {
return request({
url: 'api/queryDeviceAPI/getShelfGridAllByShelfId' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 根据层架位id获取架位图书
export function FetchInitBookDetailsByGrids(params) {
return request({
url: 'api/queryDeviceAPI/initBookDetailsByGrids' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 图书详情
export function FetchInitBookDetailsSearchInto(params) {
return request({
url: 'api/queryDeviceAPI/initBookDetailsSearchInto' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 初始化热门图书
export function FetchInitHotBookList(params) {
return request({
url: 'api/queryDeviceAPI/initHotBookList' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 初始化热门架位
export function FetchInitHotShelfList(params) {
return request({
url: 'api/queryDeviceAPI/initHotShelfList' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 根据bookRecNo查看图书详细信息
export function FetchBookDetaisByBookRecNo(params) {
return request({
url: 'api/queryDeviceAPI/getBookDetaisByBookRecNo' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
export default {
FetchFindBooksByQuery,
FetchInitStockInfo,
FetchFindHotSearch,
FetchLibraryFloorListByFondsNo,
FetchRegionListByFloorId,
FetchShelfListByRegionId,
FetchBookShelfDetails,
FetchShelfGridAllByShelfIdText,
FetchShelfGridAllByShelfId,
FetchInitBookDetailsByGrids,
FetchInitHotBookList,
FetchInitHotShelfList,
FetchBookDetaisByBookRecNo
}

BIN
src/assets/fonts/HuXiaoBoNanShenTi.otf

BIN
src/assets/fonts/YouSheBiaoTiHei.ttf

BIN
src/assets/fonts/ZhenyanGB.ttf

19
src/assets/fonts/fonts.css

@ -0,0 +1,19 @@
@font-face {
font-family: "ZhenyanGB";
src: url('ZhenyanGB.ttf');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "YouSheBiaoTiHei";
src: url('YouSheBiaoTiHei.ttf');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "HuXiaoBoNanShenTi";
src: url('HuXiaoBoNanShenTi.otf');
font-weight: normal;
font-style: normal;
}

627
src/assets/iconfont/iconfont.css

@ -0,0 +1,627 @@
@font-face {
font-family: "iconfont"; /* Project id 3966148 */
src: url('iconfont.woff2?t=1736498949884') format('woff2'),
url('iconfont.woff?t=1736498949884') format('woff'),
url('iconfont.ttf?t=1736498949884') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-zhongduanjiankong:before {
content: "\e691";
}
.icon-lianjieduankai:before {
content: "\e600";
}
.icon-stop:before {
content: "\e714";
}
.icon-zhuangtai1:before {
content: "\e690";
}
.icon-zhuangtai2:before {
content: "\e6bc";
}
.icon-biaoqian:before {
content: "\e693";
}
.icon-_biaoqian-:before {
content: "\e68f";
}
.icon-shuju:before {
content: "\e68e";
}
.icon-duolouceng:before {
content: "\e87c";
}
.icon-shujia:before {
content: "\e68c";
}
.icon-hangzhengquyuguanli:before {
content: "\e68d";
}
.icon-quyu1:before {
content: "\e68b";
}
.icon-gongsi:before {
content: "\e689";
}
.icon-louceng:before {
content: "\e68a";
}
.icon-wodeshujia:before {
content: "\e688";
}
.icon-ceshi:before {
content: "\e687";
}
.icon-yuanchengkongzhi:before {
content: "\e7d4";
}
.icon-attachment:before {
content: "\e6a9";
}
.icon-zhuti:before {
content: "\e686";
}
.icon-gengduo:before {
content: "\e670";
}
.icon-huoqu:before {
content: "\e674";
}
.icon-dayin:before {
content: "\e67b";
}
.icon-guaqi:before {
content: "\e67c";
}
.icon-dangantongji:before {
content: "\e685";
}
.icon-sulan:before {
content: "\e63b";
}
.icon-jiesuan:before {
content: "\e649";
}
.icon-rengongqueren:before {
content: "\e659";
}
.icon-shengchengpandiandan:before {
content: "\e663";
}
.icon-yijiao:before {
content: "\e669";
}
.icon-shanchu1:before {
content: "\e66f";
}
.icon-jiarujieyueche:before {
content: "\e67d";
}
.icon-liuchengfaqi:before {
content: "\e67e";
}
.icon-cuowu1:before {
content: "\e67f";
}
.icon-danganliyong:before {
content: "\e680";
}
.icon-fou:before {
content: "\e681";
}
.icon-shi:before {
content: "\e682";
}
.icon-dengdai:before {
content: "\e683";
}
.icon-jieyueche-ding:before {
content: "\e684";
}
.icon-ruku:before {
content: "\e671";
}
.icon-yulan:before {
content: "\e672";
}
.icon-chuku:before {
content: "\e673";
}
.icon-mijijia:before {
content: "\e675";
}
.icon-shuaxin:before {
content: "\e676";
}
.icon-bangding:before {
content: "\e677";
}
.icon-quyu:before {
content: "\e678";
}
.icon-shebei:before {
content: "\e679";
}
.icon-danganbaoguan-fanbai:before {
content: "\e67a";
}
.icon-kufang:before {
content: "\e667";
}
.icon-chaihe:before {
content: "\e668";
}
.icon-zishebeiguanli:before {
content: "\e66a";
}
.icon-zidonggengxin:before {
content: "\e66b";
}
.icon-jiebang:before {
content: "\e66c";
}
.icon-you-fanbai:before {
content: "\e66d";
}
.icon-zuo-fanbai:before {
content: "\e66e";
}
.icon-duqu:before {
content: "\e664";
}
.icon-chexiao:before {
content: "\e665";
}
.icon-zhuanghe:before {
content: "\e666";
}
.icon-danganguanli-fanbai:before {
content: "\e662";
}
.icon-gengduo2:before {
content: "\e661";
}
.icon-gengduo1:before {
content: "\e660";
}
.icon-wancheng:before {
content: "\e65f";
}
.icon-zhengque:before {
content: "\e653";
}
.icon-cuowu:before {
content: "\e654";
}
.icon-huifu:before {
content: "\e655";
}
.icon-dian2:before {
content: "\e656";
}
.icon-dian:before {
content: "\e657";
}
.icon-shangchuanchenggong:before {
content: "\e658";
}
.icon-shangchuanshibai:before {
content: "\e65a";
}
.icon-xiaowenjian:before {
content: "\e65b";
}
.icon-bendiguajie:before {
content: "\e65c";
}
.icon-zhongxinjiance:before {
content: "\e65d";
}
.icon-tianjiawenjian:before {
content: "\e65e";
}
.icon-shangchuan2:before {
content: "\e650";
}
.icon-a-1:before {
content: "\e64f";
}
.icon-a-2:before {
content: "\e651";
}
.icon-xiazai:before {
content: "\e652";
}
.icon-zhengli:before {
content: "\e64e";
}
.icon-changgui:before {
content: "\e64d";
}
.icon-yuguidangku:before {
content: "\e64b";
}
.icon-shoujizhengbian:before {
content: "\e64c";
}
.icon-yidong:before {
content: "\e645";
}
.icon-piliangchengjian:before {
content: "\e646";
}
.icon-hebingchengjian:before {
content: "\e647";
}
.icon-danganfenlei:before {
content: "\e648";
}
.icon-fenlei:before {
content: "\e64a";
}
.icon-tupianyulan:before {
content: "\e644";
}
.icon-fabu:before {
content: "\e643";
}
.icon-jihuo:before {
content: "\e63d";
}
.icon-xiala-shouqi:before {
content: "\e63e";
}
.icon-xiala-chakanxinxi:before {
content: "\e63f";
}
.icon-chakan:before {
content: "\e640";
}
.icon-jiazaigengduo:before {
content: "\e641";
}
.icon-quanbuyidu:before {
content: "\e642";
}
.icon-shangchuan:before {
content: "\e62b";
}
.icon-yonghuyouxiang:before {
content: "\e635";
}
.icon-yonghuming:before {
content: "\e636";
}
.icon-suoshubumen:before {
content: "\e637";
}
.icon-suoshuquanzong:before {
content: "\e638";
}
.icon-shoujihaoma:before {
content: "\e639";
}
.icon-yonghujiaose:before {
content: "\e63a";
}
.icon-xingbie:before {
content: "\e63c";
}
.icon-dianzibiao:before {
content: "\e623";
}
.icon-yonghuguanli:before {
content: "\e624";
}
.icon-danganjieshou:before {
content: "\e625";
}
.icon-xitongshezhi2:before {
content: "\e626";
}
.icon-danganguanli:before {
content: "\e627";
}
.icon-yucundangguanli:before {
content: "\e628";
}
.icon-paixu-xia:before {
content: "\e629";
}
.icon-paixu-caozuo:before {
content: "\e62a";
}
.icon-paixu-shang:before {
content: "\e62c";
}
.icon-xinzeng:before {
content: "\e62d";
}
.icon-bianji:before {
content: "\e62e";
}
.icon-shezhi:before {
content: "\e62f";
}
.icon-kuaisushezhi:before {
content: "\e630";
}
.icon-shanchu:before {
content: "\e631";
}
.icon-paixu:before {
content: "\e632";
}
.icon-daochu:before {
content: "\e633";
}
.icon-zhongzhi2:before {
content: "\e634";
}
.icon-rili:before {
content: "\e604";
}
.icon-shouye:before {
content: "\e605";
}
.icon-xitongshezhi:before {
content: "\e606";
}
.icon-shouqi:before {
content: "\e607";
}
.icon-xiala-fanbai:before {
content: "\e608";
}
.icon-xiala:before {
content: "\e609";
}
.icon-zhuangtai:before {
content: "\e60a";
}
.icon-xiaoxi:before {
content: "\e60b";
}
.icon-zhongzhi:before {
content: "\e60c";
}
.icon-xiala-xia:before {
content: "\e60d";
}
.icon-xiala-you:before {
content: "\e60e";
}
.icon-xiala-shang:before {
content: "\e60f";
}
.icon-kuangxuan-jinyong2:before {
content: "\e610";
}
.icon-kuangxuan-xuanze:before {
content: "\e611";
}
.icon-kuangxuan-moren:before {
content: "\e612";
}
.icon-kuangxuan-jinyong1:before {
content: "\e613";
}
.icon-weizhi-copy:before {
content: "\e7d5";
}
.icon-xuanzhong:before {
content: "\e614";
}
.icon-weixuan:before {
content: "\e615";
}
.icon-sousuo:before {
content: "\e616";
}
.icon-guan:before {
content: "\e617";
}
.icon-kai:before {
content: "\e618";
}
.icon-weizhi:before {
content: "\e619";
}
.icon-biaoji:before {
content: "\e61a";
}
.icon-guanbixiao:before {
content: "\e61b";
}
.icon-guanbi:before {
content: "\e61c";
}
.icon-zhuyi-hong:before {
content: "\e61d";
}
.icon-zhuyi-lan:before {
content: "\e61e";
}
.icon-zhuyi-hong-xiao:before {
content: "\e61f";
}
.icon-wenjian-xiao:before {
content: "\e620";
}
.icon-wenjian-da:before {
content: "\e621";
}
.icon-dangan:before {
content: "\e622";
}
.icon-yanzhengma:before {
content: "\e602";
}
.icon-mima:before {
content: "\e603";
}
.icon-zhanghao:before {
content: "\e601";
}

1
src/assets/iconfont/iconfont.js
File diff suppressed because it is too large
View File

1080
src/assets/iconfont/iconfont.json
File diff suppressed because it is too large
View File

BIN
src/assets/iconfont/iconfont.ttf

BIN
src/assets/iconfont/iconfont.woff

BIN
src/assets/iconfont/iconfont.woff2

BIN
src/assets/images/bg.png

After

Width: 1080  |  Height: 1920  |  Size: 62 KiB

BIN
src/assets/images/border.png

After

Width: 65  |  Height: 103  |  Size: 180 B

BIN
src/assets/images/default-img-bg.jpg

After

Width: 591  |  Height: 317  |  Size: 6.2 KiB

BIN
src/assets/images/default-img.png

After

Width: 440  |  Height: 526  |  Size: 35 KiB

BIN
src/assets/images/default-img22.png

After

Width: 440  |  Height: 478  |  Size: 37 KiB

BIN
src/assets/images/img10.png

After

Width: 39  |  Height: 38  |  Size: 1.9 KiB

BIN
src/assets/images/img3.png

After

Width: 48  |  Height: 13  |  Size: 548 B

BIN
src/assets/images/img5.png

After

Width: 24  |  Height: 24  |  Size: 619 B

BIN
src/assets/images/img6.png

After

Width: 24  |  Height: 24  |  Size: 557 B

BIN
src/assets/images/img7.png

After

Width: 46  |  Height: 50  |  Size: 3.4 KiB

BIN
src/assets/images/img8.png

After

Width: 46  |  Height: 50  |  Size: 3.6 KiB

BIN
src/assets/images/img9.png

After

Width: 46  |  Height: 50  |  Size: 3.6 KiB

BIN
src/assets/images/index/320 180 down.png

After

Width: 324  |  Height: 184  |  Size: 24 KiB

BIN
src/assets/images/index/320 180 up.png

After

Width: 324  |  Height: 184  |  Size: 26 KiB

BIN
src/assets/images/index/400 620 left.png

After

Width: 404  |  Height: 624  |  Size: 60 KiB

BIN
src/assets/images/index/400 620 right.png

After

Width: 404  |  Height: 624  |  Size: 33 KiB

BIN
src/assets/images/index/760 180 down.png

After

Width: 764  |  Height: 184  |  Size: 31 KiB

BIN
src/assets/images/index/760 180 up.png

After

Width: 764  |  Height: 184  |  Size: 28 KiB

BIN
src/assets/images/index/activities.png

After

Width: 215  |  Height: 201  |  Size: 8.2 KiB

BIN
src/assets/images/index/bg.png

After

Width: 1920  |  Height: 1080  |  Size: 680 KiB

BIN
src/assets/images/index/brief.png

After

Width: 234  |  Height: 201  |  Size: 8.1 KiB

BIN
src/assets/images/index/digital.png

After

Width: 245  |  Height: 200  |  Size: 11 KiB

BIN
src/assets/images/index/guide.png

After

Width: 153  |  Height: 200  |  Size: 8.6 KiB

BIN
src/assets/images/index/logo.png

After

Width: 675  |  Height: 90  |  Size: 12 KiB

BIN
src/assets/images/index/newbook.png

After

Width: 225  |  Height: 200  |  Size: 8.7 KiB

BIN
src/assets/images/index/search.png

After

Width: 240  |  Height: 201  |  Size: 9.9 KiB

BIN
src/assets/images/index/title.png

After

Width: 445  |  Height: 111  |  Size: 11 KiB

BIN
src/assets/images/list-title.png

After

Width: 494  |  Height: 76  |  Size: 15 KiB

BIN
src/assets/images/local-bg.png

After

Width: 83  |  Height: 346  |  Size: 6.5 KiB

BIN
src/assets/images/shad2.png

After

Width: 75  |  Height: 444  |  Size: 1.2 KiB

BIN
src/assets/images/shelf01.png

After

Width: 32  |  Height: 441  |  Size: 1.4 KiB

BIN
src/assets/images/shelf02.png

After

Width: 368  |  Height: 441  |  Size: 62 KiB

BIN
src/assets/images/shelf03.png

After

Width: 97  |  Height: 267  |  Size: 7.1 KiB

BIN
src/assets/images/shelf04.png

After

Width: 256  |  Height: 256  |  Size: 3.0 KiB

BIN
src/assets/images/shelf05.png

After

Width: 97  |  Height: 267  |  Size: 7.2 KiB

BIN
src/assets/images/shelf06.png

After

Width: 97  |  Height: 267  |  Size: 3.2 KiB

BIN
src/assets/images/top-bg.png

After

Width: 1080  |  Height: 398  |  Size: 40 KiB

BIN
src/assets/images/top.png

After

Width: 1920  |  Height: 200  |  Size: 263 KiB

BIN
src/assets/logo.png

After

Width: 200  |  Height: 200  |  Size: 6.7 KiB

829
src/assets/styles/index.scss

@ -0,0 +1,829 @@
.content-main{
width: calc(100%);
height: calc(100vh);
background: url("~@/assets/images/bg.png") left top;
background-size: 100% 100%;
}
.search-main{
background-color: #fff;
border-radius: 8px;
.search-content{
display: flex;
justify-content: flex-start;
align-items: center;
.el-button{
height: 56px;
font-size: 24px;
border: 1px solid #C6C6E2;
}
.input-search{
flex: 1;
display: flex;
margin: 0 8px;
border: 1px solid #C6C6E2;
border-radius: 8px;
overflow: hidden;
::v-deep .el-input{
flex: 1;
.el-input__inner{
font-size: 20px;
height: 56px;
border: none;
}
}
.el-button{
display: block;
padding-left: 48px;
background: url('~@/assets/images/img5.png') no-repeat 12px center #0348F3;
background-size: 24px 24px;
border-radius: 0 8px 8px 0;
border: none;
}
}
}
.hot-keyword{
font-size: 24px;
p{
padding: 16px 0 10px 0;
}
.keyword-item{
display: flex;
justify-content: flex-start;
align-items: center;
span{
display: block;
padding: 0 12px;
height: 44px;
line-height: 44px;
font-size: 20px;
background-color: #F9F9FD;
color: #737475;
border-radius: 8px;
margin-right: 12px;
&.active{
background-color: #5A86F4;
color: #fff;
}
}
}
}
}
.box-style{
// margin-top: 20px;
border-radius: 4px;
border: 1px solid #EBEEF5;
background-color: #FFF;
overflow: hidden;
color: #303133;
-webkit-transition: .3s;
transition: .3s;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
padding: 20px;
}
// 图书列表
.book-content{
margin-top: 16px;
padding: 20px 24px;
background-color: #fff;
border-radius: 8px;
}
.result{
font-size: 16px;
color: #737475;
margin-bottom: 10px;
span{
color: #5A86F4;
}
}
.book-all-list{
display: flex;
flex-wrap: wrap;
justify-content: space-between;
height: calc(100% - 28px);
overflow: hidden;
overflow-y: scroll;
}
.book-item{
width: calc(100% / 2 - 20px);
margin: 20px 10px 20px 0;
}
.load-data{
width: 100%;
padding: 15px 0;
text-align: center;
}
.book-info{
display: flex;
justify-content: space-between;
.book-img{
display: flex;
align-items: center;
width: 160px;
height: 200px;
margin-right: 17px;
// background-color: #f1f1f1;
overflow: hidden;
img{
display: block;
width: 100%;
}
}
.book-txt{
flex: 1;
h4{
font-size: 28px;
line-height: 40px;
}
div{
font-size: 20px;
height: 32px;
line-height: 32px;
margin-top: 8px;
}
.book-autor{
display: inline-block;
padding: 0 8px;
// border: 1px solid #C6C6E2;
color: #737475;
border-radius: 4px;
}
}
.to-book-more{
font-size: 20px;
width: 100px;
height: 40px;
line-height: 40px;
padding: 0 8px;
color: #fff;
background-color: #5A86F4;
border-radius: 4px;
}
}
.book-intro{
font-size: 20px;
height: 32px;
line-height: 32px;
height: 96px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow-y: scroll;
margin-top: 10px;
}
.bookList-wrap{
display: flex;
justify-content: space-between;
margin-top: 16px;
height: calc(100vh - 418px);
}
.book-content2{
flex: 1;
margin-left: 20px;
padding: 20px 24px;
background-color: #fff;
border-radius: 8px;
.book-all-list{
display: block;
}
.book-item{
width: 100%;
margin: 0 10px 20px 0;
}
.book-txt{
position: relative;
}
}
.bookList-left{
width: 310px;
height: 100%;
padding: 24px 16px 0 16px;
background-color: #fff;
border-radius: 8px;
overflow: hidden;
overflow-y: scroll;
}
.filter-header{
position: relative;
padding-left: 20px;
font-size: 28px;
font-weight: 600;
line-height: 30px;
&::before{
position: absolute;
left: 0;
top: 50%;
content: "";
width: 8px;
height: 26px;
background-color: #5A86F4;
transform: translateY(-50%);
}
}
.ranking{
.el-carousel{
margin-top: 14px;
}
.ranking-img{
display: flex;
align-items: center;
width: 140px;
height: 200px;
background-color: #f1f1f1;
overflow: hidden;
img{
display: block;
width: 100%;
}
}
.ranking-book-detail{
.book-info{
position: relative;
padding: 10px 10px 18px 16px;
background-color: #F9F9FD;
border: 1px solid #C6C6E2;
border-radius: 8px;
h5{
font-size: 24px;
}
.book-author{
padding: 10px 0;
font-size: 18px;
color: #FE6902;
span:first-child{
padding-right: 10px;
border-right: 1px solid #E9E9F4;
}
span:last-child{
padding-left: 10px;
}
}
.book-intro{
opacity: .6;
}
.ranking-status{
position: absolute;
right: 10px;
top: -22px;
width: 46px;
height: 50px;
&.num-one{
background: url('~@/assets/images/img7.png') no-repeat;
background-size: 46px 50px;
}
&.num-two{
background: url('~@/assets/images/img8.png') no-repeat;
background-size: 46px 50px;
}
&.num-three{
background: url('~@/assets/images/img9.png') no-repeat;
background-size: 46px 50px;
}
}
}
.book-click-num{
display: flex;
justify-content: space-between;
padding: 6px 6px 6px 16px;
margin-top: 8px;
background-color: #F9F9FD;
border: 1px solid #C6C6E2;
border-radius: 8px;
span{
display: block;
font-size: 24px;
height: 42px;
line-height: 42px;
&.click-num{
padding-left: 28px;
font-size: 24px;
background: url('~@/assets/images/img6.png') no-repeat left center;
background-size: 24px 24px;
}
&.detail-btn{
font-size: 20px;
padding: 0 12px;
background-color: #5A86F4;
color: #fff;
border-radius: 8px;
}
}
}
}
}
.ranking-list-bottom{
margin-top: 10px;
li{
display: flex;
justify-content: space-between;
align-items: center;
padding: 24px 0 16px 0;
border-bottom: 1px solid #C6C6E2;
font-size: 22px;
span{
display: block;
margin-right: 30px;
background: url('~@/assets/images/img3.png') no-repeat center bottom;
background-size: 48px 13px;
}
}
}
.book-place{
padding: 0 27px 0 32px;
background-color: #F8F8FD;
h4{
position: relative;
padding-left: 18px;
font-size: 28px;
line-height: 40px;
color: #191A1A;
&::before{
position: absolute;
left: 0;
top: 50%;
content: "";
width: 7px;
height: 26px;
background-color: #191A1A;
transform: translateY(-50%);
}
}
.book-place-list{
li{
display: flex;
justify-content: space-between;
padding: 14px 0;
font-size: 25px;
line-height: 40px;
border-bottom: 1px solid #C6C6E2;
span{
position: relative;
width: 57px;
color: #000;
&::before{
position: absolute;
bottom: 6px;
left: 0;
content: "";
width: 16px;
height: 7px;
background-color: #5A86F4;
opacity: .4;
}
}
.book-num{
width: 230px;
}
.place-detail{
flex: 1;
text-align: right;
padding-right: 56px;
color: #000;
background: url('~@/assets/images/img10.png') no-repeat right center;
background-size: 39px 38px;
}
}
}
}
.book-bottom{
position: relative;
display: flex;
justify-content: center;
padding: 24px 0;
z-index: 999999;
span{
display: block;
width: 212px;
height: 57px;
line-height: 53px;
font-size: 25px;
text-align: center;
background: rgba(90,134,244,0.1);
border-radius: 29px;
border: 2px solid #5A86F4;
color: #5A86F4;
}
}
.positionDialog{
.book-detail{
position: relative;
display: flex;
justify-content: flex-start;
padding: 10px 30px !important;
background-color: #F6F8FC;
li{
font-size: 18px;
line-height: 40px;
margin-right: 20px;
font-weight: bold;
span{
display: inline-block;
text-align: right;
margin-right: 20px;
color: #0C0E1E;
font-weight: normal;
}
i{
font-style: normal;
padding: 0 8px;
}
}
}
.position-content{
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 20px !important;
h5{
font-size: 18px;
color: #0C0E1E;
}
.position-left{
width: 500px;
overflow: hidden;
::v-deep .venue-preview{
height: 500px;
}
}
.position-right{
width: 400px;
margin-left: 20px;
}
}
.shelf-top{
display: flex;
justify-content: space-around;
align-items: center;
text-align: center;
margin-top: 30px;
p{
font-size: 16px;
color: #fff;
height: 30px;
line-height: 26px;
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;
.data-shelf-cell{
position: relative;
font-size: 16px;
color: #0C0E1E;
background: url('~@/assets/images/shelf02.png') repeat-x left top;
background-size: 20% 100%;
border-radius: 3px;
cursor: pointer;
&::before{
content: "";
position: absolute;
left: 0;
top: 0;
width: 6px;
height: 38px;
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: 38px;
background: url('~@/assets/images/shelf01.png') no-repeat left top;
background-size: 100% 100%;
}
.cell-name{
display: block;
width: 100%;
line-height: 38px;
}
&.active{
color: #fff;
span{
position: relative;
display: block;
&::before{
position: absolute;
top: 0;
left: 6px;
content: '';
width: 98%;
height: 100%;
background-color: rgba(255,0,0,.3);
}
}
}
}
}
}
.tooltip-style,
.popover-external-set {
width: 250px;
background:rgba(0,0,0,1);
color: #fff;
border-radius: 6px;
}
.dataScreening-header{
display: flex;
justify-content: space-between;
align-items: center;
color: #0C0E1E;
background-color: #fff;
margin-bottom: 20px;
h4{
flex: 1;
}
.bookshelf-area{
text-align: right;
position: relative;
display: flex;
justify-content: flex-start;
align-items: center;
padding: 4px 10px;
a{
font-weight: bold;
font-size: 16px;
display: block;
padding: 10px;
i{
margin-right: 10px;
}
}
span{
font-size: 14px;
display: block;
padding: 0 6px;
}
.double-click-btn{
position: inherit;
margin-left: 20px;
font-weight: normal;
border: 1px solid #545b65;
border-radius: 13px;
line-height: 18px;
background-color: #e2e9f3;
}
}
}
.shelf-top{
display: flex;
justify-content: space-around;
align-items: center;
text-align: center;
margin-top: 30px;
p{
font-size: 12px;
color: #fff;
height: 30px;
line-height: 26px;
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;
.data-shelf-cell{
position: relative;
font-size: 12px;
color: #0C0E1E;
background: url('~@/assets/images/shelf02.png') repeat-x left top;
background-size: 20% 100%;
border-radius: 3px;
cursor: pointer;
&::before{
content: "";
position: absolute;
left: 0;
top: 0;
width: 6px;
height: 38px;
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: 38px;
background: url('~@/assets/images/shelf01.png') no-repeat left top;
background-size: 100% 100%;
}
.cell-name{
display: block;
width: 100%;
line-height: 38px;
}
&.active{
color: #fff;
span{
position: relative;
display: block;
&::before{
position: absolute;
top: 0;
left: 6px;
content: '';
width: 98%;
height: 100%;
background-color: rgba(255,0,0,.3);
}
}
}
}
}
.tag-info{
display: flex;
justify-content: flex-start;
align-items: center;
position: absolute;
right: 20px;
top: 8px;
p{
font-size: 16px;
font-weight: bold;
}
.iconfont{
font-size: 16px;
margin-right: 5px;
}
}
.tag-sort{
i{
color: #0348F3;
}
}
.tag-place{
i{
color: #ED4A41;
}
}
.tag-in{
i{
color: #2ECAAC;
}
}
.tag-all{
i{
color: #0C0E1E;
}
}
.tab-content{
position: relative;
background-color: #fff;
.tab-nav{
display: flex;
justify-content: flex-start;
font-size: 22px;
margin: 0 0 18px 0;
color: #545B65;
border-bottom: 1px solid #EDEFF3;
li{
margin-right: 60px;
&.active-tab-nav{
padding-bottom: 10px;
color: #0348F3;
border-bottom: 3px solid #0348F3;
}
}
}
}
//本架图书页面
.rack-box{
padding: 30px 0 0 0;
width: 100%;
.rack-item{
position: relative;
margin-bottom: 30px;
overflow: hidden;
.rack-box-list{
height: 200px;
width: 100%;
overflow: hidden;
white-space: nowrap;
background: url('~@/assets/images/shelf02.png') repeat left top;
background-size: 10% 100%;
.list-item{
display: inline-block;
border: none;
width: 42px;
height: 120px;
background: url('~@/assets/images/shelf03.png') no-repeat left top;
background-size: 100% 100%;
position: relative;
.book-name{
position: absolute;
left: 15px;
top: 16px;
display: block;
height: 90px;
font-size: 12px;
writing-mode:vertical-rl;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
.rack-floor{
position: absolute;
left: 0;
bottom: 2px;
height: 50px;
width: 100%;
display: flex;
color: #000;
align-items: center;
justify-content: space-between;
padding: 0 40px;
z-index: 999;
.icon-l,.icon-r{
font-size: 24px;
}
}
}
.change-layer{
display: flex;
justify-content: flex-start;
padding: 20px 0;
li{
padding: 0 20px;
height: 42px;
line-height: 40px;
margin-right: 10px;
font-size: 20px;
border-radius: 7px;
border: 2px solid #C6C6E2;
&.active{
color: #fff;
background-color: #0348F3;
border-color: #0348F3;
}
}
}
::v-deep .el-carousel{
margin: 20px auto;
}
.index-ranking{
display: flex;
justify-content: space-between;
align-items: flex-start;
margin: 0 44px;
.box-style{
width: 50%;
&:first-child{
margin-right: 20px;
}
}
::v-deep .el-carousel{
width: 362px;
height: 200px;
}
}

180
src/assets/styles/style.scss

@ -0,0 +1,180 @@
@import '~@/assets/iconfont/iconfont.css';
// 全局样式
* {
margin: 0;
padding: 0;
list-style-type: none;
box-sizing: border-box;
outline: none;
}
html {
margin: 0;
padding: 0;
}
body {
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
background-color: #f1f1f1;
margin: 0;
padding: 0;
}
a {
color: #343440;
text-decoration: none;
}
ul{
margin: 0;
padding: 0;
li{
list-style: none;
}
}
.clearfix {
&::after {
content: "";
display: table;
height: 0;
line-height: 0;
visibility: hidden;
clear: both;
}
}
/**滚动条的宽度*/
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
//滚动条的滑块
::-webkit-scrollbar-thumb {
background-color: #44A9E4;
border-radius: 5px;
}
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
//浮动
.float-r {
float: right;
}
//文章一行显示多余省略号显示
.title-item {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.tooltip-style,
.popover-external-set {
width: 250px;
background:rgba(0,0,0,1);
color: #fff;
border-radius: 6px;
}
.tooltip-style{
display:none;
position:absolute;
}
.popover-external-set {
position: fixed;
z-index: 1000;
}
.tooltip-style,
.popover-content-set{
.tooltip-top{
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
height: 40px;
line-height: 40px;
padding: 0 10px;
border-bottom: 1px solid #fff;
}
.tooltip-top span,
.tooltip-top i{
font-size: 12px;
font-style: normal;
}
ul{
padding: 10px;
}
ul li{
display: flex;
justify-content: flex-start;
align-items: center;
line-height: 24px;
font-style: normal;
font-size: 12px;
}
ul li p{
width: 60px;
font-weight: bold;
text-align: right;
}
ul li span,
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 span.percentage,
ul li em.percentage{
width: auto;
}
.tag-item{
display: block;
// width: 50px;
height: 18px;
line-height: 16px;
padding: 0 4px;
border-radius: 3px;
font-size: 12px;
color: #A6ADB6;
border: 1px solid #E6E8ED;
background-color: #F3F5F9;
text-align: center;
&.tag-sort{
color: #FD7359 !important;
border-color: #FBC0B5 !important;
background-color: #FCECE9 !important;
}
&.tag-place{
color: #018BFF !important;
border-color: #9BD1FF !important;
background-color: #DCEDFD !important;
}
&.tag-all{
color: #0C0E1E;
border-color: #545B65 !important;
background-color: #E6E8ED !important;
}
}
}

147
src/common/flexible.js

@ -0,0 +1,147 @@
(function(win, lib) {
var doc = win.document
var docEl = doc.documentElement
var metaEl = doc.querySelector('meta[name="viewport"]')
var flexibleEl = doc.querySelector('meta[name="flexible"]')
var dpr = 0
var scale = 0
var tid
var flexible = lib.flexible || (lib.flexible = {})
if (metaEl) {
console.warn('将根据已有的meta标签来设置缩放比例')
var match = metaEl
.getAttribute('content')
// eslint-disable-next-line no-useless-escape
.match(/initial\-scale=([\d\.]+)/)
if (match) {
scale = parseFloat(match[1])
dpr = parseInt(1 / scale)
}
} else if (flexibleEl) {
var content = flexibleEl.getAttribute('content')
if (content) {
// eslint-disable-next-line no-useless-escape
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/)
// eslint-disable-next-line no-useless-escape
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/)
if (initialDpr) {
dpr = parseFloat(initialDpr[1])
scale = parseFloat((1 / dpr).toFixed(2))
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1])
scale = parseFloat((1 / dpr).toFixed(2))
}
}
}
if (!dpr && !scale) {
// eslint-disable-next-line no-unused-vars
var isAndroid = win.navigator.appVersion.match(/android/gi)
var isIPhone = win.navigator.appVersion.match(/iphone/gi)
var devicePixelRatio = win.devicePixelRatio
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
dpr = 2
} else {
dpr = 1
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1
}
scale = 1 / dpr
}
docEl.setAttribute('data-dpr', dpr)
if (!metaEl) {
metaEl = doc.createElement('meta')
metaEl.setAttribute('name', 'viewport')
metaEl.setAttribute(
'content',
'initial-scale=' +
scale +
', maximum-scale=' +
scale +
', minimum-scale=' +
scale +
', user-scalable=no'
)
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl)
} else {
var wrap = doc.createElement('div')
wrap.appendChild(metaEl)
doc.write(wrap.innerHTML)
}
}
function refreshRem() {
var width = docEl.getBoundingClientRect().width
// 最小1366px,最大适配2560px
if (width / dpr < 1366) {
width = 1366 * dpr
} else if (width / dpr > 2560) {
width = 2560 * dpr
}
// 设置成24等份,设计稿时1920px的,这样1rem就是80px
var rem = width / 24
docEl.style.fontSize = rem + 'px'
flexible.rem = win.rem = rem
}
win.addEventListener(
'resize',
function() {
clearTimeout(tid)
tid = setTimeout(refreshRem, 300)
},
false
)
win.addEventListener(
'pageshow',
function(e) {
if (e.persisted) {
clearTimeout(tid)
tid = setTimeout(refreshRem, 300)
}
},
false
)
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px'
} else {
doc.addEventListener(
'DOMContentLoaded',
// eslint-disable-next-line no-unused-vars
function(e) {
doc.body.style.fontSize = 12 * dpr + 'px'
},
false
)
}
refreshRem()
flexible.dpr = win.dpr = dpr
flexible.refreshRem = refreshRem
flexible.rem2px = function(d) {
var val = parseFloat(d) * this.rem
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px'
}
return val
}
flexible.px2rem = function(d) {
var val = parseFloat(d) / this.rem
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem'
}
return val
}
})(window, window['lib'] || (window['lib'] = {}))

61
src/common/scrollMixins.js

@ -0,0 +1,61 @@
export default {
data() {
return {
scrollDom: null,
interval: null,
scrollTimer: null, // 滚动定时器
pauseTimer: null, // 暂停定时器
timeout: null,
step: null
}
},
created() {
},
mounted() {
// this.dataCompleteFun()
},
destroyed() {
// 清理定时器
clearTimeout(this.pauseTimer)
this.pauseTimer = null
clearInterval(this.scrollTimer)
this.scrollTimer = null
// 清理点击监听
window.document.removeEventListener('click', this.pauseScroll)
},
methods: {
autoScroll() {
// 滚动长度为0
if (this.scrollDom.scrollHeight - this.scrollDom.clientHeight > 0) {
// 如果定时器存在
if (this.scrollTimer) {
// 则先清除
clearInterval(this.scrollTimer)
clearTimeout(this.pauseTimer)
this.scrollTimer = null
this.pauseTimer = null
}
this.scrollTimer = setInterval(() => {
const scrollHeight = this.scrollDom.scrollHeight
const clientHeight = this.scrollDom.clientHeight
const scroll = scrollHeight - clientHeight
// 获取当前滚动条距离顶部高度
const scrollTop = this.scrollDom.scrollTop
// 当滚动到底部时,间隔时间后重回顶部开始
if (scrollTop + this.step >= scroll) {
this.scrollDom.scrollTop = scroll
this.pauseTimer = setTimeout(() => {
this.scrollDom.scrollTop = 0
this.autoScroll()
}, this.timeout)
} else { // 没有则继续滚动
this.scrollDom.scrollTop = scrollTop + this.step
}
// console.log(scrollHeight, clientHeight, scroll, scrollTop)
}, this.interval)
} else {
return
}
}
}
}

39
src/main.js

@ -0,0 +1,39 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 适配flex
import '@/common/flexible.js'
// 引入全局css
import './assets/styles/style.scss'
import './assets/iconfont/iconfont.js'
import './assets/fonts/fonts.css'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
import axios from 'axios'
Vue.prototype.$axios = axios
// 馆代码 1501 江夏图书馆 1201 东西湖图书馆 FTZN 本地
// Vue.prototype.libcode = 'FTZN'
Vue.prototype.libcode = '1501'
import { parseTime } from '@/utils/index.js'
Vue.filter('parseTime', function(time, cFormat) {
return parseTime(time, cFormat)
})
// 引入echart
import echarts from 'echarts'
Vue.prototype.$echarts = echarts
Vue.config.productionTip = false
new Vue({
router,
store,
render: (h) => h(App)
}).$mount('#app')

54
src/router/index.js

@ -0,0 +1,54 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'index',
component: () => import('../views/index.vue'),
meta: {
title: '首页'
}
},
{
path: '/regions',
name: 'regions',
component: () => import('../views/regionsList.vue'),
meta: {
title: '区域'
}
},
{
path: '/shelf',
name: 'shelf',
component: () => import('../views/shelfList.vue'),
meta: {
title: '书架'
}
},
{
path: '/gird',
name: 'gird',
component: () => import('../views/girdList.vue'),
meta: {
title: '架位'
}
},
{
path: '/bookList',
name: 'bookList',
component: () => import('../views/bookList.vue'),
meta: {
title: '图书列表'
}
}
]
const router = new VueRouter({
mode: 'hash',
routes
})
export default router

15
src/store/index.js

@ -0,0 +1,15 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
}
})

92
src/utils/index.js

@ -0,0 +1,92 @@
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
// 获取当前日期时间
export function getCurrentTime() {
const yy = new Date().getFullYear()
const mm = new Date().getMonth() + 1
const dd = new Date().getDate()
const hh = new Date().getHours()
const mf = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes()
const ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date().getSeconds()
const time = yy + '年' + mm + '月' + dd + '日 ' + hh + ':' + mf + ':' + ss
return time
}
/**
* Parse the time to string
* @param {(Object|string|number)} time
* @param {string} cFormat
* @returns {string}
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'undefined' || time === null || time === 'null') {
return ''
} else if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}

55
src/utils/request.js

@ -0,0 +1,55 @@
import axios from 'axios'
import { Message } from 'element-ui'
// 创建axios实例
const service = axios.create({
// baseURL: process.env.NODE_ENV === 'production' ? process.env.VUE_APP_BASE_API : '/', // api 的 base_url
baseURL: process.env.NODE_ENV === 'production' ? window.g.ApiUrl : '/', // api 的 base_url
timeout: 1000 * 30, // 请求超时时间
headers: {
'Content-Type': 'application/json'
}
})
// request拦截器
service.interceptors.request.use(
config => {
// if (getToken()) {
// config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
// }
const token = '' // 登录后生成用于识别用户身份,项目不需要直接去掉
config.headers['Authorization'] = token || ''
return config
},
error => {
console.error('error: ', error)
Promise.reject(error)
}
)
// response 拦截器
service.interceptors.response.use(
response => {
const errorMsg = response.data.errMsg
if (response.status === 200) {
// console.log(response.data.data)
if (response.data instanceof Blob) {
return response.data
} else if (response.data.data) {
return response.data.data
} else {
return response.data
}
} else {
Message.error({
message: errorMsg,
duration: 5000
})
Promise.reject()
}
},
error => {
return Promise.reject(error)
}
)
export default service

26
src/utils/resizeMixins.js

@ -0,0 +1,26 @@
// 混入代码 resize-mixins.js
import { debounce } from '@/utils/index'
const resizeChartMethod = '$__resizeChartMethod'
export default {
data() {
// 在组件内部将图表init的引用映射到chart属性上
return {
chart: null
}
},
created() {
window.addEventListener('resize', this[resizeChartMethod], false)
},
beforeDestroy() {
window.removeEventListener('reisze', this[resizeChartMethod])
},
methods: {
// 通过lodash的防抖函数来控制resize的频率
[resizeChartMethod]: debounce(function() {
if (this.chart) {
this.chart.resize()
}
}, 100)
}
}

181
src/views/bookList.vue

@ -0,0 +1,181 @@
<template>
<div class="content-main">
<Search ref="searchRefs" />
<div class="bookList-wrap" style="margin: 30px 44px;">
<div class="bookList-left">
<Ranking />
</div>
<!-- 图书列表 -->
<div class="book-content2">
<div class="result">检索结果<span>{{ totalNum }}</span></div>
<div class="book-all-list" @scroll="listenScroll">
<div v-for="(item,index) in bookList" :key="index" class="book-item" @click="getBookDetaisByBookRecNo(item)">
<div class="book-info">
<div class="book-img">
<img :src="coverUrl+'/demoRecommend/getBookCover.do?id='+item.id" :onerror="defaultImg" alt="">
</div>
<div class="book-txt">
<h4> {{ item.title }} </h4>
<div class="book-autor"> {{ item.author }} </div>
<div class="book-publish"> {{ item.publisher }} </div>
<div class="book-isbn">ISBN{{ item.isbn }}</div>
<!-- <div class="book-intro title-item"> {{ item.introduce }} </div> -->
<div class="to-book-more">查看详情</div>
</div>
</div>
</div>
<div v-if="!hasNextPage" class="load-data">暂无更多数据</div>
<div v-else class="load-data">加载中...</div>
</div>
</div>
</div>
<BookDetails ref="bookDetailsRef" />
</div>
</template>
<script>
import { FetchFindBooksByQuery, FetchBookDetaisByBookRecNo } from '@/api/inquiryMachine'
import { positionCrud } from './mixins/index.js'
import Search from './module/search'
import Ranking from './module/ranking'
import BookDetails from './module/bookDetails'
export default {
name: 'BookList',
components: {
Search,
Ranking,
BookDetails
},
mixins: [positionCrud],
data() {
return {
defaultImg: 'this.src="' + require('@/assets/images/default-img.png') + '"',
bookList: [],
coverUrl: null,
pageIndex: 1,
pageSize: 10,
hasNextPage: false,
totalPages: 0,
totalNum: 0
}
},
computed: {
},
created() {
},
mounted() {
this.getBookRecommendList()
},
methods: {
getLocation(row) {
const parts = []
if (row.floorName) {
parts.push(row.floorName)
}
if (row.regionName) {
parts.push(row.regionName)
}
if (row.gridName) {
parts.push(row.gridName)
}
return parts.length > 0 ? parts.join('-') : '-'
},
getBookRecommendList() {
let params
if (localStorage.getItem('searchKey')) {
this.$refs.searchRefs.keyword = localStorage.getItem('searchKey')
this.$refs.searchRefs.keyWordIndex = parseInt(localStorage.getItem('keyWordIndex'))
params = {
'fondsCode': this.libcode,
'search': localStorage.getItem('searchKey'),
'page': this.pageIndex - 1,
'size': this.pageSize
}
} else {
params = {
'fondsCode': this.libcode,
'search': this.$refs.searchRefs.keyword,
'page': this.pageIndex - 1,
'size': this.pageSize
}
}
FetchFindBooksByQuery(params).then(res => {
const resData = res.content
if (resData.length === 0) {
this.hasNextPage = false
} else {
this.hasNextPage = resData.length === this.pageSize
}
this.totalNum = res.totalElements
this.totalPages = res.totalPages
this.bookList = this.bookList.concat(resData)
// if (this.pageIndex > res.totalPages) {
// this.hasNextPage = false
// }
localStorage.removeItem('searchKey')
localStorage.removeItem('keyWordIndex')
}).catch(() => {
this.$message.error('接口错误')
})
},
//
listenScroll(e) {
const ele = e.srcElement ? e.srcElement : e.target
if (ele.scrollTop + ele.offsetHeight > ele.scrollHeight - 100) { // div
this.addMoreData()
}
},
//
addMoreData() {
if (this.hasNextPage) {
this.getBookRecommendList()
this.pageIndex++
}
},
getBookDetaisByBookRecNo(item) {
FetchBookDetaisByBookRecNo({
'bookRecNo': item.bookRecNo
}).then(res => {
this.$refs.bookDetailsRef.getBookRankingDetail(res)
}).catch(() => {
this.$message.error('接口错误')
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
::v-deep .el-dialog{
width: 930px;
border-radius: 16px;
background: #F8F8FD;
box-shadow: 0px 3px 20px 1px rgba(0,0,0,0.05);
.el-dialog__body{
padding: 20px 0 23px 0;
}
}
.detail-dialog{
::v-deep .el-dialog__header{
display: none;
}
}
.dialog-book{
padding: 30px;
font-size: 28px;
.book-txt{
color: #191A1A!important;
font-size: 30px !important;
}
.book-autor{
font-size: 24px !important;
}
.book-publish{
font-size: 28px !important;
}
}
</style>

546
src/views/girdList.vue

@ -0,0 +1,546 @@
<template>
<div class="content-main">
<Search refs="searchRefs" search-type="noList" />
<div class="box-style" style="margin: 30px 44px;">
<div class="dataScreening-header">
<h4 class="filter-header">架位总览</h4>
<div class="bookshelf-area">
<!-- <span class="bookshelf-area">{{ floorName }} - {{ regionName }} - {{ gridRow +'排' }}</span> -->
<!-- <router-link :to="{ path: '/', query: {floorTabIndex: floorTabIndex }}">
{{ floorName }}
</router-link>
<span>/</span>
<router-link :to="{ path: '/regions', query: {regionTabIndex: regionTabIndex }}">
{{ regionName }}
</router-link>
<span>/</span>
<router-link :to="{ path: '/shelf', query: {tabIndex: tabIndex }}">
{{ gridRow +'排' }}
</router-link>
<div class="double-click-btn"><span>点击左侧位置返回</span></div> -->
<router-link :to="{ path: '/shelf', query: {tabIndex: tabIndex }}">
<i class="iconfont icon-huifu" />返回
</router-link>
</div>
</div>
<div class="tab-content">
<ul class="tab-nav">
<li v-for="(item,index) in tabListData" :key="index" class="active-tab-nav">{{ item.name }}<i /></li>
</ul>
<div v-loading="shelfAllGridDataLoading" class="gird-data-book">
<div v-for="(item,index) in shelfAllGridData" :key="index" class="gird-layer">
<span class="gird-left-line" />
<span class="gird-right-line" />
<div class="layer-left">
<div class="layer-left-book">
<!-- <div
v-for="(book,i) in item.books"
:key="i"
:class="['book-item', { 'red-active' : book.bookStatus === '错架'} ,{ 'blue-active' : book.bookStatus === '错序'}]"
@touchstart="handleTouchStart(index, i, $event)"
@touchmove="handleTouchMove(index, i, $event)"
@touchend="handleTouchEnd(index, i, $event)"
>
<span class="book-name">{{ book.bookName }}</span>
</div> -->
<!-- @touchstart="handleTouchStart(index, i, book, $event)" -->
<div :class="['swiper'+index,'rack-box-list']">
<div class="swiper-wrapper">
<div v-for="book in item.books" :key="book.id" :class="['list-item swiper-slide', { 'red-active' : book.bookStatus === '错架'} ,{ 'blue-active' : book.bookStatus === '错序'}]" @click="getBookDetaisByBookRecNo(book)">
<span class="book-name">{{ book.bookName }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="layer-right-handle">
<div class="layer-info-header">
<h4>{{ item.gridName }}</h4>
<!-- <span>2024-11-28 09:46</span> -->
</div>
<div class="layer-right-content">
<div class="layer-tag-info">
<div class="tag-item tag-sort">错序<i class="iconfont icon-zhuangtai2" /><p>{{ baseStockData.length !==0 && baseStockData[index].errorOrderNum }}</p><span>{{ baseStockData.length !==0 && baseStockData[index].errorOrderProbo }}</span></div>
<div class="tag-item tag-place">错架<i class="iconfont icon-zhuangtai2" /><p>{{ baseStockData.length !==0 &&baseStockData[index].errorShelfNum }}</p><span>{{ baseStockData.length !==0 && baseStockData[index].errorShelfProbo }}</span></div>
<div class="tag-item tag-all">在架<i class="iconfont icon-zhuangtai2" /><p>{{ baseStockData.length !==0 && baseStockData[index].onShelfNum }}</p></div>
</div>
</div>
</div>
</div>
<!-- popover -->
<div
v-if="popoverIndex !== null"
class="popover-external-set"
:style="popoverStyle"
>
<div class="popover-content-set">
<div class="tooltip-top">
<h4>{{ shelfAllGridData[popoverIndex.layer].books[popoverIndex.book].bookName }}</h4>
<span v-if="shelfAllGridData[popoverIndex.layer].books[popoverIndex.book].bookStatus === '错序'" class="tag-item tag-place">错序</span>
<span v-if="shelfAllGridData[popoverIndex.layer].books[popoverIndex.book].bookStatus === '错架'" class="tag-item tag-sort">错架</span>
</div>
<ul>
<li><p>索书号</p><em>{{ shelfAllGridData[popoverIndex.layer].books[popoverIndex.book].sortmark }}</em></li>
<li><p>ISBN</p><em>{{ shelfAllGridData[popoverIndex.layer].books[popoverIndex.book].isbn }}</em></li>
<li><p>著者</p><em>{{ shelfAllGridData[popoverIndex.layer].books[popoverIndex.book].bookAuthor }}</em></li>
<li><p>出版社</p><em>{{ shelfAllGridData[popoverIndex.layer].books[popoverIndex.book].bookPublish }}</em></li>
</ul>
</div>
</div>
</div>
<!-- <ul style="display: flex; justify-content: flex-start; padding: 20px; margin-top: 20px;">
<li v-for="(item,index) in rackOptions" :key="index" style=" background-color: red; padding: 0 10px; margin-right: 10px;">{{ item.name }}</li>
</ul> -->
<ul class="change-layer">
<li v-for="(item,index) in rackOptions" :key="index" :class="{ 'active': layerVal === index+1 }" @click="changeShelfGetGrid(parseInt(item.name))">{{ item.name }}</li>
</ul>
</div>
</div>
<BookDetails ref="bookDetailsRef" />
</div>
</template>
<script>
import { FetchShelfGridByShelfIdAndGridShelf, FetchBillByShelfIdAndGridShelf, FetchBookShelfDetails, FetchInitStockInfo, FetchInitBookDetailsByGrids, FetchBookDetaisByBookRecNo } from '@/api/inquiryMachine'
import { positionCrud } from './mixins/index.js'
import Search from './module/search'
import BookDetails from './module/bookDetails'
import { Swiper } from 'vue-awesome-swiper'
import 'swiper/swiper-bundle.css'
export default {
name: 'Index',
components: {
Search,
BookDetails
},
mixins: [positionCrud],
data() {
return {
activeIndex: 0,
detailVisible: false,
detailTable: [],
detailCurrent: {},
tabdialogIndex: 0,
listLoading: false,
tabIndex: 0,
floorTabIndex: 0,
regionTabIndex: 0,
floorName: null,
floorId: null,
regionName: null,
regionId: null,
gridRow: null,
gridShelf: null,
bookShelfDetails: null,
booShelfGrid: null,
shelfAllGridData: [],
layerNum: 0,
rackNum: 0,
bookNum: 46,
layerVal: null,
rackOptions: [],
tabListData: [],
popoverIndex: null,
popoverVisible: [],
popoverStyles: [],
popoverPosition: { x: 0, y: 0 }, // Popover
baseStockData: [],
shelfAllGridDataLoading: false,
billNoImg: null,
bigImg: ''
}
},
computed: {
cellStyle: function() {
const h = '76px'
const w = '100%/' + this.rackNum
return { width: `calc(${w} )`, height: `calc(${h})` }
},
popoverStyle() {
return {
top: `${this.popoverPosition.y - 300}px`, // Y10px
left: `${this.popoverPosition.x - 240}px`, // X10px
position: 'absolute'
}
}
},
created() {
if (localStorage.getItem('dataScreenFloorTableIndex')) {
this.floorTabIndex = localStorage.getItem('dataScreenFloorTableIndex')
}
if (localStorage.getItem('dataScreenRegionTableIndex')) {
this.regionTabIndex = localStorage.getItem('dataScreenRegionTableIndex')
}
if (localStorage.getItem('dataScreenShelf')) {
const dataScreenShelf = JSON.parse(localStorage.getItem('dataScreenShelf'))
this.floorName = dataScreenShelf.floorName
this.regionName = dataScreenShelf.regionName
this.gridRow = dataScreenShelf.gridRow
this.gridShelf = dataScreenShelf.gridShelf
if (localStorage.getItem('dataScreenShelfAllGrid')) {
this.shelfAllGridData = JSON.parse(localStorage.getItem('dataScreenShelfAllGrid'))
}
// /
this.tabListData = [{ name: dataScreenShelf.regionName + ' - ' + this.removeAreaPrefix(dataScreenShelf.gridName) }]
this.tabIndex = dataScreenShelf.toward - 1
FetchBookShelfDetails({ 'shelfId': dataScreenShelf.shelfId }).then(res => {
this.layerNum = res.shelfFloor
this.rackNum = res.shelfShelf
this.floorId = res.floorId
this.regionId = res.regionId
this.bookShelfDetails = res
this.rackOptions = []
for (let i = 1; i <= this.rackNum; i++) {
this.rackOptions.push({ id: i, name: `0${i}` })
}
this.layerVal = parseInt(this.gridShelf) || ''
this.getInitStockInfo(this.shelfAllGridData)
this.getInitBookDetailsByGrids(this.shelfAllGridData)
// this.getBillByShelfIdAndGridShelf(this.shelfAllGridData)
this.initSwiper()
}).catch(() => {
})
}
},
mounted() {
},
methods: {
initSwiper() {
this.$nextTick(() => {
this.shelfAllGridData.forEach((el, index) => {
new Swiper('.swiper' + index, {
slidesPerView: 'auto',
slidesPerGroup: 15,
observer: true
})
})
})
},
removeAreaPrefix(gridNames) {
const index = gridNames.indexOf('面')
if (index !== -1) {
return gridNames.substring(0, index + 1)
}
return gridNames
},
getBillByShelfIdAndGridShelf(data) {
const params = {
'gridShelf': data[0].gridShelf,
'ShelfId': data[0].shelfId,
'size': 5
}
FetchBillByShelfIdAndGridShelf(params).then(res => {
if (res !== null) {
this.checkDateLine = res.sort((a, b) => {
return new Date(b.endTime) - new Date(a.endTime)
})
this.billNoImg = this.checkDateLine[0].stockBill
this.checkDateLine[0].icon = 'el-icon-more'
this.checkDateLine[0].color = '#0bbd87'
} else {
this.checkDateLine = []
}
}).catch(() => {
})
},
async getInitStockInfo(data) {
const promises = data.map(item => {
const params = {
'fondsCode': this.libcode,
'floorId': this.floorId,
'regionId': this.regionId,
'shelfId': item.shelfId,
'gridId': item.id
}
return FetchInitStockInfo(params)
})
const results = await Promise.all(promises)
if (!Array.isArray(this.baseStockData)) {
this.baseStockData = []
}
this.baseStockData = this.baseStockData.concat(results)
},
getInitBookDetailsByGrids(data) {
const ids = data.map(item => item.id)
const params = {
'grids': ids.join(',')
}
FetchInitBookDetailsByGrids(params).then(res => {
this.shelfAllGridData.forEach((item) => {
const gridId = item.id
if (Object.prototype.hasOwnProperty.call(res, gridId)) {
// item.books = res[gridId]
this.$set(item, 'books', res[gridId])
}
})
}).catch(() => {
})
},
handleDetail(item) {
console.log('item', item)
this.detailVisible = true
this.detailTable = item.books
this.detailCurrent = item
if (this.detailCurrent.onShelfNum) {
this.bigImg = `${this.baseApi}/api/fileRelevant/getImg?imgId=/${this.libcode}/${this.billNoImg}/${this.detailCurrent.gridCode}/img_result/result_LSD_compressed.jpg`
} else {
this.bigImg = ''
}
},
// handleTouchStart(item) {
// // this.isTouching = true
// // this.showPopover(layerIndex, bookIndex, event)
// // this.$refs.bookDetailsRef.detailShow = true
// },
getBookDetaisByBookRecNo(item) {
FetchBookDetaisByBookRecNo({
'bookRecNo': item.bookRecNo
}).then(res => {
this.$refs.bookDetailsRef.getBookRankingDetail(res)
}).catch(() => {
this.$message.error('接口错误')
})
},
handleTouchMove(layerIndex, bookIndex, event) {
if (this.isTouching) {
this.showPopover(layerIndex, bookIndex, event)
}
},
handleTouchEnd(layerIndex, bookIndex, event) {
this.isTouching = false
this.hidePopover()
},
showPopover(layerIndex, bookIndex, event) {
this.popoverIndex = { layer: layerIndex, book: bookIndex }
const bookElement = event.target //
const rect = bookElement.getBoundingClientRect() //
if (layerIndex === 0) {
this.popoverPosition = {
x: window.scrollX + rect.left + rect.width / 2 - 40,
y: window.scrollY + rect.top + rect.height
}
} else {
this.popoverPosition = {
x: window.scrollX + rect.left + rect.width / 2 - 40,
y: window.scrollY + rect.top + rect.height - 170
}
}
},
hidePopover() {
this.popoverIndex = null
},
changeShelfGetGrid(val) {
this.shelfAllGridDataLoading = true
this.layerVal = val
const params = {
'gridShelf': '0' + val,
'shelfId': this.bookShelfDetails.id,
'toward': this.bookShelfDetails.toward,
'floorType': this.bookShelfDetails.floorType
}
FetchShelfGridByShelfIdAndGridShelf(params).then(res => {
this.shelfAllGridData = []
this.shelfAllGridData = res
this.getInitStockInfo(this.shelfAllGridData)
this.getInitBookDetailsByGrids(this.shelfAllGridData)
this.shelfAllGridDataLoading = false
}).catch(() => {
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.gird-data-book{
position: relative;
// padding: 0 10px;
// height: 650px;
// margin-top: 4px;
// overflow: hidden;
// overflow-y: scroll;
}
.gird-layer{
position: relative;
display: flex;
justify-content: space-between;
.gird-left-line{
position: absolute;
left: 0;
top: 0;
display: block;
width: 6px;
height: 130px;
background: url('~@/assets/images/shelf01.png') no-repeat left top;
background-size: 100% 100%;
z-index: 999;
}
.gird-right-line{
position: absolute;
right: calc(310px);
top: 0;
display: block;
width: 6px;
height: 130px;
background: url('~@/assets/images/shelf01.png') no-repeat left top;
background-size: 100% 100%;
z-index: 999;
}
.layer-left{
width: calc(100% - 300px);
height: 130px;
margin-right: 5px;
padding: 0 0 0 12px;
background: url('~@/assets/images/shelf02.png') repeat left top;
background-size: 10% 100%;
overflow: hidden;
overflow-x: scroll;
}
.layer-left-book{
// width: calc(100vw - 655px);
height: 130px;
white-space: nowrap;
.rack-box-list{
height: 200px;
width: 100%;
overflow: hidden;
white-space: nowrap;
}
.list-item{
display: inline-block;
border: none;
width: 42px;
height: 120px;
background: url('~@/assets/images/shelf03.png') no-repeat left top;
background-size: 100% 100%;
position: relative;
margin-left: -14px;
margin-top: 10px;
.book-name{
position: absolute;
left: 12px;
top: 16px;
display: block;
height: 90px;
font-size: 16px;
writing-mode:vertical-rl;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&.red-active{
background: url('~@/assets/images/shelf05.png') no-repeat left top;
background-size: 100% 100%;
}
&.blue-active{
background: url('~@/assets/images/shelf06.png') no-repeat left top;
background-size: 100% 100%;
}
}
// .book-item{
// position: relative;
// display: inline-block;
// width: 42px;
// height: 120px;
// margin-top: 18px;
// background: url('~@/assets/images/shelf03.png') no-repeat left top;
// background-size: 100% 100%;
// margin-left: -15px;
// cursor: pointer;
// span.book-name{
// position: absolute;
// left: 15px;
// top: 16px;
// display: block;
// height: 90px;
// font-size: 12px;
// writing-mode:vertical-rl;
// overflow: hidden;
// white-space: nowrap;
// text-overflow: ellipsis;
// }
// &.red-active{
// background: url('~@/assets/images/shelf05.png') no-repeat left top;
// background-size: 100% 100%;
// }
// &.blue-active{
// background: url('~@/assets/images/shelf06.png') no-repeat left top;
// background-size: 100% 100%;
// }
// }
}
}
.layer-right-handle{
width: 300px;
padding: 0 10px;
background-color: #E8F2FF;
margin: 3px 5px;
border-radius: 6px;
}
.layer-info-header{
display: flex;
justify-content: space-between;
align-items: center;
line-height: 36px;
h4{
font-size: 20px;
color: #0c0e1e;
}
span{
font-size: 12px;
}
}
.layer-right-content{
display: flex;
justify-content: space-between;
}
.layer-tag-info{
div.tag-item {
display: flex;
justify-content: flex-start;
line-height: 26px;
font-size: 18px;
}
}
.layer-handle{
display: flex;
flex-direction: column;
.el-button,
.el-button--info.is-plain {
margin-left: 0 !important;
margin-bottom: 10px;
padding: 7px 10px !important;
font-weight: bold !important;
border-color: #0348f3 !important;
color: #0348f3 !important;
}
.check-btn{
color: #fff !important;
background-color: #0348f3 !important;
}
}
</style>

163
src/views/index.vue

@ -0,0 +1,163 @@
<template>
<div class="content-main">
<Search refs="searchRefs" search-type="noList" />
<div class="box-style" style="margin: 30px 44px;">
<div class="dataScreening-header">
<h4 class="filter-header">图书馆总览</h4>
</div>
<div class="home-wrap tab-content">
<ul class="tab-nav">
<li v-for="(item,index) in floorOptions" :key="index" :class="{ 'active-tab-nav': tabIndex == index }" @click="changeActiveTab(index)">{{ item.floorName }}<i /></li>
</ul>
<CanvasPreview ref="previewRefs" v-loading="prewLoading" page-preview="floor" :current-mark-data="currentMarkData" :image-url="imageUrl" />
</div>
</div>
<div class="index-ranking">
<div class="box-style">
<Ranking form-ranking="home" />
</div>
<div class="box-style">
<ShelfRanking />
</div>
</div>
</div>
</template>
<script>
import { FetchLibraryFloorListByFondsNo, FetchRegionListByFloorId, FetchInitStockInfo } from '@/api/inquiryMachine'
import CanvasPreview from './module/canvasPreview'
import { positionCrud } from './mixins/index.js'
import Search from './module/search'
import Ranking from './module/ranking'
import ShelfRanking from './module/shelfRanking'
import defaultImg from '@/assets/images/default-img-bg.jpg'
export default {
name: 'Index',
components: {
Search, CanvasPreview, Ranking, ShelfRanking
},
mixins: [positionCrud],
data() {
return {
prewLoading: false,
floorOptions: [],
tabIndex: 0,
defaultImg: defaultImg,
imageUrl: defaultImg,
imageRegionUrl: defaultImg,
currentMarkData: null,
allCoverData: [],
baseStockDataAllShelf: []
}
},
created() {
this.getFloorList()
},
mounted() {
},
methods: {
getFloorList() {
FetchLibraryFloorListByFondsNo({ 'fondsCode': this.libcode }).then(res => {
this.floorOptions = res
this.changeActiveTab(this.tabIndex)
}).catch(() => {
this.$message.error('接口错误')
})
},
async changeActiveTab(index) {
const baseUrl = process.env.NODE_ENV === 'production' ? window.g.ApiUrl : process.env.VUE_APP_BASE_API
this.prewLoading = true
if (this.$refs.previewRefs.canvasPreview.lowerCanvasEl) {
this.$refs.previewRefs.canvasPreview.clear()
this.$refs.previewRefs.canvasPreview.dispose()
}
this.allCoverData = []
this.tabIndex = index
const params = {
'floorId': this.floorOptions[index].id
}
try {
const res = await FetchRegionListByFloorId(params)
console.log(res)
this.allCoverData = res
if (this.floorOptions[index].floorMap) {
this.imageUrl = baseUrl + '/api/fileRelevant/getImg?imgId=' + this.floorOptions[index].floorMap
} else {
this.imageUrl = this.defaultImg
}
if (this.allCoverData.length !== 0) {
this.currentMarkData = this.allCoverData[0]
// const signPoint = this.allCoverData.find(item => item.signPoint !== null)?.signPoint
const imgInfo = JSON.parse(this.allCoverData[0].signPoint).imgInfo
const baseStockDataAllShelf = await this.getInitStockInfo(this.allCoverData)
const parsedSignPoints = this.allCoverData.map(item => {
const signPoint = item.signPoint ? JSON.parse(item.signPoint) : null
return {
id: item.id,
name: item.regionName,
floorName: this.floorOptions[index].floorName,
floorId: item.floorId,
pointInfo: signPoint ? signPoint.pointInfo[0].pointInfo : null
}
})
parsedSignPoints.forEach(parsedItem => {
const baseStockItem = baseStockDataAllShelf.find(baseItem => baseItem.id === parsedItem.id)
if (baseStockDataAllShelf) {
Object.assign(parsedItem, baseStockItem)
}
})
const result = {
pointInfo: parsedSignPoints,
imgInfo: imgInfo
}
console.log('result', result)
this.$nextTick(() => {
this.$refs.previewRefs.initCanvasPreview(result, this.tabIndex)
})
} else {
this.currentMarkData = {}
setTimeout(() => {
this.prewLoading = false
}, 500)
}
} catch (error) {
console.error(error)
}
},
async getInitStockInfo(data) {
const promises = data.map(item => {
const params = {
'fondsCode': this.libcode,
'floorId': this.floorOptions[this.tabIndex].id,
'regionId': item.id
}
return FetchInitStockInfo(params)
})
const results = await Promise.all(promises)
if (!Array.isArray(this.baseStockDataAllShelf)) {
this.baseStockDataAllShelf = []
}
// id
results.forEach((result, index) => {
result.id = data[index].id
})
this.baseStockDataAllShelf = this.baseStockDataAllShelf.concat(results)
return this.baseStockDataAllShelf
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
</style>

344
src/views/mixins/index.js

@ -0,0 +1,344 @@
import { FetchShelfGridAllByShelfIdText, FetchBookShelfDetails } from '@/api/inquiryMachine'
import defaultImg from '@/assets/images/default-img-bg.jpg'
import { mapGetters } from 'vuex'
import { fabric } from 'fabric'
export const positionCrud = {
mixins: [],
// 组件共用属性
data() {
return {
positionVisible: false,
positionTitle: '图书定位',
positionContent: {},
currentMarkData: null,
defaultImg: defaultImg,
imageUrl: defaultImg,
canvasPreview: {},
width: 900,
height: 600,
drawWidth: 2, // 笔触宽度
booShelfGrid: [],
layerNum: 0,
rackNum: 0,
cellIndex: 0,
bookShelfDetails: {},
getGridShelf: '',
getGridFloor: '',
getGridToward: 1
}
},
computed: {
...mapGetters([
'user',
'baseApi'
]),
cellStyle: function() {
const h = '38px'
let w
if (this.rackNum === 1) {
w = '146px'
} else {
w = '100%/' + this.rackNum
}
return { width: `calc(${w})`, height: `calc(${h})` }
},
rowStyle: function() {
let w
if (this.rackNum === 1) {
w = 146 * this.rackNum + 'px'
} else {
w = '100%'
}
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 []
}
}
},
// 组件挂载时的共用方法
mounted() {
},
// 组件共用方法
methods: {
removeAreaPrefix(gridNames) {
const index = gridNames.indexOf('面')
if (index !== -1) {
return gridNames.substring(index + 1)
}
return gridNames
},
handlePosition(row, type) {
const baseUrl = process.env.NODE_ENV === 'production' ? window.g.ApiUrl : process.env.VUE_APP_BASE_API
this.booShelfGrid = []
this.currentMarkData = {}
if (this.canvasPreview.lowerCanvasEl) {
this.canvasPreview.clear()
this.canvasPreview.dispose()
}
this.positionVisible = true
console.log('type', type)
let params
if (type !== 'hot') {
this.positionTitle = '图书定位 - 《' + row.bookName + '》'
this.updateParts(row.gridName)
params = {
'shelfId': row.actualShelfId
}
this.positionContent = row
} else {
this.positionTitle = '架位定位'
params = {
'shelfId': row.shelf_id
}
this.updateParts(row.grid_name)
}
FetchBookShelfDetails(params).then(res => {
this.layerNum = res.shelfFloor
this.rackNum = res.shelfShelf
this.bookShelfDetails = res
this.currentMarkData = res
if (row.regionMap) {
this.imageUrl = baseUrl + '/api/fileRelevant/getImg?imgId=' + row.regionMap
this.$nextTick(() => {
const drawinfo = JSON.parse(res.signPoint)
this.initCanvasPreview(drawinfo, type)
})
} else {
this.imageUrl = this.defaultImg
}
this.getInitShelfGridByShelfId(this.getGridToward)
}).catch(() => {
})
},
updateParts(gridName) {
const parts = gridName.match(/(\d+)区(\d+)排([A-Za-z])面(\d+)架(\d+)层/)
if (parts) {
this.getGridShelf = parts[4]
this.getGridFloor = parts[5]
const toward = parts[3]
if (toward === 'A') {
this.getGridToward = 1
} else if (toward === 'B') {
this.getGridToward = 2
}
}
},
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 '最底层为第一层(从下至上)'
FetchShelfGridAllByShelfIdText({ '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)
const index = this.booShelfGrid.findIndex((obj) => {
return obj.gridShelf === this.getGridShelf && obj.gridFloor === this.getGridFloor
})
this.cellIndex = index
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
},
initCanvasPreview(drawinfo, type) {
console.log('type222', type)
if (!this.currentMarkData) {
console.error('currentMarkData is null or undefined')
return
}
let canvasId
if (type === 'hot') {
canvasId = `canvasPreview${this.currentMarkData.id}${type}`
} else {
canvasId = `canvasPreview${this.currentMarkData.id}`
}
this.canvasPreview = new fabric.Canvas(canvasId, {
skipTargetFind: false,
selectable: false,
selection: false
})
this.$nextTick(() => {
this.canvasPreview.selectionColor = 'rgba(0,0,0,0.05)'
this.loadDrawPreview(drawinfo)
this.canvasPreview.on('mouse:wheel', this.mouse)
})
},
// 鼠标滚轮放大缩小
mouse(e) {
if (undefined === e) return
let zoom = (e.e.deltaY > 0 ? -0.1 : 0.1) + this.canvasPreview.getZoom()
zoom = Math.max(0.8, zoom)
// 最小为原来的1/10
zoom = Math.min(3, zoom)
// 最大是原来的3倍
const zoomPoint = new fabric.Point(e.e.pageX, e.e.pageY)
this.canvasPreview.zoomToPoint(zoomPoint, zoom)
},
// 回显详情信息
loadDrawPreview(drawinfo) {
const self = this
const pointGroup = drawinfo.pointInfo
const imgInfo = drawinfo.imgInfo
imgInfo.src = self.imageUrl
// 加载底图
fabric.util.enlivenObjects([imgInfo], objects => {
objects.forEach(o => {
o.selectable = false
o.hasControls = false
o.centeredScaling = false
self.canvasPreview.add(o)
})
// 处理多边形绘制回显操作
pointGroup.forEach(async(item, index) => {
if (item.pointInfo !== '') {
const polygon = new fabric.Polygon(item.pointInfo, {
name: item.name,
stroke: 'rgba(196, 43, 1, 1)',
strokeWidth: self.drawWidth,
fill: 'rgba(196, 43, 1, 1)',
opacity: 0.5,
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'left',
originY: 'top'
})
self.canvasPreview.add(polygon)
polygon.on('mousedown', function(e) {
console.log('Rect' + (index + 1) + 'clicked', e)
console.log('e.target.name', e.target.name)
})
polygon.on('mouseover', function(e) {
console.log('e', e)
console.log('e.target', e.target)
console.log('e.target.name', e.target.name)
this.set({ opacity: 0.8, hoverCursor: 'pointer' })
self.canvasPreview.renderAll()
})
polygon.on('mouseout', function() {
this.set({ opacity: 0.5 })
self.canvasPreview.renderAll()
})
// 加载完成后直接对多边形执行zoomToPoint操作
const center = polygon.getCenterPoint()
self.canvasPreview.zoomToPoint({ x: center.x, y: center.y }, 1.4)
}
})
})
self.canvasPreview.renderAll()
},
handleCloseDialog() {
this.positionVisible = false
this.detailVisible = false
}
}
}

195
src/views/module/bookDetails.vue

@ -0,0 +1,195 @@
<template>
<div>
<!-- 图书详情dialog -->
<el-dialog class="detail-dialog" append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :visible.sync="detailShow">
<div class="dialog-book">
<div class="book-info">
<div class="book-img">
<img :src="coverUrl+'/demoRecommend/getBookCover.do?id='+detailData.id" :onerror="defaultImg" alt="">
</div>
<div class="book-txt">
<h4> {{ detailData.bookBasics.title }} </h4>
<div class="book-autor"> {{ detailData.bookBasics.author }} </div>
<div class="book-publish"> {{ detailData.bookBasics.publisher }} </div>
<div class="book-isbn">ISBN{{ detailData.bookBasics.isbn }}</div>
<div class="book-isbn">分类号{{ detailData.bookBasics.classNo }}</div>
</div>
</div>
<div class="book-intro"> {{ detailData.bookBasics.summary }} </div>
</div>
<div class="book-place">
<h4>馆藏列表</h4>
<ul class="book-place-list">
<li v-for="(item,index) in detailData.bookDetailsList" :key="index" @click="handlePosition(item)">
<span>{{ index+1 }}</span>
<p class="book-num">{{ item.sortmark }}</p>
<p class="place-detail"> {{ getLocation(item) }}</p>
</li>
</ul>
</div>
<div class="book-bottom" @click="handleClosed">
<span>关闭</span>
</div>
</el-dialog>
<el-dialog class="positionDialog" append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="handleCloseDialog" :visible="positionVisible" :title="positionTitle">
<div class="setting-dialog">
<ul class="book-detail">
<li><span>所属楼层</span>{{ positionContent.floorName }}</li>
<li><span>所属区域</span>{{ positionContent.regionName }}</li>
<li><span>所属书架</span>{{ positionContent.gridName }}</li>
</ul>
<div class="position-content">
<div class="position-left">
<h5>平面图</h5>
<div class="venue-preview">
<div v-show="currentMarkData && currentMarkData.signPoint ">
<canvas :id="`canvasPreview${currentMarkData && currentMarkData.id}`" :width="width" :height="height" />
</div>
<img v-if="currentMarkData && !currentMarkData.signPoint" :src="imageUrl" :onerror="defaultImg" alt="">
<img v-if="!currentMarkData" :src="imageUrl" :onerror="defaultImg" alt="">
</div>
</div>
<div class="position-right">
<h5>书架图</h5>
<div class="shelf-top" :style="rowStyle">
<p v-for="(item,index) in reversedRackNum" :key="index" :style="{width: `calc(${'100%/' + rackNum} - 4px )`}"><span>{{ item + '架' }}</span></p>
</div>
<ul class="data-shelf-row" :style="rowStyle">
<li
v-for="(cell,i) in booShelfGrid"
:key="i"
:class="{ active: i === cellIndex }"
class="data-shelf-cell"
:style="cellStyle"
>
<span class="cell-name">{{ removeAreaPrefix(cell.gridName) }}</span>
</li>
</ul>
</div>
</div>
</div>
<div class="book-bottom" @click="handleCloseDialog">
<span>关闭</span>
</div>
</el-dialog>
</div>
</template>
<script>
import { FetchInitBookDetailsSearchInto } from '@/api/inquiryMachine'
import { positionCrud } from '../mixins/index.js'
export default {
name: 'BookDetails',
mixins: [positionCrud],
data() {
return {
booShelfGrid: [],
coverUrl: null,
detailShow: false,
positionVisible: false,
positionContent: {},
detailData: {
bookBasics: {},
bookDetailsList: []
},
defaultImg: 'this.src="' + require('@/assets/images/default-img.png') + '"'
}
},
computed: {
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 []
}
}
},
created() {
},
methods: {
getLocation(row) {
const parts = []
if (row.floorName) {
parts.push(row.floorName)
}
if (row.regionName) {
parts.push(row.regionName)
}
if (row.gridName) {
parts.push(row.gridName)
}
return parts.length > 0 ? parts.join('-') : '-'
},
getBookRankingDetail(item) {
this.detailShow = true
this.detailData = item
},
getBookDetail(item) {
FetchInitBookDetailsSearchInto({ 'id': item.id }).then((res) => {
this.detailShow = true
if (res && Object.keys(res).length !== 0) {
console.log('res', res)
this.detailData = res
} else {
console.log('res', '111')
this.detailData = null
}
}).catch(() => {
this.$message.error('接口错误')
})
},
handleClosed() {
this.detailShow = false
},
handleCloseDialog() {
this.positionVisible = false
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
::v-deep .el-dialog{
width: 930px;
border-radius: 16px;
background: #F8F8FD;
box-shadow: 0px 3px 20px 1px rgba(0,0,0,0.05);
.el-dialog__body{
padding: 20px 0 23px 0;
}
}
.detail-dialog{
::v-deep .el-dialog__header{
display: none;
}
}
::v-deep .el-dialog__headerbtn{
display: none;
}
::v-deep .el-dialog__title{
font-weight: bold;
}
::v-deep .el-dialog__header{
border-bottom: 1px solid #EDEFF3;
}
.dialog-book{
padding: 30px;
font-size: 28px;
.book-txt{
color: #191A1A!important;
font-size: 30px !important;
}
.book-autor{
font-size: 24px !important;
}
.book-publish{
font-size: 28px !important;
}
}
</style>

337
src/views/module/canvasPreview.vue

@ -0,0 +1,337 @@
<template>
<div class="venue-preview">
<!-- v-show="currentMarkData && currentMarkData.signPoint" -->
<div v-show="currentMarkData && currentMarkData.signPoint">
<canvas :id="`canvasPreview${currentMarkData && currentMarkData.id}`" :width="width" :height="height" />
</div>
<img v-if="currentMarkData &&!currentMarkData.signPoint" :src="imageUrl" :onerror="defaultImg" alt="">
<div id="tooltip" class="tooltip-style">
<!-- <div class="tooltip-top">
<h4>区域名称</h4>
<span class="update-time">2024-11-28 09:46</span>
</div>
<ul>
<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>
</template>
<script>
// import { dataScreeningCrud } from '@/views/visualCheck/mixins/index'
import defaultImg from '@/assets/images/default-img-bg.jpg'
import { fabric } from 'fabric'
import { mapGetters } from 'vuex'
export default {
name: 'Mark',
mixins: [],
props: {
currentMarkData: {
type: Object,
require: true,
default: function() {
return {}
}
},
imageUrl: {
type: String,
default: ''
},
pagePreview: {
type: String,
default: ''
}
},
data() {
return {
defaultImg: defaultImg,
canvasPreview: {},
width: 1200,
height: 600,
drawWidth: 2,
tooltipInfo: null,
initialDistance: 0,
initialZoom: 0
}
},
computed: {
...mapGetters([
'user',
'baseApi'
])
},
watch: {
width() {
this.canvasPreview.setWidth(this.width)
},
height() {
this.canvasPreview.setHeight(this.height)
},
currentMarkData: {
handler(newVal, oldVal) {
// newVal null undefined
if (!newVal) {
console.log('newVal - null')
return
}
},
deep: true
},
imageUrl(newVal, oldVal) {
if (newVal !== oldVal) {
console.log('imageUrl', newVal)
}
}
},
mounted() {
if (this.canvasPreview.lowerCanvasEl) {
this.canvasPreview.getCanvas().el.addEventListener('touchstart', this.handleTouchStart.bind(this))
this.canvasPreview.getCanvas().el.addEventListener('touchmove', this.handleTouchMove.bind(this))
this.canvasPreview.getCanvas().el.addEventListener('touchend', this.handleTouchEnd.bind(this))
}
},
beforeDestroy() {
// if (this.canvasPreview) {
// this.canvasPreview.clear()
// this.canvasPreview.dispose()
// }
},
methods: {
initCanvasPreview(drawinfo, tabIndex) {
if (!this.currentMarkData) {
console.error('currentMarkData - null')
return
}
const canvasId = `canvasPreview${this.currentMarkData.id}`
this.canvasPreview = new fabric.Canvas(canvasId, {
skipTargetFind: false,
selectable: false,
selection: false
})
this.$nextTick(() => {
this.canvasPreview.selectionColor = 'rgba(0,0,0,0.05)'
this.loadDrawPreview(drawinfo, tabIndex)
// this.canvasPreview.on('mouse:wheel', this.mouse)
})
},
//
handleTouchStart(e) {
if (e.touches.length === 2) {
const touch1 = e.touches[0]
const touch2 = e.touches[1]
this.initialDistance = Math.sqrt((touch2.clientX - touch1.clientX) * (touch2.clientX - touch1.clientX) + (touch2.clientY - touch1.clientY) * (touch2.clientY - touch1.clientY))
this.initialZoom = this.canvasPreview.getZoom()
}
},
handleTouchMove(e) {
if (e.touches.length === 2) {
const touch1 = e.touches[0]
const touch2 = e.touches[1]
const currentDistance = Math.sqrt((touch2.clientX - touch1.clientX) * (touch2.clientX - touch1.clientX) + (touch2.clientY - touch1.clientY) * (touch2.clientY - touch1.clientY))
let newZoom = this.initialZoom * (currentDistance / this.initialDistance)
newZoom = Math.max(0.8, newZoom)
newZoom = Math.min(3, newZoom)
const centerX = (touch1.clientX + touch2.clientX) / 2
const centerY = (touch1.clientY + touch2.clientY) / 2
const zoomPoint = new fabric.Point(centerX, centerY)
this.canvasPreview.zoomToPoint(zoomPoint, newZoom)
}
},
handleTouchEnd() {
//
},
//
loadDrawPreview(drawinfo, tabIndex) {
const self = this
const pointGroup = drawinfo.pointInfo
const imgInfo = drawinfo.imgInfo
imgInfo.src = self.imageUrl
//
fabric.util.enlivenObjects([imgInfo], objects => {
objects.forEach(o => {
o.selectable = false
o.hasControls = false
o.centeredScaling = false
self.canvasPreview.add(o)
})
//
pointGroup.forEach(async(item, index) => {
console.log('item', item)
if (item.pointInfo !== '') {
const polygon = new fabric.Polygon(item.pointInfo, {
id: item.id,
name: item.name,
floorId: item.floorId,
regionId: item.regionId,
rowType: item.rowType,
toward: item.toward,
floorName: item.floorName,
regionName: item.regionName,
shelfFloor: item.shelfFloor,
shelfShelf: item.shelfShelf,
errorOrderNum: item.errorOrderNum,
errorOrderProbo: item.errorOrderProbo,
errorShelfNum: item.errorShelfNum,
errorShelfProbo: item.errorShelfProbo,
onShelfNum: item.onShelfNum,
stroke: 'rgba(196,43, 1, 1)',
strokeWidth: self.drawWidth,
fill: 'rgba(196,43, 1, 1)',
opacity: 0.5,
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'left', //
originY: 'top' //
})
self.canvasPreview.add(polygon)
// const lastClickTime = 0
// const doubleClickInterval = 300
polygon.on('mousedown', function(e) {
// const currentTime = new Date().getTime()
// const timeDiff = currentTime - lastClickTime
const toReigonsData = {
id: e.target.id,
name: e.target.name,
floorId: e.target.floorId,
regionId: e.target.regionId,
rowType: e.target.rowType,
toward: e.target.toward,
regionName: e.target.regionName,
floorName: e.target.floorName
}
console.log('toReigonsData', toReigonsData)
if (self.pagePreview === 'floor') {
console.log('toReigonsData', toReigonsData)
self.handleToRegions(toReigonsData, tabIndex)
} else if (self.pagePreview === 'region') {
self.handleToShelfs(toReigonsData, tabIndex)
}
// if (timeDiff <= doubleClickInterval) {
// console.log('', e)
// lastClickTime = 0
// const toReigonsData = {
// id: e.target.id,
// name: e.target.name,
// floorId: e.target.floorId,
// regionId: e.target.regionId,
// rowType: e.target.rowType,
// toward: e.target.toward,
// regionName: e.target.regionName,
// floorName: e.target.floorName
// }
// console.log('toReigonsData', toReigonsData)
// if (self.pagePreview === 'floor') {
// self.handleToRegions(toReigonsData, tabIndex)
// } else if (self.pagePreview === 'region') {
// self.handleToShelfs(toReigonsData, tabIndex)
// }
// } else {
// lastClickTime = currentTime
// }
})
// polygon.on('mouseover', function(e) {
// this.tooltipInfo = {
// 'id': e.target.id,
// 'name': e.target.name,
// 'rowType': e.target.rowType,
// 'shelfFloor': e.target.shelfFloor,
// 'shelfShelf': e.target.shelfShelf,
// 'floorId': e.target.floorId,
// 'regionId': e.target.regionId,
// 'errorOrderNum': e.target.errorOrderNum,
// 'errorOrderProbo': e.target.errorOrderProbo,
// 'errorShelfNum': e.target.errorShelfNum,
// 'errorShelfProbo': e.target.errorShelfProbo,
// 'onShelfNum': e.target.onShelfNum
// }
// this.set({ opacity: 0.8, hoverCursor: 'pointer' })
// // <span class="update-time">2024-11-28 09:46</span>
// if (self.pagePreview === 'floor') {
// document.getElementById('tooltip').innerHTML =
// `<div class="tooltip-top">
// <h4>${this.tooltipInfo.name}</h4>
// </div>
// <ul>
// <li><p></p><span><i>${this.tooltipInfo.onShelfNum}</i></span></li>
// <li><p></p><span><i>${this.tooltipInfo.errorShelfNum}</i></span> <span class="percentage">${this.tooltipInfo.errorShelfProbo}</span></li>
// <li><p></p><span><i>${this.tooltipInfo.errorOrderNum}</i></span><span class="percentage">${this.tooltipInfo.errorOrderProbo}</span></li>
// </ul>`
// } else if (self.pagePreview === 'region') {
// document.getElementById('tooltip').innerHTML =
// `<div class="tooltip-top">
// <h4></h4>
// </div>
// <ul>
// <li><p></p><span><i>${this.tooltipInfo.name}</i></span></li>
// <li><p></p><span><i>${this.tooltipInfo.rowType === 1 ? '' : ''}${this.tooltipInfo.shelfFloor}*${this.tooltipInfo.shelfShelf}</i></span></li>
// <li><p></p><span><i>${this.tooltipInfo.onShelfNum}</i></span></li>
// <li><p></p><span><i>${this.tooltipInfo.errorShelfNum}</i></span> <span class="percentage">${this.tooltipInfo.errorShelfProbo}</span></li>
// <li><p></p><span><i>${this.tooltipInfo.errorOrderNum}</i></span><span class="percentage">${this.tooltipInfo.errorOrderProbo}</span></li>
// </ul>`
// }
// // var rectLeft = e.target.left + e.target.width - 100
// var rectLeft = e.target.left + e.target.width
// var rectTop = e.target.top + e.target.height - 40
// document.getElementById('tooltip').style.left = rectLeft + 'px'
// document.getElementById('tooltip').style.top = rectTop + 'px'
// document.getElementById('tooltip').style.display = 'block'
// self.canvasPreview.renderAll()
// })
// polygon.on('mouseout', function() {
// this.set({ opacity: 0.5 })
// document.getElementById('tooltip').style.display = 'none'
// self.canvasPreview.renderAll()
// })
}
})
//
const centerX = self.canvasPreview.width / 2
const centerY = self.canvasPreview.height / 2
const centerPoint = new fabric.Point(centerX, centerY)
//
self.canvasPreview.zoomToPoint(centerPoint, 1)
self.canvasPreview.renderAll()
setTimeout(() => {
self.$parent.prewLoading = false
}, 500)
})
},
handleToRegions(data, tabIndex) {
this.$router.push({ path: '/regions' })
localStorage.setItem('dataScreenFloor', JSON.stringify(data))
localStorage.setItem('dataScreenFloorTableIndex', tabIndex)
},
handleToShelfs(data, tabIndex) {
this.$router.push({ path: '/shelf' })
localStorage.setItem('dataScreenRegion', JSON.stringify(data))
localStorage.setItem('dataScreenRegionTableIndex', tabIndex)
}
}
}
</script>
<style lang="scss" scoped>
#expImg{
display: none;
}
.venue-preview{
width: 100%;
background-color: #fff;
overflow: hidden;
margin-top: -30px;
}
</style>

145
src/views/module/ranking.vue

@ -0,0 +1,145 @@
<template>
<div class="ranking">
<h4 class="filter-header">图书借阅排行榜</h4>
<el-carousel
ref="carousel"
:interval="4000"
type="card"
indicator-position="none"
height="200px"
@change="carouselChange"
>
<el-carousel-item v-for="(item,index) in topThree" :key="index">
<div class="ranking-img" @click="getBookDetaisByBookRecNo(item)">
<img :src="coverUrl+'/demoRecommend/getBookCover.do?id='+item.id" :onerror="defaultImg" alt="">
</div>
</el-carousel-item>
</el-carousel>
<div v-for="(item,i) in topThree" :key="i" class="ranking-book-detail">
<div v-if="i === carIndex">
<div class="book-info" @click="getBookDetaisByBookRecNo(item)">
<h5 class="title-item-only">{{ item.title }}</h5>
<!-- <div class="book-author" style="display: flex; justify-content: flex-start;">
<span class="title-item-only">{{ item.author }}</span>
<span class="title-item-only">{{ item.publish }}</span>
</div> -->
<!-- <p class="book-intro title-item">
{{ item.introduce }}
</p> -->
<div :class="carClass" />
</div>
<!-- <div class="book-click-num">
<span class="click-num">{{ item.clickNum }}</span>
<span class="detail-btn" @click="getBookDetail(item)">查看详情</span>
</div> -->
</div>
</div>
<ul class="ranking-list-bottom other-list">
<li v-for="(item,index) in rankingOther" :key="index" @click="getBookDetaisByBookRecNo(item)">
<span>{{ 'TOP'+ (index+4) }}</span>
<p class="title-item-only">{{ item.title }}</p>
</li>
</ul>
<BookDetails ref="bookDetailsRef" />
</div>
</template>
<script>
import { FetchInitHotBookList, FetchBookDetaisByBookRecNo } from '@/api/inquiryMachine'
import { positionCrud } from '../mixins/index.js'
import BookDetails from '../module/bookDetails'
export default {
name: 'Ranking',
components: {
BookDetails
},
mixins: [positionCrud],
props: {
formRanking: {
type: String,
default: ''
}
},
data() {
return {
defaultImg: 'this.src="' + require('@/assets/images/default-img.png') + '"',
bookList: [],
coverUrl: null,
pageIndex: 1,
pageSize: 10,
hasNextPage: false,
totalPages: 0,
totalNum: 0,
topThree: [],
rankingOther: [],
carIndex: 0
}
},
computed: {
carClass() {
if (this.carIndex === 0) {
return 'ranking-status num-one'
} else if (this.carIndex === 1) {
return 'ranking-status num-two'
} else if (this.carIndex === 2) {
return 'ranking-status num-three'
} else {
return 'ranking-status'
}
}
},
created() {
this.getHotBookList()
},
mounted() {
this.$refs.carousel.$el.addEventListener('current-change', this.carouselChange)
},
methods: {
getHotBookList() {
let params
if (this.formRanking === 'home') {
params = {
'fondsCode': this.libcode,
'size': 5
}
} else {
params = {
'fondsCode': this.libcode,
'size': 10
}
}
FetchInitHotBookList(params).then(res => {
this.topThree = res.slice(0, 3)
if (this.formRanking === 'home') {
this.rankingOther = res.slice(3, 7)
} else {
this.rankingOther = res.slice(3, 10)
}
}).catch(() => {
this.$message.error('接口错误')
})
},
getBookDetaisByBookRecNo(item) {
FetchBookDetaisByBookRecNo({
'bookRecNo': item.bookRecNo
}).then(res => {
this.$refs.bookDetailsRef.getBookRankingDetail(res)
}).catch(() => {
this.$message.error('接口错误')
})
},
carouselChange(val) {
this.carIndex = val
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.other-list{
height: 140px;
}
</style>

133
src/views/module/search.vue

@ -0,0 +1,133 @@
<template>
<div>
<div class="search-header-bg" />
<el-card class="box-card" style="margin: -220px 44px 0 44px; padding-bottom: 12px;">
<h4 style="font-size: 30px; margin-bottom: 24px; text-align: center;">图书检索</h4>
<div class="search-main">
<!-- 搜索栏 -->
<div class="search-content">
<div class="input-search">
<el-input
v-model="keyword"
placeholder="请输入关键词进行检索"
/>
<el-button type="primary" @click="toSearch">搜索</el-button>
</div>
<el-button @click="resetSearch">重置</el-button>
</div>
<!-- 热门检索 -->
<div class="hot-keyword">
<p style="margin-left: 10px;">热门检索</p>
<div class="keyword-item">
<!-- @click="selectKeyWord(index)" -->
<span v-for="(item,index) in keywordData" :key="index" :class="{ 'active': keyWordIndex === index }" @click="selectKeyWord(index)">{{ item }}</span>
</div>
</div>
</div>
</el-card>
</div>
</template>
<script>
import { FetchFindHotSearch } from '@/api/inquiryMachine'
import { positionCrud } from '../mixins/index.js'
export default {
name: 'Search',
mixins: [positionCrud],
props: {
searchType: {
type: String,
default: ''
}
},
data() {
return {
keyword: '',
keyWordIndex: null,
keywordData: []
}
},
created() {
this.getHotSearch()
},
mounted() {
},
methods: {
getHotSearch() {
const params = {
'fondsCode': this.libcode,
'size': 30
}
FetchFindHotSearch(params).then(res => {
this.keywordData = res.filter(item => {
return item !== ''
})
}).catch(() => {
this.$message.error('接口错误')
})
},
resetData() {
this.$parent.pageIndex = 1
this.$parent.hasNextPage = true
this.keyword = ''
this.$parent.bookList = []
},
resetSearch() {
this.$parent.pageIndex = 1
this.$parent.hasNextPage = false
this.keyword = ''
this.$parent.bookList = []
this.keyWordIndex = null
console.log('this.totalNum ', this.$parent.totalNum)
this.$parent.totalNum = 0
// this.$parent.addMoreData()
},
toSearch() {
if (this.keyword) {
this.keyWordIndex = null
if (this.searchType === 'noList') {
localStorage.setItem('searchKey', this.keyword)
this.$router.push({ path: '/bookList' })
} else {
this.$parent.pageIndex = 1
this.$parent.hasNextPage = true
this.$parent.bookList = []
this.$parent.addMoreData()
}
} else {
this.$message.error('请输入检索关键词')
}
},
selectKeyWord(index) {
this.resetData()
if (parseInt(this.keyWordIndex) === index) {
this.keyWordIndex = null
} else {
this.keyWordIndex = index
this.keyword = this.keywordData[index]
}
this.$forceUpdate()
if (this.searchType === 'noList') {
localStorage.setItem('searchKey', this.keyword)
localStorage.setItem('keyWordIndex', this.keyWordIndex)
this.$router.push({ path: '/bookList' })
this.$parent.pageIndex = 1
} else {
this.$parent.pageIndex = 1
this.$parent.hasNextPage = true
this.$parent.bookList = []
this.$parent.addMoreData()
}
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.search-header-bg{
height: 298px;
background: url("~@/assets/images/top-bg.png") no-repeat left top;
background-size: 100% 100%;
}
</style>

141
src/views/module/shelfRanking.vue

@ -0,0 +1,141 @@
<template>
<div class="ranking">
<h4 class="filter-header">热门书架</h4>
<ul class="ranking-list-bottom shelf-list">
<li v-for="(item,index) in shelfHot" :key="index" @click="handleShelfPosition(item)">
<span>{{ 'TOP'+ (index+1) }}</span>
<p class="title-item-only">{{ removeAreaPrefix(item.grid_name) }}</p>
</li>
</ul>
<el-dialog class="positionDialog" append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="handleCloseDialog" :visible="positionVisible" :title="positionTitle">
<div class="setting-dialog">
<ul class="book-detail">
<li><span>所属楼层</span>{{ positionContent.floorName }}</li>
<li><span>所属区域</span>{{ positionContent.regionName }}</li>
<li><span>所属书架</span>{{ positionContent.gridName }}</li>
</ul>
<div class="position-content">
<div class="position-left">
<h5>平面图</h5>
<div class="venue-preview">
<div v-show="currentMarkData && currentMarkData.signPoint ">
<canvas :id="`canvasPreview${currentMarkData && currentMarkData.id}hot`" :width="width" :height="height" />
</div>
<img v-if="currentMarkData && !currentMarkData.signPoint" :src="imageUrl" :onerror="defaultImg" alt="">
<img v-if="!currentMarkData" :src="imageUrl" :onerror="defaultImg" alt="">
</div>
</div>
<div class="position-right">
<h5>书架图</h5>
<div class="shelf-top" :style="rowStyle">
<p v-for="(item,index) in reversedRackNum" :key="index" :style="{width: `calc(${'100%/' + rackNum} - 4px )`}"><span>{{ item + '架' }}</span></p>
</div>
<ul class="data-shelf-row" :style="rowStyle">
<li
v-for="(cell,i) in booShelfGrid"
:key="i"
:class="{ active: i === cellIndex }"
class="data-shelf-cell"
:style="cellStyle"
>
<span class="cell-name">{{ removeLayerPrefix(cell.gridName) }}</span>
</li>
</ul>
</div>
</div>
</div>
<div class="book-bottom" @click="handleCloseDialog">
<span>关闭</span>
</div>
</el-dialog>
</div>
</template>
<script>
import { FetchInitHotShelfList } from '@/api/inquiryMachine'
import { positionCrud } from '../mixins/index.js'
export default {
name: 'Ranking',
components: {
},
mixins: [positionCrud],
props: {
},
data() {
return {
defaultImg: 'this.src="' + require('@/assets/images/default-img.png') + '"',
shelfHot: []
}
},
computed: {
},
created() {
this.getInitHotShelfList()
},
mounted() {
},
methods: {
removeLayerPrefix(gridNames) {
const index = gridNames.indexOf('面')
if (index !== -1) {
return gridNames.substring(index + 1)
}
return gridNames
},
handleShelfPosition(item) {
this.positionContent = {
'floorName': item.floorName,
'regionName': item.regionName,
'gridName': item.grid_name
}
this.handlePosition(item, 'hot')
},
removeAreaPrefix(gridNames) {
const index = gridNames.indexOf('区')
if (index !== -1) {
return gridNames.substring(index + 1)
}
return gridNames
},
getInitHotShelfList() {
const params = {
'fondsCode': this.libcode,
'size': 5
}
FetchInitHotShelfList(params).then(res => {
this.shelfHot = res
}).catch(() => {
this.$message.error('接口错误')
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.shelf-list{
height: 434px;
}
::v-deep .el-dialog{
width: 930px;
border-radius: 16px;
background: #F8F8FD;
box-shadow: 0px 3px 20px 1px rgba(0,0,0,0.05);
.el-dialog__body{
padding: 20px 0 23px 0;
}
}
::v-deep .el-dialog__headerbtn{
display: none;
}
::v-deep .el-dialog__title{
font-weight: bold;
}
::v-deep .el-dialog__header{
border-bottom: 1px solid #EDEFF3;
}
</style>

195
src/views/regionsList.vue

@ -0,0 +1,195 @@
<template>
<div class="content-main">
<Search refs="searchRefs" search-type="noList" />
<div class="box-style" style="margin: 30px 44px;">
<div class="dataScreening-header">
<h4 class="filter-header">区域总览</h4>
<div class="bookshelf-area">
<router-link :to="{ path: '/', query: {floorTabIndex: floorTabIndex }}">
<i class="iconfont icon-huifu" />返回
</router-link>
<!-- <div class="double-click-btn"><span>点击左侧位置返回</span></div> -->
</div>
</div>
<div class="home-wrap tab-content">
<!-- <ul class="fixed-menu">
<li v-for="(item,index) in regionOptions" :key="index" :class="{ 'active': tabIndex == index }" @click="changeActiveTab(index)">{{ item.regionName }}<i /></li>
</ul> -->
<ul class="tab-nav">
<li v-for="(item,index) in regionOptions" :key="index" :class="{ 'active-tab-nav': tabIndex == index }" @click="changeActiveTab(index)">{{ item.regionName }}<i /></li>
</ul>
<!-- <div class="total-data">
<span>楼层概况</span>
<p>区域{{ baseData.regionCount }}</p>
<p>书架{{ baseData.shelfCount }}</p>
<p>摄像头{{ baseData.deviceCount }}</p>
</div> -->
<CanvasPreview ref="previewRefs" v-loading="prewLoading" page-preview="region" :current-mark-data="currentMarkData" :image-url="imageUrl" />
</div>
</div>
</div>
</template>
<script>
import { FetchShelfListByRegionId, FetchRegionListByFloorId, FetchInitStockInfo } from '@/api/inquiryMachine'
import CanvasPreview from './module/canvasPreview'
import { positionCrud } from './mixins/index.js'
import Search from './module/search'
import defaultImg from '@/assets/images/default-img-bg.jpg'
export default {
name: 'Index',
components: {
Search, CanvasPreview
},
mixins: [positionCrud],
data() {
return {
prewLoading: false,
regionOptions: [],
floorTabIndex: 0,
floorId: null,
floorName: null,
currentRegionId: null,
tabIndex: 0,
defaultImg: defaultImg,
imageUrl: defaultImg,
imageRegionUrl: defaultImg,
currentMarkData: null,
allCoverData: [],
baseStockDataAllShelf: []
}
},
created() {
const data = JSON.parse(localStorage.getItem('dataScreenFloor'))
if (data) {
this.floorId = data.floorId
this.floorName = data.floorName
console.log('this.floorName', this.floorName)
this.currentRegionId = data.id
this.getRegionList(this.floorId)
}
},
mounted() {
},
methods: {
getRegionList(floorId) {
FetchRegionListByFloorId({ 'floorId': floorId }).then(res => {
this.regionOptions = res
if (localStorage.getItem('dataScreenFloorTableIndex')) {
this.floorTabIndex = localStorage.getItem('dataScreenFloorTableIndex')
}
const item = this.regionOptions.find(element => element.id === this.currentRegionId)
let index
if (this.$route.query.regionTabIndex) {
index = this.$route.query.regionTabIndex
} else {
index = item ? this.regionOptions.indexOf(item) : -1
}
console.log('this.regionOptions', this.regionOptions)
console.log('this.regionOptions', this.regionOptions[index])
this.tabIndex = index
this.changeActiveTab(index)
}).catch(() => {
this.$message.error('接口错误')
})
},
async changeActiveTab(index) {
const baseUrl = process.env.NODE_ENV === 'production' ? window.g.ApiUrl : process.env.VUE_APP_BASE_API
this.prewLoading = true
this.tabIndex = index
const params = {
'floorId': this.regionOptions[index].floorId,
'regionId': this.regionOptions[index].id
}
try {
const res = await FetchShelfListByRegionId(params)
console.log(res)
this.allCoverData = res
if (this.regionOptions[index].regionMap) {
this.imageUrl = baseUrl + '/api/fileRelevant/getImg?imgId=' + this.regionOptions[index].regionMap
} else {
this.imageUrl = this.defaultImg
}
if (this.allCoverData.length !== 0) {
this.currentMarkData = this.regionOptions[index]
const signPoint = this.allCoverData.find(item => item.signPoint !== null)?.signPoint
const imgInfo = signPoint ? JSON.parse(signPoint).imgInfo : null
const baseStockDataAllShelf = await this.getInitStockInfo(this.allCoverData)
const parsedSignPoints = this.allCoverData.map(item => {
const signPoint = item.signPoint ? JSON.parse(item.signPoint) : null
return {
id: item.shelfId,
name: item.shelfName,
rowType: item.rowType,
toward: item.toward,
floorName: item.floorName,
regionName: item.regionName,
shelfFloor: item.shelfFloor,
shelfShelf: item.shelfShelf,
floorId: this.regionOptions[index].floorId,
regionId: this.regionOptions[index].id,
pointInfo: signPoint ? signPoint.pointInfo[0].pointInfo : null
}
})
parsedSignPoints.forEach(parsedItem => {
const baseStockItem = baseStockDataAllShelf.find(baseItem => baseItem.id === parsedItem.id)
if (baseStockItem) {
Object.assign(parsedItem, baseStockItem)
}
})
const result = {
pointInfo: parsedSignPoints,
imgInfo: imgInfo
}
console.log('result', result)
this.$nextTick(() => {
this.$refs.previewRefs.initCanvasPreview(result, this.tabIndex)
})
} else {
this.currentMarkData = {}
setTimeout(() => {
this.prewLoading = false
}, 500)
}
} catch (error) {
console.error(error)
}
},
async getInitStockInfo(data) {
const promises = data.map(item => {
const params = {
'fondsCode': this.libcode,
'floorId': this.floorId,
'regionId': this.regionId,
'shelfId': item.shelfId
}
return FetchInitStockInfo(params)
})
const results = await Promise.all(promises)
if (!Array.isArray(this.baseStockDataAllShelf)) {
this.baseStockDataAllShelf = []
}
// id
results.forEach((result, index) => {
result.id = data[index].shelfId
})
this.baseStockDataAllShelf = this.baseStockDataAllShelf.concat(results)
return this.baseStockDataAllShelf
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
</style>

629
src/views/shelfList.vue

@ -0,0 +1,629 @@
<template>
<div class="content-main">
<Search refs="searchRefs" search-type="noList" />
<div class="box-style" style="margin: 30px 44px;">
<div class="dataScreening-header">
<h4 class="filter-header">书架总览</h4>
<div class="bookshelf-area">
<!-- <router-link :to="{ path: '/', query: {floorTabIndex: floorTabIndex }}">
{{ floorName }}
</router-link>
<span>/</span>
<router-link :to="{ path: '/regions', query: {regionTabIndex: regionTabIndex }}">
{{ regionName }}
</router-link>
<div class="double-click-btn"><span>点击左侧位置返回</span></div> -->
<router-link :to="{ path: '/regions', query: {regionTabIndex: regionTabIndex }}">
<i class="iconfont icon-huifu" />返回
</router-link>
</div>
</div>
<div class="tab-content">
<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>
</ul>
<div class="tag-info">
<!-- <p class="tag-sort">错序<i class="iconfont icon-zhuangtai2" /></p>
<p class="tag-place">错架<i class="iconfont icon-zhuangtai2" /></p> -->
<p class="tag-all">在架<i class="iconfont icon-zhuangtai2" /></p>
</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">
<!-- @dblclick="handleCellCurrent(cell,i)"
@mouseenter="showPopover(i)"
@mouseleave="hidePopover"
@touchmove="handleTouchMove(i)"
@touchstart="handleTouchStart($event, cell,i)"
@touchend="handleTouchEnd"
-->
<li
v-for="(cell,i) in booShelfGrid"
:key="i"
:class="['data-shelf-cell',{'active': isActiveColumn(i)}]"
:style="cellStyle"
@click="handleCellCurrent(cell,i)"
>
<div class="mask" />
<div class="tag-info">
<!-- <p class="tag-sort"><i class="iconfont icon-zhuangtai2" />{{ cell.errorOrderNum }}</p>
<p class="tag-place"><i class="iconfont icon-zhuangtai2" />{{ cell.errorShelfNum }}</p> -->
<p class="tag-all"><i class="iconfont icon-zhuangtai2" />{{ cell.onShelfNum }}</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>{{ itemStockData.onShelfNum }}</i></em></li>
<li><p>错架</p><em><i>{{ itemStockData.errorShelfNum }}</i></em> <em class="percentage">{{ itemStockData.errorShelfProbo }}</em></li>
<li><p>错序</p><em><i>{{ itemStockData.errorOrderNum }}</i></em><em class="percentage">{{ itemStockData.errorOrderProbo }}</em></li>
</ul>
</div>
</el-popover>
</li>
</ul>
<ul class="change-layer">
<li v-for="(item,index) in layerOptions" :key="index" :class="{ 'active': item.name === layerVal.name }" @click="changeShelfGetGrid(item)">{{ item.name }}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import { FetchShelfListByRegionId, FetchShelfGridAllByShelfIdText, FetchBookShelfDetails, FetchInitStockInfo } from '@/api/inquiryMachine'
import { positionCrud } from './mixins/index.js'
import Search from './module/search'
export default {
name: 'Index',
components: {
Search
},
mixins: [positionCrud],
data() {
return {
listLoading: false,
tabIndex: 0,
floorTabIndex: 0,
regionTabIndex: 0,
floorName: null,
floorId: null,
regionName: null,
regionId: null,
rowType: null,
bookShelfDetails: {},
booShelfGrid: [],
cellInfo: {
gridName: null,
startSortmark: null,
endSortmark: null,
cameraId: null
},
callNumVisible: false,
layerNum: 0,
rackNum: 0,
swiperActiveIndex: 0,
cellIndex: null,
columnIndex: null,
layerVal: null,
layerOptions: [],
tabListData: [],
popoverIndex: null,
popoverVisible: [],
popoverStyles: [],
activeColumns: {},
itemStockData: {},
touchStartTime: null,
touchStartPosition: null,
doubleClickTimeout: null
}
},
computed: {
cellStyle: function() {
const h = '76px'
const w = '118px'
return { width: `calc(${w} )`, height: `calc(${h})` }
},
rowStyle: function() {
const w = 118 * 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 []
}
}
},
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.floorId = dataScreenRegion.floorId
this.regionName = dataScreenRegion.regionName
this.regionId = dataScreenRegion.regionId
this.rowType = dataScreenRegion.rowType
this.initData(dataScreenRegion)
this.getInitBookShelfList(dataScreenRegion)
this.getInitStockInfo(dataScreenRegion)
}
},
mounted() {
},
methods: {
changeShelfGetGrid(val) {
this.layerVal = val
this.initData(val)
this.getInitStockInfo(val)
},
initData(data) {
// /
this.tabListData = data.rowType === 1
? data.toward === 1
? [{ name: 'A面' }]
: [{ name: 'B面' }]
: [{ name: 'A面' }, { name: 'B面' }]
this.tabIndex = this.$route.query.tabIndex ? this.$route.query.tabIndex : 0
FetchBookShelfDetails({ 'shelfId': data.id }).then(res => {
this.layerNum = res.shelfFloor
this.rackNum = res.shelfShelf
this.bookShelfDetails = res
if (this.$route.query.tabIndex) {
this.getInitShelfGridByShelfId(this.bookShelfDetails.id, this.$route.query.tabIndex + 1)
} else {
this.getInitShelfGridByShelfId(this.bookShelfDetails.id, this.bookShelfDetails.toward)
}
}).catch(() => {
})
},
getInitStockInfo(data) {
const params = {
'fondsCode': this.libcode,
'floorId': this.floorId,
'regionId': this.regionId,
'shelfId': data.id
}
FetchInitStockInfo(params).then(res => {
this.baseStockData = res
}).catch(() => {
})
},
getInitBookShelfList(data) {
console.log('data', data)
const params = { 'floorId': data.floorId, 'regionId': data.regionId }
FetchShelfListByRegionId(params).then(res => {
this.layerOptions = res.map(item => {
return {
name: item.shelfName,
id: item.shelfId,
toward: item.toward,
rowType: item.rowType
}
})
this.layerVal = data
}).catch(() => {
})
},
removeAreaPrefix(gridNames) {
return gridNames.replace(/\d*区|\d*层/g, '')
},
getInitShelfGridByShelfId(shelfId, toward) {
this.listLoading = true
// rowType 1 2
// toward 1 A 2 B
// shelfType 1 '1S'
// shelfType 2 'A1B1'
// shelfType 3 'B1A1'
// floorType 1 ''
// floorType 2 ''
FetchShelfGridAllByShelfIdText({ 'shelfId': shelfId, 'toward': toward }).then(res => {
console.log('res', 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('this.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(this.layerVal.id, index + 1)
},
handleCellCurrent(item, index) {
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.layerVal.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: '/gird' })
if (localStorage.getItem('dataScreenRegion')) {
const dataScreenRegion = JSON.parse(localStorage.getItem('dataScreenRegion'))
dataScreenRegion.rowType = this.layerVal.rowType
dataScreenRegion.toward = this.layerVal.toward
dataScreenRegion.name = this.layerVal.name
dataScreenRegion.id = this.layerVal.id
localStorage.setItem('dataScreenRegion', JSON.stringify(dataScreenRegion))
}
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
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
const params = {
'fondsCode': this.libcode,
'floorId': this.floorId,
'regionId': this.regionId,
'shelfId': this.bookShelfDetails.id,
'gridShelf': this.booShelfGrid[columnIndex].gridShelf
}
FetchInitStockInfo(params).then(res => {
this.itemStockData = res
}).catch(() => {
})
},
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)
}
})
},
handleTouchStart(event, cell, i) {
let currentPosition
if (event.touches) {
currentPosition = event.touches[0] ? event.touches[0].pageX : null
}
const currentTime = new Date().getTime()
// && Math.abs(currentPosition - this.touchStartPosition) < 50
if (this.touchStartTime && currentTime - this.touchStartTime < 300) {
console.log('1111')
//
clearTimeout(this.doubleClickTimeout)
this.cellIndex = i
this.cellInfo = {
id: cell.id,
gridName: cell.gridName,
gridRow: cell.gridRow,
gridShelf: cell.gridShelf,
shelfId: cell.shelfId,
floorName: this.floorName,
regionName: this.regionName,
rowType: this.layerVal.rowType,
toward: cell.toward
}
this.currentShelfAllGrid = this.booShelfGrid.filter(gird => gird.gridShelf === cell.gridShelf)
this.handleToGrids(this.cellInfo, this.currentShelfAllGrid)
} else {
console.log('222')
//
this.doubleClickTimeout = setTimeout(() => {
this.touchStartTime = null
this.touchStartPosition = null
}, 300)
this.touchStartTime = currentTime
this.touchStartPosition = currentPosition
this.handleTouchMove(i)
}
},
//
handleTouchMove(i) {
this.popoverIndex = i
if (!this.popoverVisible[i]) {
this.$set(this.popoverVisible, i, true)
}
const lastColumnIndexes = []
const secondLastColumnIndexes = []
for (let i = 0; i < this.booShelfGrid.length; i++) {
const columnIndex = i % this.rackNum
// 5
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 = i % this.rackNum
this.activeColumns[columnIndex] = true
console.log('columnIndex', columnIndex)
console.log(' this.libcode', this.libcode)
const params = {
'fondsCode': this.libcode,
'floorId': this.floorId,
'regionId': this.regionId,
'shelfId': this.bookShelfDetails.id,
'gridShelf': this.booShelfGrid[columnIndex].gridShelf
}
FetchInitStockInfo(params).then(res => {
this.itemStockData = res
}).catch(() => {})
},
handleTouchEnd(event) {
clearTimeout(this.doubleClickTimeout)
this.activeColumns = {}
this.popoverIndex = null
this.popoverVisible.forEach((isVisible, index) => {
if (isVisible) {
this.$set(this.popoverVisible, index, false)
}
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.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;
.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>

69
vue.config.js

@ -0,0 +1,69 @@
const path = require('path')
const resolve = dir => {
return path.join(__dirname, dir)
}
const name = '江夏区图书馆自助查询机'
module.exports = {
publicPath: process.env.NODE_ENV === 'development' ? '/' : './',
outputDir: 'dist',
assetsDir: 'static',
lintOnSave: process.env.NODE_ENV === 'development',
productionSourceMap: false,
devServer: {
port: 3000,
open: true,
overlay: {
warnings: false,
errors: true
},
// host: 'localhost',
proxy: {
'/dxhtsg': {
target: process.env.VUE_APP_BASE_API,
changeOrigin: true,
pathRewrite: {
'^/dxhtsg': 'dxhtsg'
}
},
'/api': {
target: process.env.VUE_APP_BASE_API,
changeOrigin: true,
pathRewrite: {
'^/api': 'api'
}
},
'/auth': {
target: process.env.VUE_APP_BASE_API,
changeOrigin: true,
pathRewrite: {
'^/auth': 'auth'
}
}
}
},
configureWebpack: {
name: name,
resolve: {
alias: {
'@': resolve('src')
}
},
performance: {
hints: 'warning',
// 入口起点的最大体积
maxEntrypointSize: 50000000,
// 生成文件的最大体积
maxAssetSize: 30000000
}
},
chainWebpack: config => {
config.resolve
.alias.set('_c', resolve('src/components'))
config.plugin('html')
.tap(args => {
args[0].title = '自助查询机'
return args
})
}
}
Loading…
Cancel
Save