Browse Source

first

master
xuhuajiao 7 months ago
parent
commit
6b174a9bb5
  1. 9
      .env.development
  2. 9
      .env.production
  3. 197
      .eslintrc.js
  4. 23
      .gitignore
  5. 37
      README.md
  6. 11
      babel.config.js
  7. 56
      package.json
  8. BIN
      public/favicon.ico
  9. 19
      public/index.html
  10. 4
      public/static/config.js
  11. 16
      src/App.vue
  12. 84
      src/api/inquiryMachine.js
  13. BIN
      src/assets/404_images/404.png
  14. BIN
      src/assets/404_images/404_cloud.png
  15. BIN
      src/assets/fonts/HuXiaoBoNanShenTi.otf
  16. BIN
      src/assets/fonts/YouSheBiaoTiHei.ttf
  17. BIN
      src/assets/fonts/ZhenyanGB.ttf
  18. 19
      src/assets/fonts/fonts.css
  19. 80
      src/assets/iconfont/iconfont.css
  20. 1
      src/assets/iconfont/iconfont.js
  21. 121
      src/assets/iconfont/iconfont.json
  22. 51
      src/assets/iconfont/iconfont.svg
  23. BIN
      src/assets/iconfont/iconfont.ttf
  24. BIN
      src/assets/iconfont/iconfont.woff
  25. BIN
      src/assets/iconfont/iconfont.woff2
  26. BIN
      src/assets/images/default-img.png
  27. BIN
      src/assets/images/empty.png
  28. BIN
      src/assets/images/index/320 180 down.png
  29. BIN
      src/assets/images/index/320 180 up.png
  30. BIN
      src/assets/images/index/400 620 left.png
  31. BIN
      src/assets/images/index/400 620 right.png
  32. BIN
      src/assets/images/index/760 180 down.png
  33. BIN
      src/assets/images/index/760 180 up.png
  34. BIN
      src/assets/images/index/activities.png
  35. BIN
      src/assets/images/index/bg.png
  36. BIN
      src/assets/images/index/brief.png
  37. BIN
      src/assets/images/index/digital.png
  38. BIN
      src/assets/images/index/guide.png
  39. BIN
      src/assets/images/index/logo.png
  40. BIN
      src/assets/images/index/newbook.png
  41. BIN
      src/assets/images/index/search.png
  42. BIN
      src/assets/images/index/title.png
  43. BIN
      src/assets/images/list-title.png
  44. BIN
      src/assets/images/local-bg.png
  45. BIN
      src/assets/images/top.png
  46. BIN
      src/assets/logo.png
  47. 87
      src/assets/styles/index.scss
  48. 209
      src/assets/styles/quillEditor.css
  49. 80
      src/assets/styles/style.scss
  50. 147
      src/common/flexible.js
  51. 61
      src/common/scrollMixins.js
  52. 41
      src/main.js
  53. 98
      src/router/index.js
  54. 48
      src/store/column.js
  55. 13
      src/store/index.js
  56. 92
      src/utils/index.js
  57. 55
      src/utils/request.js
  58. 26
      src/utils/resizeMixins.js
  59. 90
      src/views/columnLayout.vue
  60. 173
      src/views/columnListMix.vue
  61. 93
      src/views/columnListMixDetail.vue
  62. 64
      src/views/imgTxtDetail.vue
  63. 293
      src/views/index.vue
  64. 174
      src/views/menu.vue
  65. 228
      src/views/module/bookDetails.vue
  66. 434
      src/views/module/waterfall.vue
  67. 282
      src/views/newBookRecommend.vue
  68. 158
      src/views/old.vue
  69. 342
      src/views/waterfallFlow.vue
  70. 160
      src/views/waterfallFlow2.vue
  71. 61
      vue.config.js

9
.env.development

@ -0,0 +1,9 @@
ENV = 'development'
# 接口地址
# 许镇-本地服地址
VUE_APP_BASE_API = 'http://192.168.99.72:14000'
# 是否启用 babel-plugin-dynamic-import-node插件
VUE_CLI_BABEL_TRANSPILE_MODULES = true

9
.env.production

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

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

37
README.md

@ -0,0 +1,37 @@
自助查询机
```bash
# install dependency
npm install
# develop
npm run serve
# 构建生产环境
npm run build
```
```bash
## 项目结构
├── src # 源代码
│ ├── api # 所有请求
│ ├── assets # 主题 字体等静态资源
│ ├── router # 路由
│ ├── views # views 所有页面
│ │ ├── module
│ │ │ ├── bookDetails.vue # 书籍详情
│ │ ├── activityDetail # 活动详情
│ │ ├── activityFeed # 活动资讯list
│ │ ├── digitalResource # 数字资源
│ │ ├── libraryIntroduction # 图书馆简介
│ │ ├── newBookRecommend # 新书推荐
│ ├── 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'
]
}

56
package.json

@ -0,0 +1,56 @@
{
"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",
"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.5:8080' // 配置服务器地址
}

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>

84
src/api/inquiryMachine.js

@ -0,0 +1,84 @@
import request from '@/utils/request'
import qs from 'qs'
// 查询机栏目管理详情
export function FetchQueryTopicDetails(params) {
return request({
url: '/api/queryMachine/getQueryMenuDetails',
method: 'get',
params: params
})
}
// 根据栏目id获取内容
export function FetchInitQueryTopicContext(params) {
return request({
url: '/api/queryMachine/initQueryTopicContext',
method: 'get',
params: params
})
}
// 新书推荐
export function FetchNewBook(params) {
return request({
url: '/api/screenSetting/getNewBook' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 通过isbn获取图书详细信息
export function FetchMarcByISBN(params) {
return request({
url: '/api/screenSetting/marcByISBN' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 数字资源
export function initNumberResoures(params) {
return request({
url: '/dxhtsg/initNumberResoures',
method: 'get',
params: params
})
}
// 新书推荐
export function FetchNewBookRecommend(params) {
return request({
url: '/dxhtsg/newBookRecommend',
method: 'get',
params: params
})
}
// 通过isbn获取图书封面
export function FetchCoverByISBN(params) {
return request({
url: '/dxhtsg/getCoverByISBN',
method: 'get',
params: params
// responseType: 'blob'
})
}
// 图书详情
export function getBookDetailsByISBN(params) {
return request({
url: '/api/bookBasice/getBookBasiceByISBN',
method: 'get',
params: params
})
}
export default {
FetchQueryTopicDetails,
FetchInitQueryTopicContext,
FetchNewBook,
FetchMarcByISBN,
FetchNewBookRecommend,
FetchCoverByISBN,
initNumberResoures,
getBookDetailsByISBN
}

BIN
src/assets/404_images/404.png

After

Width: 1014  |  Height: 556  |  Size: 96 KiB

BIN
src/assets/404_images/404_cloud.png

After

Width: 152  |  Height: 138  |  Size: 4.7 KiB

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

80
src/assets/iconfont/iconfont.css

@ -0,0 +1,80 @@
@font-face {
font-family: "iconfont"; /* Project id 3646564 */
src: url('iconfont.woff2?t=1664428017869') format('woff2'),
url('iconfont.woff?t=1664428017869') format('woff'),
url('iconfont.ttf?t=1664428017869') format('truetype'),
url('iconfont.svg?t=1664428017869#iconfont') format('svg');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-weizhi:before {
content: "\e639";
}
.icon-youhua:before {
content: "\e635";
}
.icon-zuohua:before {
content: "\e638";
}
.icon-a-no21:before {
content: "\e636";
}
.icon-a-21:before {
content: "\e637";
}
.icon-jianpan:before {
content: "\e634";
}
.icon-guanbi:before {
content: "\e630";
}
.icon-zuo:before {
content: "\e631";
}
.icon-benjiatushu:before {
content: "\e632";
}
.icon-remen:before {
content: "\e633";
}
.icon-you:before {
content: "\e62f";
}
.icon-tongzhi:before {
content: "\e628";
}
.icon-a-no3:before {
content: "\e629";
}
.icon-a-no1:before {
content: "\e62b";
}
.icon-a-3:before {
content: "\e62d";
}
.icon-a-1:before {
content: "\e62e";
}

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

121
src/assets/iconfont/iconfont.json

@ -0,0 +1,121 @@
{
"id": "3646564",
"name": "东西湖图书馆",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "32102019",
"name": "位置",
"font_class": "weizhi",
"unicode": "e639",
"unicode_decimal": 58937
},
{
"icon_id": "31966175",
"name": "右滑",
"font_class": "youhua",
"unicode": "e635",
"unicode_decimal": 58933
},
{
"icon_id": "31966176",
"name": "左滑",
"font_class": "zuohua",
"unicode": "e638",
"unicode_decimal": 58936
},
{
"icon_id": "31879702",
"name": "no.2",
"font_class": "a-no21",
"unicode": "e636",
"unicode_decimal": 58934
},
{
"icon_id": "31879708",
"name": "2",
"font_class": "a-21",
"unicode": "e637",
"unicode_decimal": 58935
},
{
"icon_id": "31879044",
"name": "键盘",
"font_class": "jianpan",
"unicode": "e634",
"unicode_decimal": 58932
},
{
"icon_id": "31859389",
"name": "关闭",
"font_class": "guanbi",
"unicode": "e630",
"unicode_decimal": 58928
},
{
"icon_id": "31859390",
"name": "左",
"font_class": "zuo",
"unicode": "e631",
"unicode_decimal": 58929
},
{
"icon_id": "31859393",
"name": "本架图书",
"font_class": "benjiatushu",
"unicode": "e632",
"unicode_decimal": 58930
},
{
"icon_id": "31859394",
"name": "热门",
"font_class": "remen",
"unicode": "e633",
"unicode_decimal": 58931
},
{
"icon_id": "31859388",
"name": "右",
"font_class": "you",
"unicode": "e62f",
"unicode_decimal": 58927
},
{
"icon_id": "31804863",
"name": "通知",
"font_class": "tongzhi",
"unicode": "e628",
"unicode_decimal": 58920
},
{
"icon_id": "31804864",
"name": "no.3",
"font_class": "a-no3",
"unicode": "e629",
"unicode_decimal": 58921
},
{
"icon_id": "31804866",
"name": "no.1",
"font_class": "a-no1",
"unicode": "e62b",
"unicode_decimal": 58923
},
{
"icon_id": "31804868",
"name": "3",
"font_class": "a-3",
"unicode": "e62d",
"unicode_decimal": 58925
},
{
"icon_id": "31804869",
"name": "1",
"font_class": "a-1",
"unicode": "e62e",
"unicode_decimal": 58926
}
]
}

51
src/assets/iconfont/iconfont.svg
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/default-img.png

After

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

BIN
src/assets/images/empty.png

After

Width: 200  |  Height: 200  |  Size: 6.0 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/top.png

After

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

BIN
src/assets/logo.png

After

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

87
src/assets/styles/index.scss

@ -0,0 +1,87 @@
#screen-container {
width: 100%;
height: 100vh;
padding: 0 0.175rem;
font-size: 0.25rem;
line-height: 0.35rem;
color: #fff;
background-color: #010326;
overflow: hidden;
}
.content-main{
display: flex;
justify-content: space-between;
height: 880px;
background: #F4F4F4;
overflow: hidden;
}
.introduction-main {
padding: 30px 40px;
.nav-menu {
background: #ffffff;
width: 300px;
ul {
margin-top: 30px;
li {
height: 90px;
font-size: 30px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
line-height: 90px;
text-align: center;
margin-bottom: 20px;
&:hover {
background: #e4e4e4;
}
&.is-active {
background: linear-gradient(
318deg,
#38b8d9 0%,
#5394f1 45%,
#a0a9ef 100%
);
color: #ffffff;
}
}
}
}
.introduction-info {
margin-left: 30px;
background: #ffffff;
width: 1510px;
padding: 20px;
overflow-y: auto;
.info-title {
height: 100px;
text-align: center;
font-size: 38px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #333333;
line-height: 100px;
}
.info-img {
background: url("~@/assets/images/top.png") no-repeat center -5px;
}
}
}
.activity-list li {
height: 90px;
margin: 0 40px;
border-bottom: 1px solid #d9d9d9;
font-size: 30px;
line-height: 90px;
padding: 0 20px;
span.date {
float: right;
}
.activity-title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: calc(100% - 282px);
display: inline-block;
}
}

209
src/assets/styles/quillEditor.css

@ -0,0 +1,209 @@
.ql-align-center{
text-align: center;
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
content: "黑体";
font-family: "SimHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
content: "微软雅黑";
font-family: "Microsoft YaHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
content: "楷体";
font-family: "KaiTi";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
content: "仿宋";
font-family: "FangSong";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
content: "Arial";
font-family: "Arial";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before {
content: "Times New Roman";
font-family: "Times New Roman";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before {
content: "sans-serif";
font-family: "sans-serif";
}
.ql-font-SimSun {
font-family: "SimSun";
}
.ql-font-SimHei {
font-family: "SimHei";
}
.ql-font-Microsoft-YaHei {
font-family: "Microsoft YaHei";
}
.ql-font-KaiTi {
font-family: "KaiTi";
}
.ql-font-FangSong {
font-family: "FangSong";
}
.ql-font-Arial {
font-family: "Arial";
}
.ql-font-Times-New-Roman {
font-family: "Times New Roman";
}
.ql-font-sans-serif {
font-family: "sans-serif";
}
/* 字号设置 */
/* 默认字号 */
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: "12px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="12px"]::before{
content: "12px";
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before{
content: "14px";
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before{
content: "16px";
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before{
content: "18px";
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before{
content: "20px";
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="22px"]::before{
content: "22px";
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="26px"]::before{
content: "26px";
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="28px"]::before{
content: "28px";
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="30px"]::before{
content: "30px";
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="12px"]::before {
content: "12px";
font-size: 12px;
}
.ql-size-12px {
font-size: 12px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
content: "14px";
font-size: 14px;
}
.ql-size-14px {
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
content: "16px";
font-size: 16px;
}
.ql-size-16px {
font-size: 16px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
content: "18px";
font-size: 18px;
}
.ql-size-18px {
font-size: 18px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
content: "20px";
font-size: 20px;
}
.ql-size-20px {
font-size: 20px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="22px"]::before {
content: "22px";
font-size: 22px;
}
.ql-size-22px {
font-size: 22px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="26px"]::before {
content: "26px";
font-size: 26px;
}
.ql-size-26px {
font-size: 26px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="28px"]::before {
content: "28px";
font-size: 28px;
}
.ql-size-28px {
font-size: 28px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="30px"]::before {
content: "30px";
font-size: 30px;
}
.ql-size-30px {
font-size: 30px;
}

80
src/assets/styles/style.scss

@ -0,0 +1,80 @@
@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;
}

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

41
src/main.js

@ -0,0 +1,41 @@
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 './assets/styles/quillEditor.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')

98
src/router/index.js

@ -0,0 +1,98 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import ColumnLayout from '@/views/columnLayout'
import ColumnListMix from '@/views/columnListMix'
import ColumnListMixDetail from '@/views/columnListMixDetail'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'index',
component: () => import('@/views/index.vue'),
meta: {
title: '首页'
}
},
{
path: '/NewBookRecommend',
name: 'NewBookRecommend',
component: () => import('@/views/newBookRecommend.vue'),
meta: {
title: '新书推荐'
}
},
// {
// path: '/DigitalResource',
// name: 'DigitalResource',
// component: () => import('@/views/digitalResource.vue'),
// meta: {
// title: '数字资源'
// }
// },
// {
// path: '/LibraryIntroduction',
// name: 'LibraryIntroduction',
// component: () => import('@/views/libraryIntroduction.vue'),
// meta: {
// title: '图书馆简介'
// }
// },
// {
// path: '/ActivityFeed',
// name: 'ActivityFeed',
// component: () => import('@/views/activityFeed.vue'),
// meta: {
// title: '活动咨询'
// }
// },
// {
// path: '/ActivityDetail',
// name: 'ActivityDetail',
// component: () => import('@/views/activityDetail.vue'),
// meta: {
// title: '活动详情'
// }
// },
{
path: '/menu',
name: 'menu',
component: () => import('@/views/menu.vue')
},
{
path: '/imgTxtDetail',
name: 'imgTxtDetail',
component: () => import('@/views/imgTxtDetail.vue')
},
{
path: '/column',
name: 'ColumnLayout',
component: ColumnLayout,
redirect: '/column/list',
children: [
{
path: 'list',
name: 'ColumnListMix',
component: ColumnListMix
},
{
path: 'detail',
name: 'ColumnListMixDetail',
component: ColumnListMixDetail
}
]
},
{
path: '/waterfallFlow',
name: 'waterfallFlow',
component: () => import('@/views/waterfallFlow.vue')
}
]
const router = new VueRouter({
mode: 'hash',
routes
})
export default router

48
src/store/column.js

@ -0,0 +1,48 @@
// 初始状态
const state = {
tabList: [],
libcode: '',
selectMenuIndex: 0
}
// mutations
const mutations = {
// 设置专栏数据
setColumnData(state, payload) {
state.tabList = payload.tabList || []
state.libcode = payload.libcode || ''
state.selectMenuIndex = payload.selectMenuIndex || 0
},
// 更新选中的菜单索引
setSelectMenuIndex(state, index) {
state.selectMenuIndex = index
}
}
// actions
const actions = {
updateColumnData({ commit }, data) {
commit('setColumnData', data)
},
updateSelectIndex({ commit }, index) {
commit('setSelectMenuIndex', index)
}
}
// getters
const getters = {
getTabList: state => state.tabList,
getLibcode: state => state.libcode,
getSelectIndex: state => state.selectMenuIndex
}
export default {
namespaced: true,
state,
mutations,
actions,
getters
}

13
src/store/index.js

@ -0,0 +1,13 @@
import Vue from 'vue'
import Vuex from 'vuex'
import column from './column'
Vue.use(Vuex)
// 创建并导出store实例
export default new Vuex.Store({
modules: {
column // 注册column模块
}
})

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 : process.env.VUE_APP_BASE_API, // 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)
}
}

90
src/views/columnLayout.vue

@ -0,0 +1,90 @@
<template>
<div class="content-main introduction-main">
<div class="nav-menu">
<ul>
<li
v-for="(item,index) in tabList"
:key="index"
:class="{ 'is-active': selectMenuIndex === index }"
@click="selectMenuChange(item, index)"
>
{{ item.title }}
</li>
</ul>
</div>
<div class="introduction-info">
<router-view />
</div>
</div>
</template>
<script>
import { FetchQueryTopicDetails } from '@/api/inquiryMachine'
export default {
name: 'ColumnLayout',
data() {
return {
libcode: '',
tabList: [],
selectMenuIndex: 0
}
},
created() {
const savedIndex = localStorage.getItem('selectIndex')
if (savedIndex !== null) {
this.selectMenuIndex = parseInt(savedIndex)
}
this.getQueryTopicDetails()
},
methods: {
getQueryTopicDetails() {
const params = { 'id': this.$route.query.menuId }
FetchQueryTopicDetails(params).then(res => {
console.log('接口返回数据:', res)
if (res && res.queryTopicList && res.queryTopicList.length > 0) {
this.tabList = res.queryTopicList
this.libcode = res.queryMenu.libcode
// this.$store.commit
this.$store.commit('column/setColumnData', {
tabList: this.tabList,
libcode: this.libcode,
selectMenuIndex: this.selectMenuIndex
})
console.log('Vuex设置成功:', this.$store.state.column.tabList)
} else {
console.error('接口返回的queryTopicList为空或格式错误')
}
}).catch(err => {
console.error('接口请求失败:', err)
this.$message.error('接口错误')
})
},
selectMenuChange(item, index) {
this.selectMenuIndex = index
localStorage.setItem('selectIndex', index)
this.$store.commit('column/setSelectMenuIndex', index)
const targetRoute = {
name: 'ColumnListMix',
query: this.$route.query
}
const currentRoute = this.$router.currentRoute
if (currentRoute.name === targetRoute.name &&
JSON.stringify(currentRoute.query) === JSON.stringify(targetRoute.query)) {
if (this.$children.length > 0) {
this.$children[0].getInitQueryTopicContext(item)
}
return
}
this.$router.push(targetRoute).then(() => {
if (this.$children.length > 0) {
this.$children[0].getInitQueryTopicContext(item)
}
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
</style>

173
src/views/columnListMix.vue

@ -0,0 +1,173 @@
<template>
<div style="height:calc(100%);">
<div v-if="detailData && tabList[selectMenuIndex].type ===1" class="new-detial-info">
<h2 class="detail-title">{{ detailData.overTitle }}</h2>
<div class="detail-date">发布时间{{ detailData.create_time }}</div>
<div class="detail-content" v-html="detailData.introHtml" />
</div>
<ul v-else-if="infoList.length !== 0 && (tabList[selectMenuIndex].type ===2 || tabList[selectMenuIndex]===3)" class="activity-list">
<li v-for="(item,index) in infoList" :key="index" @click="toDetail(item)">
<div class="activity-title">{{ item.overTitle?item.overTitle:'' }}</div>
<span class="date">{{ item.create_time }}</span>
</li>
</ul>
<Waterfall
v-else-if="infoList.length !== 0 && tabList[selectMenuIndex].type ===4"
:list="infoList"
:columns="columns"
:loading="loading"
:placeholder="placeholderImg"
:total-items="totalItems"
@scroll-end="loadMoreData"
/>
<div v-else style="height:calc(100%);display: flex; flex-direction: column; justify-content:center ; align-items: center;">
<img style="display: block;" src="~@/assets/images/empty.png">
<p style="font-size: 30px;">暂无相关数据</p>
</div>
</div>
</template>
<script>
import Waterfall from './module/waterfall.vue'
import { mapState } from 'vuex'
import { FetchInitQueryTopicContext } from '@/api/inquiryMachine'
export default {
name: 'ColumnListMix',
components: {
Waterfall
},
data() {
return {
detailData: {},
infoList: [],
columns: 2,
dataList: [],
loading: false,
currentPage: 0,
pageSize: 10,
totalItems: 0,
placeholderImg: require('@/assets/images/default-img.png')
}
},
computed: {
...mapState('column', ['tabList', 'libcode', 'selectMenuIndex'])
},
watch: {
$route: {
handler() {
console.log('当前Vuex中的tabList:', this.$store.state.column.tabList)
if (this.tabList.length > 0) {
this.getInitQueryTopicContext(this.tabList[this.selectMenuIndex])
} else {
const timer = setTimeout(() => {
if (this.tabList.length > 0) {
this.getInitQueryTopicContext(this.tabList[this.selectMenuIndex])
} else {
console.log('重试后tabList仍为空')
}
clearTimeout(timer)
}, 500)
}
},
immediate: true
}
},
methods: {
getInitQueryTopicContext(result) {
console.log(result.type)
const params = {
'page': this.currentPage,
'size': this.pageSize,
'libcode': this.libcode,
'topicId': result.id,
'topicType': result.type
}
FetchInitQueryTopicContext(params).then(res => {
if (res.totalElements) {
this.totalItems = res.totalElements
}
const linkSrc = process.env.NODE_ENV === 'production'
? window.g.ApiUrl
: process.env.VUE_APP_BASE_API
if (res && res.content.length > 0) {
if (result.type === 4) {
const formattedData = res.content.map(item => ({
id: item.id || `item-${Date.now()}-${Math.random()}`,
type: 'image',
title: item.overTitle || '无标题',
image: item.imgUrl ? `${linkSrc}/api/fileRelevant/getImg?imgType=2&imgId=${item.imgUrl}` : this.placeholderImg,
linkUrl: item.linkUrl || '#'
}))
this.infoList = [...this.infoList, ...formattedData]
} else if (result.type === 1) {
this.detailData = res.content[0]
} else {
this.infoList = res.content
}
} else {
this.infoList = []
}
}).catch(() => {
this.$message.error('接口错误')
})
},
loadMoreData() {
if (this.dataList.length >= this.totalItems) {
return
}
if (this.loading) return
this.currentPage++
this.getInitQueryTopicContext(this.tabList[this.selectMenuIndex])
},
isAllDataLoaded() {
if (this.totalItems > 0) {
return this.dataList.length >= this.totalItems
}
return this.currentPage > 0 && this.dataList.length % this.pageSize !== 0
},
toDetail(item) {
// itemId: item.id
this.$router.push({
name: 'ColumnListMixDetail',
query: {
...this.$route.query
}
})
localStorage.setItem('columnListMixDetail', JSON.stringify(item))
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.detail-title{
font-size: 32px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #333333;
line-height: 100px;
text-align: center;
}
.detail-date{
text-align: center;
margin-bottom: 20px;
}
.detail-content{
::v-deep p{
span{
display: block !important;
text-indent:2em !important;
}
img{
display: block;
margin: 10px auto;
}
}
}
</style>

93
src/views/columnListMixDetail.vue

@ -0,0 +1,93 @@
<template>
<div class="detail-content">
<div v-if="detailData" class="detail-info">
<h2 class="detail-title">{{ detailData.overTitle }}</h2>
<div class="detail-date">发布时间{{ detailData.create_time }}</div>
<div class="detail-content" v-html="detailData.introHtml" />
</div>
<div v-else-if="loading">
<p>加载中...</p>
</div>
<div v-else>
<p>加载详情失败</p>
<button @click="reloadDetail">重新加载</button>
</div>
</div>
</template>
<script>
export default {
name: 'ColumnListMixDetail',
data() {
return {
detailData: null,
loading: true
}
},
created() {
this.loadDetailData()
},
methods: {
loadDetailData() {
this.loading = true
const storedData = localStorage.getItem('columnListMixDetail')
if (storedData) {
this.detailData = JSON.parse(storedData)
this.loading = false
return
}
// 2:
// const itemId = this.$route.query.itemId
// if (itemId) {
// FetchTopicDetail({ id: itemId }).then(res => {
// this.detailData = res.data
// }).catch(err => {
// console.error('', err)
// }).finally(() => {
// this.loading = false
// })
// } else {
// this.loading = false
// }
},
//
reloadDetail() {
this.loadDetailData()
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.detail-info{
.detail-title{
font-size: 32px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
font-weight: 400;
color: #333333;
line-height: 100px;
text-align: center;
}
.detail-date{
text-align: center;
margin-bottom: 20px;
}
.detail-content{
::v-deep p{
span{
display: block !important;
text-indent:2em !important;
}
img{
display: block;
margin: 10px auto;
}
}
}
}
</style>

64
src/views/imgTxtDetail.vue

@ -0,0 +1,64 @@
<template>
<div class="content-main introduction-main">
<div class="new-detial-info" v-html="rawHtml" />
</div>
</template>
<script>
import { FetchQueryTopicDetails, FetchInitQueryTopicContext } from '@/api/inquiryMachine'
export default {
name: 'ImgTxtDetail',
data() {
return {
rawHtml: ''
}
},
created() {
this.getQueryTopicDetails()
},
methods: {
getQueryTopicDetails() {
const params = {
'id': this.$route.query.menuId
}
FetchQueryTopicDetails(params).then(res => {
console.log(res)
if (res && res.queryTopicList.length > 0) {
this.getInitQueryTopicContext(res)
}
}).catch(() => {
this.$message.error('接口错误')
})
},
getInitQueryTopicContext(result) {
const params = {
'page': 0,
'size': 10,
'libcode': result.queryMenu.libcode,
'topicId': result.queryTopicList[0].id,
'topicType': result.queryTopicList[0].type
}
FetchInitQueryTopicContext(params).then(res => {
console.log(res)
if (res && res.content.length > 0) {
this.rawHtml = res.content[0].introHtml
} else {
this.rawHtml = '暂无数据'
}
}).catch(() => {
this.$message.error('接口错误')
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.new-detial-info {
background: #ffffff;
width: 100%;
padding: 20px;
overflow-y: auto;
}
</style>

293
src/views/index.vue

@ -0,0 +1,293 @@
<template>
<div v-if="pageError" class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">请检查您输入的网址是否正确</div>
</div>
</div>
</div>
</template>
<script>
import { FetchQueryTopicDetails } from '@/api/inquiryMachine'
export default {
name: 'Home',
data() {
return {
pageError: false
}
},
computed: {
message() {
return '暂未查到相关页面......'
}
},
created() {
console.log('this.router.path', this.$route.query)
this.getQueryTopicDetails()
},
methods: {
getQueryTopicDetails() {
const params = {
'id': this.$route.query.menuId
}
FetchQueryTopicDetails(params).then(res => {
this.pageError = false
console.log(res)
// res.queryTopicList[0].type = 1 //
// res.queryTopicList[0].type = 2 // -
// res.queryTopicList[0].type = 3 // -
// res.queryTopicList[0].type = 4 // -
// res.queryMenu.type = 1 //
// res.queryMenu.type = 2 && res.queryTopicList.length === 1 && res.queryTopicList[0].type==1 // imgTxtDetail
// res.queryMenu.type = 3 && res.queryTopicList.length >0 // columnListMix
// res.queryMenu.type = 4 // newBookRecommend
localStorage.setItem('menuId', res.queryMenu.id)
localStorage.setItem('menuInfo', JSON.stringify(res))
//
if (res.queryMenu.type === 1) {
//
this.$router.push({
path: '/menu',
query: { 'menuId': res.queryMenu.id }
})
} else if (res.queryMenu.type === 2 && res.queryTopicList.length === 1 && res.queryTopicList[0].type === 1) {
//
// , 'topicId': res.queryTopicList[0].id
this.$router.push({
path: '/imgTxtDetail',
query: { 'menuId': res.queryMenu.id }
})
} else if (res.queryMenu.type === 3 && res.queryTopicList.length > 1) {
// columnListMix
this.$router.push({
path: '/column',
query: { 'menuId': res.queryMenu.id }
})
} else if (res.queryMenu.type === 3 && res.queryTopicList.length === 1 && res.queryTopicList[0].type === 4) {
// waterfallFlow
this.$router.push({
path: '/waterfallFlow',
query: { 'menuId': res.queryMenu.id }
})
} else if (res.queryMenu.type === 4) {
// newBookRecommend
this.$router.push({
path: '/newBookRecommend',
query: { 'menuId': res.queryMenu.id, 'libcode': res.queryMenu.libcode }
})
}
}).catch(() => {
// this.$message.error('')
this.pageError = true
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

174
src/views/menu.vue

@ -0,0 +1,174 @@
<template>
<!-- 活动咨询 -->
<div class="content-main introduction-main">
<div class="nav-menu">
<ul>
<li
v-for="(item,index) in tabList"
:key="index"
:class="{ 'is-active': selectMenuIndex === index }"
@click="selectMenuChange(item, index)"
>
{{ item.title }}
</li>
</ul>
</div>
<div class="introduction-info">
<ul v-if="rightColumnTab.length !== 0" class="menu-tab-list">
<li
v-for="(item,index) in rightColumnTab"
:key="item.id"
:class="{ 'active': columeIndex === index }"
@click="handleColumnClick(item, index)"
>
{{ item.title }}
</li>
</ul>
<div style="height: calc(100% - 60px);">
<ul v-if="infoList.length !== 0" class="activity-list">
<li v-for="item in infoList" :key="item.id" @click="toDetail(item)">
<div class="activity-title">{{ item.overTitle?item.overTitle:'' }}</div>
<span class="date">{{ item.create_time }}</span>
</li>
</ul>
<div v-else style="height:calc(100%);display: flex; flex-direction: column; justify-content:center ; align-items: center;">
<img style="display: block;" src="~@/assets/images/empty.png">
<p style="font-size: 30px;">暂无相关数据</p>
</div>
</div>
</div>
</div>
</template>
<script>
import { FetchQueryTopicDetails, FetchInitQueryTopicContext } from '@/api/inquiryMachine'
export default {
name: 'ActivityFeed',
data() {
return {
libcode: '',
tabList: [],
selectMenuIndex: 0,
rightColumnTab: [],
columeIndex: 0,
infoList: []
}
},
created() {
this.getQueryTopicDetails()
},
methods: {
getQueryTopicDetails() {
const params = { 'id': this.$route.query.menuId }
FetchQueryTopicDetails(params).then(res => {
console.log('接口返回数据:', res)
if (res && res.queryMenu) {
this.tabList = res.queryMenu.children
this.libcode = res.queryMenu.libcode
this.rightColumnTab = []
this.tabList.forEach(childMenu => {
if (childMenu.queryMenuTopics && childMenu.queryMenuTopics.length > 0) {
childMenu.queryMenuTopics.forEach(menuTopic => {
if (menuTopic.queryTopic) {
this.rightColumnTab.push(menuTopic.queryTopic)
}
})
}
})
if (this.rightColumnTab.length > 0) {
this.columeIndex = 0 //
const defaultTopic = this.rightColumnTab[0] // queryTopic
this.getInitQueryTopicContext(defaultTopic) // topic
}
} else {
this.tabList = []
this.rightColumnTab = []
this.infoList = []
}
}).catch(err => {
console.error('接口请求失败:', err)
this.$message.error('接口错误')
this.rightColumnTab = []
this.infoList = []
})
},
getInitQueryTopicContext(currentTopic) {
const params = {
'page': 0,
'size': 10,
'libcode': this.libcode,
'topicId': currentTopic.id, // queryTopicidresult.id
'topicType': currentTopic.type // queryTopictyperesult.type
}
FetchInitQueryTopicContext(params).then(res => {
if (res && res.content.length > 0) {
this.infoList = res.content
} else {
this.infoList = [] //
}
}).catch(() => {
this.$message.error('接口错误')
this.infoList = []
})
},
handleColumnClick(currentTopic, index) {
this.columeIndex = index //
this.getInitQueryTopicContext(currentTopic) // topic
},
selectMenuChange(childMenu, index) {
this.selectMenuIndex = index
this.rightColumnTab = []
if (childMenu.queryMenuTopics && childMenu.queryMenuTopics.length > 0) {
childMenu.queryMenuTopics.forEach(menuTopic => {
if (menuTopic.queryTopic) {
this.rightColumnTab.push(menuTopic.queryTopic)
}
})
if (this.rightColumnTab.length > 0) {
this.columeIndex = 0
this.getInitQueryTopicContext(this.rightColumnTab[0])
} else {
this.infoList = []
}
} else {
this.rightColumnTab = []
this.infoList = []
}
},
toDetail(item) {
this.$router.push('/ActivityDetail')
localStorage.setItem('selectIndex', JSON.stringify(this.selectMenuIndex))
localStorage.setItem('activityDetail', JSON.stringify(item))
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
.menu-tab-list{
display: flex;
justify-content: flex-start;
font-size: 26px;
// padding: 10px 0;
border-bottom: 2px solid #D9D9D9;
li{
padding: 10px 20px;
margin-right: 20px;
&.active{
position: relative;
color:#5394F1;
&::before{
position: absolute;
bottom: -2px;
left: 0;
content: '';
width: 100%;
height: 2px;
background-color: #5394F1;
}
}
}
}
</style>

228
src/views/module/bookDetails.vue

@ -0,0 +1,228 @@
<template>
<div>
<el-dialog
title="图书详情"
:visible.sync="dialogVisible"
width="60%"
>
<!-- :before-close="handleClose" -->
<div class="dialog-setting">
<div class="book-details-box">
<div class="book-details">
<div class="book-cover">
<img :src="bookData.imgPath ? bookData.imgPath : ''" :onerror="defaultImg">
</div>
<div class="book-desc">
<h3>{{ bookData.title }}</h3>
<p class="book-from">作者{{ bookData.author }}</p>
<p v-if="bookData.publisher" class="book-from">出版社{{ bookData.publisher }}</p>
<p class="book-brief"> 简介{{ bookData.summary }}</p>
</div>
</div>
<div class="book-local">
<!-- <span v-if="bookData.bookPlace">所在位置{{ bookData.bookPlace }}</span> -->
<!-- 待定热门图书/新书推荐跳转到 场馆导航操作 -->
<!-- <span v-else>所在位置</span> -->
<div class="local-title">
所在位置
</div>
<ul class="local-list">
<template v-if="!bookData.places || bookData.places.length === 0">
<li class="local-none">
<span>当前图书暂未查到位置信息</span>
</li>
</template>
<template v-else>
<li
v-for="(item, index) in bookData.places"
:key="index"
class="local-list-info"
@click="toBookNav(index)"
>
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-weizhi" />
</svg>
<span>{{ item.shelfName }}</span>
</li>
</template>
</ul>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'BookDetails',
data() {
return {
dialogVisible: false,
bookData: {},
defaultImg: 'this.src="' + require('@/assets/images/default-img.png') + '"'
}
},
created() {
},
methods: {
toBookNav(index) {
const linkSrc = process.env.VUE_APP_BASE_API
window.location.href = linkSrc + '/anchoring/goNavigation.do?libcode=1201&barcode=' + this.bookData.places[index].barcode
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .el-dialog{
position: fixed;
top: 50%;
left: 50%;
margin-top: 0 !important;
transform: translate(-50%,-50%);
border-radius: 24px;
.el-dialog__body{
padding: 0 40px 40px 40px ;
z-index: 999;
}
}
::v-deep .el-dialog__header{
text-align: center;
padding-top:0 ;
padding-bottom:4px ;
.el-dialog__title{
display: inline-block;
font-family: "ZhenyanGB";
font-size: 40px;
width: 494px;
height: 76px;
line-height: 76px;
background: url('~@/assets/images/list-title.png') no-repeat 0 0;
}
}
::v-deep .el-dialog__headerbtn{
background-color: #3F92F6;
width: 40px;
height: 40px;
border-radius: 50%;
.el-dialog__close{
color: #fff;
font-size: 30px;
}
}
.book-details-box{
.book-details{
display: flex;
justify-content: space-between;
padding: 30px 0;
.book-cover{
width: 318px;
height: 382px;
margin-right: 40px;
img{
width: 100%;
height: 100%;
}
}
.book-desc{
flex: 1;
color: #333;
overflow: hidden;
h3{
font-size: 40px;
font-weight: normal;
}
.book-from{
width: 100%;
font-size: 30px;
}
h3,.book-from{
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
margin-bottom: 20px;
}
.book-brief{
font-size: 24px;
margin-bottom: 20px;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 4;
line-clamp: 4;
-webkit-box-orient: vertical;
}
}
}
// .book-local{
// border: 1px solid #3F92F6;
// background: #D0E0F9;
// border-radius: 52px;
// height: 64px;
// line-height: 64px;
// text-align: center;
// font-size: 30px;
// color: #333;
// overflow: hidden;
// white-space: nowrap;
// text-overflow: ellipsis;
// }
.book-local {
height: 343px;
color: #333;
display: flex;
// border-radius: 52px;
// line-height: 64px;
// text-align: center;
// font-size: 30px;
// overflow: hidden;
// white-space: nowrap;
// text-overflow: ellipsis;
.local-title {
width: 83px;
height: 100%;
background: url("~@/assets/images/local-bg.png") no-repeat;
writing-mode: vertical-rl;
text-align: center;
line-height: 83px;
font-size: 30px;
color: #fff;
}
.local-list {
flex: 1;
border: 1px solid #3f92f6;
border-left: none;
background: #d0e0f9;
overflow: hidden;
overflow-y: auto;
position: relative;
font-size: 26px;
.local-list-info {
height: 56px;
line-height: 56px;
background-color: #fff;
// margin-right: 8px;
margin-bottom: 15px;
padding-left: 13px;
span {
margin-left: 19px;
}
}
.local-none {
width: 100%;
text-align: center;
position: absolute;
// left: 50%;
top: 50%;
transform: translateY(-50%);
}
}
}
}
</style>

434
src/views/module/waterfall.vue

@ -0,0 +1,434 @@
<template>
<!-- 参考 https://blog.csdn.net/weixin_44198965/article/details/147831036 -->
<div class="waterfall-container">
<!-- 列容器 -->
<div ref="columnWrapper" class="waterfall-column-wrapper">
<!-- 瀑布流项 -->
<div
v-for="(item, index) in renderedList"
:key="item.id || index"
v-lazy-load="item.image || item.cover"
class="waterfall-item"
>
<!-- 图片类型 -->
<div v-if="item.type === 'image'" class="item-content" @click="jump(item.linkUrl)">
<img
:src="placeholder"
:alt="item.title || '图片'"
class="item-image"
>
<div v-if="item.title" class="item-title">{{ item.title }}</div>
</div>
<!-- 视频类型 -->
<div v-else-if="item.type === 'video'" class="item-content">
<video
:poster="placeholder"
controls
class="item-video"
>
<source :src="item.videoUrl" type="video/mp4">
</video>
<div v-if="item.title" class="item-title">{{ item.title }}</div>
</div>
<!-- 图文类型 -->
<div v-else class="item-content">
<img
v-if="item.image"
:src="placeholder"
:alt="item.title || '图文'"
class="item-image"
>
<div v-if="item.title" class="item-title">{{ item.title }}</div>
<div v-if="item.desc" class="item-desc">{{ item.desc }}</div>
</div>
</div>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading-more">
加载中...
</div>
<div v-if="isComplete && renderedList.length > 0" class="load-complete">
没有更多内容了
</div>
</div>
</template>
<script>
export default {
name: 'Waterfall',
directives: {
lazyLoad: {
inserted(el, binding, vnode) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = el.tagName === 'IMG' ? el : el.querySelector('img')
if (img) {
img.src = binding.value
//
img.onload = () => {
vnode.context.layoutItems()
}
//
img.onerror = () => {
if (vnode.context.placeholder) {
img.src = vnode.context.placeholder
}
}
}
observer.unobserve(el)
}
})
}, {
rootMargin: '0px 0px 100px 0px' // 100px
})
observer.observe(el)
el._observer = observer
},
unbind(el) {
if (el._observer) {
el._observer.disconnect()
}
}
}
},
// props
props: {
//
list: {
type: Array,
required: true,
default: () => []
},
//
columns: {
type: Number,
default: 2,
validator: function(value) {
return value >= 1 && value <= 5 // 1-5
}
},
//
gap: {
type: Number,
default: 10
},
//
lazyLoad: {
type: Boolean,
default: true
},
//
loadThreshold: {
type: Number,
default: 100
},
//
placeholder: {
type: String,
default: '改成你的图片'
},
totalItems: {
type: Number,
default: 0
}
},
data() {
return {
waterfallRef: null, //
columnHeights: [], //
columnWrapperRef: null, //
loading: false, //
isComplete: false, //
renderedList: [], //
pageSize: 20, //
currentPage: 1 //
}
},
computed: {
//
columnWidth() {
if (!this.waterfallRef) return 0
const containerWidth = this.waterfallRef.clientWidth
return (containerWidth - (this.columns - 1) * this.gap) / this.columns
}
},
watch: {
//
columns(newVal, oldVal) {
if (newVal !== oldVal) {
this.resetLayout()
}
},
//
list: {
handler(newList) {
this.renderedList = newList //
this.$nextTick(() => this.layoutItems())
},
deep: true
}
},
mounted() {
this.scrollContainer = this.$el
this.waterfallRef = this.$el
this.columnWrapperRef = this.$el.querySelector('.waterfall-column-wrapper')
this.resetLayout()
if (this.lazyLoad) {
this.scrollContainer.addEventListener('scroll', this.handleScroll)
}
window.addEventListener('resize', this.handleResize)
},
beforeDestroy() {
if (this.lazyLoad && this.scrollContainer) {
this.scrollContainer.removeEventListener('scroll', this.handleScroll)
}
window.removeEventListener('resize', this.handleResize)
},
methods: {
//
initColumnHeights() {
this.columnHeights = new Array(this.columns).fill(0)
},
//
initRenderList() {
this.renderedList = this.list.slice(0, this.pageSize)
this.currentPage = 1
this.isComplete = this.renderedList.length >= this.list.length
},
//
resetLayout() {
this.initColumnHeights()
this.initRenderList()
this.$nextTick(() => {
this.layoutItems()
})
},
//
layoutItems() {
if (!this.columnWrapperRef || !this.waterfallRef) return
//
const items = this.columnWrapperRef.children
if (!items || items.length === 0) return
//
this.initColumnHeights()
//
const tempColumnHeights = [...this.columnHeights]
//
Array.from(items).forEach((item, index) => {
//
const minHeight = Math.min(...tempColumnHeights)
const columnIndex = tempColumnHeights.indexOf(minHeight)
//
const left = columnIndex * (this.columnWidth + this.gap)
const top = minHeight
//
item.style.position = 'absolute'
item.style.width = `${this.columnWidth}px`
item.style.left = `${left}px`
item.style.top = `${top}px`
item.style.transition = 'all 0.3s ease'
//
tempColumnHeights[columnIndex] += item.clientHeight + this.gap
})
//
this.columnHeights = tempColumnHeights
//
const maxHeight = Math.max(...tempColumnHeights)
this.columnWrapperRef.style.height = `${maxHeight}px`
},
//
// handleScroll() {
// if (this.loading || this.isComplete) return
// const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
// const windowHeight = window.innerHeight
// const scrollHeight = document.documentElement.scrollHeight
// //
// if (scrollTop + windowHeight >= scrollHeight - this.loadThreshold) {
// this.loadMore()
// }
// },
handleScroll() {
if (this.loading || (this.totalItems > 0 && this.renderedList.length >= this.totalItems)) {
return
}
// .content-main
const scrollContainer = this.$parent.$el.querySelector('.content-main') || window
let scrollTop, clientHeight, scrollHeight
if (scrollContainer === window) {
scrollTop = document.documentElement.scrollTop || document.body.scrollTop
clientHeight = window.innerHeight
scrollHeight = document.documentElement.scrollHeight
} else {
// padding
scrollTop = scrollContainer.scrollTop
clientHeight = scrollContainer.clientHeight
scrollHeight = scrollContainer.scrollHeight
}
// 100px
if (scrollTop + clientHeight >= scrollHeight - this.loadThreshold) {
this.$emit('scroll-end') // loadMoreData
console.log('触发加载更多')
}
},
//
loadMore() {
if (this.loading || this.isComplete) return
this.loading = true
//
setTimeout(() => {
try {
const nextPage = this.currentPage + 1
const startIndex = (nextPage - 1) * this.pageSize
const endIndex = nextPage * this.pageSize
if (startIndex >= this.list.length) {
this.isComplete = true
return
}
const newItems = this.list.slice(startIndex, endIndex)
this.renderedList = [...this.renderedList, ...newItems]
this.currentPage = nextPage
//
if (endIndex >= this.list.length) {
this.isComplete = true
}
// DOM
this.$nextTick(() => {
this.layoutItems()
})
} catch (error) {
console.error('加载更多数据失败:', error)
} finally {
this.loading = false
}
}, 500)
},
jump(url) {
window.location.href = url
},
//
handleResize() {
this.resetLayout()
}
}
}
</script>
<style scoped>
.waterfall-container {
position: relative;
width: calc(100%);
height: 800px;
overflow: hidden;
overflow-y: auto;
}
.waterfall-column-wrapper {
position: relative;
width: 100%;
}
.waterfall-item {
margin-bottom: 10px;
break-inside: avoid;
box-sizing: border-box;
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.waterfall-item:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.item-content {
padding: 12px;
}
.item-image, .item-video {
width: 100%;
border-radius: 4px;
display: block;
background: #f5f5f5;
}
.item-video {
aspect-ratio: 9/16;
object-fit: cover;
}
.item-title {
margin-top: 8px;
font-size: 24px;
line-height: 1.4;
color: #333;
font-weight: bold;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.item-desc {
margin-top: 6px;
font-size: 12px;
color: #666;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.loading-more, .load-complete {
text-align: center;
padding: 20px 0;
color: #999;
font-size: 14px;
}
</style>

282
src/views/newBookRecommend.vue

@ -0,0 +1,282 @@
<template>
<!-- 新书推荐 -->
<div class="content-main new-main">
<div class="swiper-container">
<div class="swiper-wrapper new-book-list">
<div v-for="(item,index) in rankingList" :key="index" class="swiper-slide" @click="handleDetails(index)">
<div class="book-list-item">
<div class="book-img">
<img :src="item.imgPath" :onerror="defaultImg">
</div>
<div class="book-info">
<h4 class="title-item">{{ item.name }}</h4>
<p>作者{{ item.author }}</p>
<p>出版社{{ item.publisher }}</p>
<p>出版年份{{ item.publisherdate }}</p>
</div>
</div>
</div>
</div>
<div class="icon iconfont prev-btn">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-zuohua" />
</svg>
</div>
<div class="icon iconfont next-btn">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-youhua" />
</svg>
</div>
</div>
<BookDetails ref="detailDom" />
</div>
</template>
<script>
import { Swiper } from 'vue-awesome-swiper'
import 'swiper/swiper-bundle.css'
// import { FetchNewBookRecommend, FetchCoverByISBN, getBookDetailsByISBN } from '@/api/inquiryMachine'
import { FetchNewBook, FetchMarcByISBN, getBookDetailsByISBN } from '@/api/inquiryMachine'
import BookDetails from './module/bookDetails.vue'
export default {
name: 'NewBookRecommend',
components: {
BookDetails
},
data() {
return {
defaultImg: 'this.src="' + require('@/assets/images/default-img.png') + '"',
coverUrl: null,
rankingList: []
}
},
created() {
this.getNewBook()
},
mounted() {
},
methods: {
initSwiper() {
this.$nextTick(() => {
new Swiper('.swiper-container', {
touchEventsTarget: '.swiper-container',
slidesPerView: 5,
spaceBetween: 20,
centeredSlides: true,
observer: true,
// loop: true,
centeredSlidesBounds: true,
watchSlidesVisibility: true,
navigation: {
nextEl: '.next-btn',
prevEl: '.prev-btn'
}
// on: {
// click: function() {
// alert(this.clickedIndex)
// }
// }
})
})
},
getNewBook() {
console.log('this.$route.query.libcode', this.$route.query.libcode)
const params = {
'libcode': this.$route.query.libcode,
'size': 40
}
FetchNewBook(params).then(res => {
// http://192.168.99.67:8080/downloadFile/qytsg/ae281b90-b100-4541-9379-3e104854652c.png
// http://192.168.99.72:14000/api/fileRelevant/getImg?imgType=2&imgId=c964bcab-ec82-43f0-8653-04d930d7da4a
const linkSrc = process.env.NODE_ENV === 'production' ? window.g.ApiUrl : process.env.VUE_APP_BASE_API
console.log('res.data', res)
this.rankingList = res.map(item => {
if (item.imgPath) {
item.imgPath = linkSrc + '/api/fileRelevant/getImg?imgType=2&imgId=' + item.imgPath
return Promise.resolve(item)
} else {
const params = {
'sIsbn': item.isbn
}
return FetchMarcByISBN(params).then(response => {
const result = JSON.parse(response.data)[0]
if (result.srcurl) {
item.imgPath = result.srcurl
return item
} else if (result.img) {
item.imgPath = 'data:image/png;base64,' + result.img
return item
} else {
return null
}
})
}
})
Promise.all(this.rankingList).then(results => {
this.rankingList = results.filter(item => item !== null)
this.initSwiper()
})
console.log('this.rankingList', this.rankingList)
})
},
// getBookList() {
// const linkSrc = process.env.NODE_ENV === 'production' ? window.g.ApiUrl : process.env.VUE_APP_BASE_API
// const params = {
// libcode: this.libcode,
// pageNo: 1,
// pageSize: 10
// }
// FetchNewBookRecommend(params).then(res => {
// console.log(res)
// this.rankingList = res.newbookList.map((item, index) => {
// if (item.nbImgPath) {
// item.cover = linkSrc + '/downloadFile' + item.nbImgPath
// } else {
// item.cover = null
// }
// return item
// })
// this.initSwiper()
// }).catch(() => {
// this.$message.error('')
// })
// },
// getCoverByISBN(isbn, item) {
// const params = {
// isbn: isbn
// }
// FetchCoverByISBN(params).then((res) => {
// // item.cover = window.URL.createObjectURL(res)
// item.cover = res
// this.rankingList.push(item)
// this.initSwiper()
// })
// },
//
handleDetails(index) {
const params = {
isbn: this.rankingList[index].isbn.replace(/\-/g, '')
}
getBookDetailsByISBN(params).then(res => {
const linkSrc = process.env.NODE_ENV === 'production' ? window.g.ApiUrl : process.env.VUE_APP_BASE_API
res.imgPath = linkSrc + '/api/fileRelevant/getImg?imgType=2&imgId=' + res.bookCover
this.$refs.detailDom.bookData = res
this.$refs.detailDom.dialogVisible = true
})
}
}
}
</script>
<style lang="scss">
@import "~@/assets/styles/index.scss";
.new-main{
position: relative;
padding: 0 50px;
}
.new-book-list{
color: #333;
.book-list-item{
width: 100%;
box-shadow: 0px 0px 20px 1px #D2D2D2;
border-radius: 2px 2px 2px 2px;
}
.book-img{
height: 6.575rem;
display: flex;
align-items: center;
overflow: hidden;
img{
display: block;
width: 100%;
// height: 100%;
}
}
.book-info{
padding: 10px 20px;
h4{
font-weight: normal;
margin-bottom: 20px;
}
p{
margin-bottom: 10px;
}
}
}
.icon{
height: 1.1em;
}
.prev-btn,
.next-btn{
position: absolute;
bottom: 60px;
font-size: 60px;
// color: #71C09E;
z-index: 999;
}
.prev-btn{
left: 100px;
}
.next-btn{
right: 100px;
}
.swiper-container {
width: 100%;
height: 100%;
overflow: hidden;
}
.swiper-slide {
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
transition: 300ms;
transform: scale(0.84);
.book-info{
line-height: 20px;
font-size: 16px;
h4{
font-size: 20px;
}
}
}
.swiper-slide.swiper-slide-prev,
.swiper-slide.swiper-slide-next {
font-size: 28px;
transform: scale(0.9);
.book-info{
line-height: 28px;
font-size: 22px;
h4{
font-size: 28px;
}
}
}
.swiper-slide-active,.swiper-slide-duplicate-active {
transform: scaleX(1);
.book-info{
line-height: 40px;
font-size: 28px;
h4{
font-size: 40px;
}
}
}
</style>

158
src/views/old.vue

@ -0,0 +1,158 @@
<template>
<!-- 数字资源 -->
<div class="content-main">
<div class="swiper mySwiper">
<div class="swiper-wrapper">
<div v-for="(item,index) in swiperListTemp" :key="index" class="swiper-slide img-module">
<div v-for="(cell,i) in item" :key="i" :class="{'item-big': i % 4 === 0 || i % 4 === 3, 'item-small': i % 4 === 1 || i % 4 === 2, 'main-item': true}" @click="jump(cell.linkUrl)">
<img :src="cell.image" :alt="cell.overTitle || '图片'">
<div class="title">
{{ cell.overTitle }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { Swiper } from 'vue-awesome-swiper'
import 'swiper/swiper-bundle.css'
import { FetchQueryTopicDetails, FetchInitQueryTopicContext } from '@/api/inquiryMachine'
export default {
name: 'WaterfallFlow',
data() {
return {
rankingList: []
}
},
computed: {
swiperListTemp() {
let index = 0
const count = 4
const arrTemp = []
const experts = this.rankingList
for (let i = 0; i < this.rankingList.length; i++) {
index = parseInt(i / count)
if (arrTemp.length <= index) {
arrTemp.push([])
}
arrTemp[index].push(experts[i])
}
return arrTemp
}
},
created() {
this.getQueryTopicDetails()
},
mounted() {
this.initSwiper()
},
methods: {
initSwiper() {
this.$nextTick(() => {
new Swiper('.swiper', {})
})
},
getQueryTopicDetails() {
const params = {
'id': this.$route.query.menuId
}
FetchQueryTopicDetails(params).then(res => {
console.log(res)
if (res && res.queryTopicList.length > 0) {
this.getInitQueryTopicContext(res)
}
}).catch(() => {
this.$message.error('接口错误')
})
},
getInitQueryTopicContext(result) {
const params = {
'page': 0,
'size': 10,
'libcode': result.queryMenu.libcode,
'topicId': result.queryTopicList[0].id,
'topicType': result.queryTopicList[0].type
}
FetchInitQueryTopicContext(params).then(res => {
const linkSrc = process.env.NODE_ENV === 'production'
? window.g.ApiUrl
: process.env.VUE_APP_BASE_API
if (res && res.content && res.content.length > 0) {
this.rankingList = res.content.map(item => {
if (item && item.imgUrl) {
return {
...item,
image: `${linkSrc}/api/fileRelevant/getImg?imgType=2&imgId=${item.imgUrl}`
}
}
return {
...item,
image: null
}
})
} else {
this.rankingList = []
}
}).catch(() => {
this.$message.error('接口错误')
})
},
jump(url) {
window.location.href = url
}
}
}
</script>
<style lang="scss">
@import "~@/assets/styles/index.scss";
.img-module {
width: 100%;
height: 100%;
padding: 40px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
align-content: space-between;
.main-item {
position: relative;
img {
width: 100%;
height: 100%;
border-radius: 30px;
}
.title {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-family: "HuXiaoBoNanShenTi";
font-size: 80px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
}
}
.item-big {
width: 1000px;
height: 390px;
}
.item-small {
width: 800px;
height: 310px;
.title {
font-size: 64px;
}
}
}
</style>

342
src/views/waterfallFlow.vue

@ -0,0 +1,342 @@
<template>
<!-- 数字资源 -->
<div class="content-main">
<div class="swiper mySwiper">
<div class="swiper-wrapper">
<!-- 每个swiper-slide包含4个项目 -->
<div v-for="(group, groupIndex) in swiperGroups" :key="groupIndex" class="swiper-slide">
<div class="img-module">
<div
v-for="(cell, i) in group"
:key="i"
:class="{'item-big': i % 4 === 0 || i % 4 === 3, 'item-small': i % 4 === 1 || i % 4 === 2, 'main-item': true}"
@click="jump(cell.linkUrl)"
>
<img v-if="cell.image" :src="cell.image" :alt="cell.overTitle || '图片'">
<div v-else class="placeholder" />
<div class="title">
{{ cell.overTitle }}
</div>
</div>
</div>
</div>
</div>
<!-- Swiper导航按钮 -->
<div class="swiper-button-prev" />
<div class="swiper-button-next" />
</div>
<!-- 加载更多按钮 -->
<div class="load-more" @click="loadMore">
<span v-if="isLoading">加载中...</span>
<span v-else-if="hasMore">加载更多</span>
<!-- <span v-else>没有更多数据了</span> -->
</div>
</div>
</template>
<script>
import { Swiper } from 'vue-awesome-swiper'
import 'swiper/swiper-bundle.css'
import { FetchQueryTopicDetails, FetchInitQueryTopicContext } from '@/api/inquiryMachine'
export default {
name: 'WaterfallFlow',
components: {
},
data() {
return {
rankingList: [],
swiperInstance: null,
pageParams: {
page: 0,
size: 100,
libcode: '',
topicId: '',
topicType: ''
},
hasMore: true,
isLoading: false
}
},
computed: {
// 4
swiperGroups() {
const groups = []
const itemsPerGroup = 4
for (let i = 0; i < this.rankingList.length; i += itemsPerGroup) {
// 4
groups.push(this.rankingList.slice(i, i + itemsPerGroup))
}
return groups
}
},
created() {
this.getQueryTopicDetails()
},
mounted() {
// Swiper
this.$watch('swiperGroups', (newVal) => {
if (newVal.length > 0) {
this.initSwiper()
}
}, { immediate: false })
},
methods: {
initSwiper() {
this.$nextTick(() => {
if (this.swiperInstance) {
this.swiperInstance.destroy(true, true)
}
//
this.swiperInstance = new Swiper('.mySwiper', {
direction: 'horizontal',
slidesPerView: 1, //
speed: 500,
loop: false,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
},
pagination: {
el: '.swiper-pagination',
clickable: true,
renderBullet: function(index, className) {
return `<span class="${className}">${index + 1}</span>`
}
},
observer: true,
observeParents: true
})
})
},
getQueryTopicDetails() {
const params = {
'id': this.$route.query.menuId
}
FetchQueryTopicDetails(params).then(res => {
console.log('主题详情:', res)
if (res && res.queryTopicList && res.queryTopicList.length > 0) {
this.pageParams.libcode = res.queryMenu.libcode
this.pageParams.topicId = res.queryTopicList[0].id
this.pageParams.topicType = res.queryTopicList[0].type
this.loadMore()
} else {
this.hasMore = false
}
}).catch(() => {
this.$message.error('获取主题详情失败')
this.hasMore = false
})
},
loadMore() {
if (this.isLoading || !this.hasMore) return
this.isLoading = true
const params = {
page: this.pageParams.page,
size: this.pageParams.size,
libcode: this.pageParams.libcode,
topicId: this.pageParams.topicId,
topicType: this.pageParams.topicType
}
FetchInitQueryTopicContext(params).then(res => {
console.log('获取数据:', res)
if (res && res.content && res.content.length > 0) {
const linkSrc = process.env.NODE_ENV === 'production'
? window.g.ApiUrl
: process.env.VUE_APP_BASE_API
const newItems = res.content.map(item => ({
...item,
image: item.imgUrl ? `${linkSrc}/api/fileRelevant/getImg?imgType=2&imgId=${item.imgUrl}` : null
}))
this.rankingList = [...this.rankingList, ...newItems]
this.hasMore = newItems.length >= this.pageParams.size
this.pageParams.page++
} else {
this.hasMore = false
}
}).catch(() => {
this.$message.error('获取数据失败')
}).finally(() => {
this.isLoading = false
})
},
jump(url) {
if (url) {
window.location.href = url
}
}
}
}
</script>
<style lang="scss">
@import "~@/assets/styles/index.scss";
//
.img-module {
width: 100%;
height: 100%;
padding: 40px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
align-content: space-between;
.main-item {
position: relative;
cursor: pointer;
transition: transform 0.3s ease;
&:hover {
transform: translateY(-5px);
}
img {
width: 100%;
height: 100%;
border-radius: 30px;
object-fit: cover;
}
.placeholder {
width: 100%;
height: 100%;
background-color: #f5f5f5;
border-radius: 30px;
}
.title {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-family: "HuXiaoBoNanShenTi";
font-size: 80px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
text-shadow: 0 2px 4px rgba(255, 255, 255, 0.8);
padding: 0 20px;
text-align: center;
}
}
.item-big {
width: 1000px;
height: 390px;
}
.item-small {
width: 800px;
height: 310px;
.title {
font-size: 64px;
}
}
}
//
.load-more {
position: fixed;
bottom: 20px;
left: 0;
width: 100%;
text-align: center;
padding: 30px 0;
font-size: 18px;
color: #666;
z-index: 9999;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: #007bff;
}
}
// Swiper
.swiper-button-prev,
.swiper-button-next {
width: 50px;
height: 50px;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
&::after {
font-size: 20px;
color: #333;
}
}
.swiper-button-prev {
left: 10px;
}
.swiper-button-next {
right: 10px;
}
.swiper-pagination {
bottom: 0;
.swiper-pagination-bullet {
width: 12px;
height: 12px;
margin: 0 6px;
background-color: #ddd;
&.swiper-pagination-bullet-active {
background-color: #007bff;
width: 30px;
border-radius: 6px;
}
}
}
@media (max-width: 1920px) {
.item-big {
width: calc(50% - 20px);
height: auto;
}
.item-small {
width: calc(25% - 20px);
height: auto;
}
}
@media (max-width: 1200px) {
.main-item .title {
font-size: 60px;
}
.item-small .title {
font-size: 48px;
}
}
</style>

160
src/views/waterfallFlow2.vue

@ -0,0 +1,160 @@
<template>
<div class="content-main" style="padding: 40px;">
<waterfall
:list="dataList"
:columns="columns"
:loading="loading"
:placeholder="placeholderImg"
:total-items="totalItems"
@scroll-end="loadMoreData"
/>
</div>
<!-- <div class="controls">
<button :class="{ active: columns === 2 }" @click="columns = 2">2</button>
<button :class="{ active: columns === 3 }" @click="columns = 3">3</button>
<button :class="{ active: columns === 4 }" @click="columns = 4">4</button>
</div> -->
</template>
<script>
import Waterfall from './module/waterfall.vue'
import { FetchQueryTopicDetails, FetchInitQueryTopicContext } from '@/api/inquiryMachine'
export default {
components: {
Waterfall
},
data() {
return {
columns: 2,
dataList: [],
loading: false,
currentPage: 0,
pageSize: 10,
totalItems: 0,
topicInfo: null,
placeholderImg: require('@/assets/images/default-img.png')
}
},
created() {
this.getQueryTopicDetails()
},
methods: {
getQueryTopicDetails() {
this.loading = true
const params = {
'id': this.$route.query.menuId
}
FetchQueryTopicDetails(params).then(res => {
console.log('主题详情数据:', res)
if (res && res.queryTopicList && res.queryTopicList.length > 0) {
this.topicInfo = res
this.currentPage = 0
this.getInitQueryTopicContext()
} else {
this.$message.warning('未找到相关主题数据')
this.loading = false
}
}).catch(() => {
this.$message.error('获取主题详情失败')
this.loading = false
})
},
//
getInitQueryTopicContext() {
if (!this.topicInfo) return
this.loading = true
const params = {
'page': this.currentPage,
'size': this.pageSize,
'libcode': this.topicInfo.queryMenu.libcode,
'topicId': this.topicInfo.queryTopicList[0].id,
'topicType': this.topicInfo.queryTopicList[0].type
}
FetchInitQueryTopicContext(params).then(res => {
this.loading = false
// console.log('res.totalElements', res.totalElements)
if (res.totalElements) {
this.totalItems = res.totalElements
}
const linkSrc = process.env.NODE_ENV === 'production'
? window.g.ApiUrl
: process.env.VUE_APP_BASE_API
if (res && res.content && res.content.length > 0) {
const formattedData = res.content.map(item => ({
id: item.id || `item-${Date.now()}-${Math.random()}`,
type: 'image',
title: item.overTitle || '无标题',
image: item.imgUrl ? `${linkSrc}/api/fileRelevant/getImg?imgType=2&imgId=${item.imgUrl}` : this.placeholderImg,
linkUrl: item.linkUrl || '#'
}))
this.dataList = [...this.dataList, ...formattedData]
// console.log(`${this.currentPage + 1}:`, this.dataList.length)
} else {
this.totalItems = this.dataList.length
}
}).catch(() => {
this.$message.error('获取主题内容失败')
this.loading = false
})
},
//
loadMoreData() {
if (this.dataList.length >= this.totalItems) {
// console.log('5')
return
}
if (this.loading) return
this.currentPage++
// console.log('', this.currentPage + 1, '')
this.getInitQueryTopicContext()
},
isAllDataLoaded() {
if (this.totalItems > 0) {
return this.dataList.length >= this.totalItems
}
return this.currentPage > 0 && this.dataList.length % this.pageSize !== 0
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/index.scss";
// .controls {
// margin-bottom: 20px;
// display: flex;
// gap: 10px;
// padding: 0 16px;
// }
// .controls button {
// padding: 8px 16px;
// background: #f0f0f0;
// border: none;
// border-radius: 4px;
// cursor: pointer;
// transition: all 0.2s ease;
// }
// .controls button:hover {
// background: #e0e0e0;
// }
// .controls button.active {
// background: #ff2442;
// color: white;
// }
</style>

61
vue.config.js

@ -0,0 +1,61 @@
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
},
proxy: {
'/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 = name
return args
})
}
}
Loading…
Cancel
Save