-
9.env.development
-
8.env.production
-
197.eslintrc.js
-
23.gitignore
-
120.trae/documents/traffic-flow-screen-plan.md
-
11babel.config.js
-
57package.json
-
BINpublic/favicon.ico
-
19public/index.html
-
19public/static/branches.json
-
4public/static/config.js
-
16src/App.vue
-
13src/api/library.js
-
BINsrc/assets/fonts/YouSheBiaoTiHei.ttf
-
BINsrc/assets/fonts/ZhenyanGB.ttf
-
13src/assets/fonts/fonts.css
-
68src/assets/iconfont/iconfont.css
-
1src/assets/iconfont/iconfont.js
-
100src/assets/iconfont/iconfont.json
-
45src/assets/iconfont/iconfont.svg
-
BINsrc/assets/iconfont/iconfont.ttf
-
BINsrc/assets/iconfont/iconfont.woff
-
BINsrc/assets/iconfont/iconfont.woff2
-
BINsrc/assets/images/default-img.png
-
BINsrc/assets/images/header-bg.png
-
BINsrc/assets/images/header-left.png
-
BINsrc/assets/images/header-right.png
-
BINsrc/assets/images/notice.gif
-
BINsrc/assets/images/num-img.png
-
BINsrc/assets/images/star-1.png
-
BINsrc/assets/images/star-2.png
-
BINsrc/assets/images/star-3.png
-
BINsrc/assets/images/title-big.png
-
BINsrc/assets/images/title.png
-
BINsrc/assets/images/top-line.png
-
BINsrc/assets/images/top.png
-
BINsrc/assets/logo.png
-
116src/assets/styles/ball-running-animate.css
-
46src/assets/styles/font-some.css
-
710src/assets/styles/index.scss
-
93src/assets/styles/style.scss
-
147src/common/flexible.js
-
49src/main.js
-
22src/router/index.js
-
15src/store/index.js
-
100src/utils/index.js
-
39src/utils/request.js
-
26src/utils/resizeMixins.js
-
75src/views/header/index.vue
-
26src/views/index.vue
-
29src/views/trafficFlow/components/DayInFlowCard.vue
-
159src/views/trafficFlow/components/MonthInFlowChart.vue
-
106src/views/trafficFlow/components/TotalFlowSummary.vue
-
157src/views/trafficFlow/components/YearInFlowChart.vue
-
118src/views/trafficFlow/index.vue
-
68vue.config.js
@ -0,0 +1,9 @@ |
|||||
|
ENV = 'development' |
||||
|
|
||||
|
# 接口地址 |
||||
|
# 许镇-本地服地址 |
||||
|
VUE_APP_BASE_API = 'http://192.168.99.63:8080' |
||||
|
VUE_APP_WS_API = 'ws://192.168.99.63:8081' |
||||
|
|
||||
|
# 是否启用 babel-plugin-dynamic-import-node插件 |
||||
|
VUE_CLI_BABEL_TRANSPILE_MODULES = true |
||||
@ -0,0 +1,8 @@ |
|||||
|
ENV = 'production' |
||||
|
|
||||
|
# 如果使用 Nginx 代理后端接口,那么此处需要改为 '/',文件查看 Docker 部署篇,Nginx 配置 |
||||
|
# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http |
||||
|
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://172.22.0.23:7071' |
||||
@ -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'] |
||||
|
} |
||||
|
} |
||||
@ -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 |
||||
@ -0,0 +1,120 @@ |
|||||
|
# 客流量大屏改造方案 |
||||
|
|
||||
|
## Context(背景与目标) |
||||
|
|
||||
|
现有项目 `hs-flow‑screen` 是一个深色科技风的"借阅/通知大屏"(三栏布局:总借阅、借阅排行、进馆、阅读之星、通知、新书推荐)。用户希望仿照目标页面 `http://libraryu.seaeverit.com:15000/main?token=...` 做一个**客流量大屏**,目标页面布局为:顶部 Header → "客流量展示"标签 → 三列卡片(当日进馆折线图 / 当月进馆折线图 / 当年进馆数字翻牌)→ 全宽"累计客流量汇总"翻牌器。 |
||||
|
|
||||
|
经确认的改造口径: |
||||
|
1. **保留深色科技风视觉**(深蓝背景 `#010326`、白色文字、蓝色发光 `#1AC9FF`、`common-title`/`module-content` 蓝色样式),**不**改成目标页的白底橙色,只仿照**布局结构**。 |
||||
|
2. **保留现有 Header 组件**(动画线条 + 天气 iframe + 渐变标题),标题用"黄石图书馆"。 |
||||
|
3. **删除不再使用的旧组件目录**。 |
||||
|
4. **数据先用 mock**,预留接口对接位置。 |
||||
|
|
||||
|
## 关键约束(基于源码确认) |
||||
|
|
||||
|
- 适配:rem 方案(`src/common/flexible.js`,1920 设计稿,1rem≈80px,范围 1366–2560),所有尺寸用 rem。 |
||||
|
- echarts 4:`this.$echarts` 全局已注册(`main.js`),用 `this.$echarts.init(this.$refs.chart)` + ref,**不要用 `getElementById`**(旧 `components/echart/*` 用 id 会有冲突)。 |
||||
|
- `resizeMixins.js`(`src/utils/resizeMixins.js`)提供自动 resize(要求组件 `data.chart` 字段),但其 `removeEventListener('reisze'…)` 拼写错误且不 dispose → 新组件须在 `beforeDestroy` 手动 `this.chart.dispose(); this.chart=null`。 |
||||
|
- 数字翻牌:`vue-count-to` 的 `CountTo`,用法 `<count-to :start-val="0" :end-val="num" :duration="3200" />`。 |
||||
|
- 滚动:`vue-seamless-scroll` 已 `Vue.use`,组件名 `<seamless>`。 |
||||
|
- `.common-title` / `.module-content` 是 `.screen-main` 的后代选择器 → 新布局根容器必须仍是 `.screen-main`(叠加 `.traffic-main`)。 |
||||
|
- Header 标题 prop:`headerTitle`,`getCurrentTime()` 返回"YYYY年MM月DD日 HH:mm:ss"。 |
||||
|
- 端口 8080,`npm run serve` 自动开浏览器。 |
||||
|
|
||||
|
## 新文件结构 |
||||
|
|
||||
|
``` |
||||
|
src/views/ |
||||
|
├── index.vue # 改写:Header + <TrafficFlow/>,删除旧的高度计算逻辑 |
||||
|
├── header/index.vue # 保留不动 |
||||
|
└── trafficFlow/ |
||||
|
├── index.vue # 新建:.screen-main.traffic-main 布局容器 |
||||
|
└── components/ |
||||
|
├── DayInFlowChart.vue # 当日进馆折线图(按小时) |
||||
|
├── MonthInFlowChart.vue # 当月进馆折线图(按日) |
||||
|
├── YearInFlowCard.vue # 当年进馆数字翻牌(单个大数字) |
||||
|
└── TotalFlowSummary.vue # 累计客流量汇总(8 位翻牌卡片,横向可滚动) |
||||
|
``` |
||||
|
|
||||
|
## 实施步骤 |
||||
|
|
||||
|
### 1. 新建 trafficFlow 4 个子组件 + index.vue(先建骨架,不破坏现有) |
||||
|
- `DayInFlowChart.vue` / `MonthInFlowChart.vue`:`<div class="chart-box" ref="chart"/>`,`mixins:[resize]`,`data.chart=null` + mock 数据,`mounted` → `initChart()` → `setOption()`,`beforeDestroy` dispose。setOption 用蓝色系(线 `#1AC9FF`、轴 `#2C75E0`、分割线 `rgba(44,117,224,0.3)` 虚线、白色文字、`areaStyle` 用 `this.$echarts.graphic.LinearGradient` 蓝色渐变、`smooth:true`)。 |
||||
|
- `YearInFlowCard.vue`:`<count-to :end-val="yearNum"/>`,mock `yearNum:1286500`。 |
||||
|
- `TotalFlowSummary.vue`:`totalNum:98765432` → `computed.digitList` 用 `padStart(8,'0').split('')` 固定 8 位,每位一个 `<count-to>`,外裹 `<seamless :data :class-option>` 横向滚动(`direction:2`)。预留 `fetchData()` 注释。 |
||||
|
- `trafficFlow/index.vue`:模板见下方。 |
||||
|
|
||||
|
### 2. 在 `src/assets/styles/index.scss` 末尾追加客流量布局样式(不动现有任何规则) |
||||
|
新增 `// ===== 客流量大屏布局 =====` 段,关键规则: |
||||
|
- `.traffic-main { flex-direction:column; justify-content:flex-start; gap:0.125rem; }`(叠加在 `.screen-main` 上) |
||||
|
- `.traffic-section-title`(顶部"客流量展示"标签,用 `title-big.png`) |
||||
|
- `.traffic-row { display:flex; }`、`.traffic-row-3 { flex:1; gap:0.25rem; min-height:0; }`(min-height:0 防 echarts 高度坍缩) |
||||
|
- `.traffic-card { flex:1; display:flex; flex-direction:column; min-width:0; .module-content{flex:1; overflow:hidden; padding:0.125rem 0.2rem;} }` |
||||
|
- `.traffic-summary { height:1.6rem; }` |
||||
|
- `.chart-box { width:100%; height:100%; }` |
||||
|
- `.year-num`(当年大数字,`#1AC9FF` + text-shadow)、`.summary-digits`/`.digit-card`(每位翻牌,`num-img.png` 背景) |
||||
|
|
||||
|
### 3. 改写 `src/views/index.vue` 为极简入口 |
||||
|
```vue |
||||
|
<template> |
||||
|
<div id="screen-container"> |
||||
|
<Header :header-title="headerTitle" /> |
||||
|
<TrafficFlow /> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script> |
||||
|
import Header from '@/views/header/index.vue' |
||||
|
import TrafficFlow from '@/views/trafficFlow/index.vue' |
||||
|
export default { |
||||
|
name: 'Home', |
||||
|
components: { Header, TrafficFlow }, |
||||
|
data() { return { headerTitle: '黄石图书馆' } } |
||||
|
} |
||||
|
</script> |
||||
|
<style lang="scss">@import "~@/assets/styles/index.scss";</style> |
||||
|
``` |
||||
|
(删除原 `calcXxxHeight`/`$refs`/resize 监听等所有逻辑) |
||||
|
|
||||
|
`trafficFlow/index.vue` 模板骨架: |
||||
|
```vue |
||||
|
<template> |
||||
|
<div class="screen-main traffic-main"> |
||||
|
<div class="common-title traffic-section-title">客流量展示</div> |
||||
|
<div class="traffic-row traffic-row-3"> |
||||
|
<div class="traffic-card"><div class="common-title">当日进馆</div><div class="module-content"><DayInFlowChart/></div></div> |
||||
|
<div class="traffic-card"><div class="common-title">当月进馆</div><div class="module-content"><MonthInFlowChart/></div></div> |
||||
|
<div class="traffic-card"><div class="common-title">当年进馆</div><div class="module-content year-content"><YearInFlowCard/></div></div> |
||||
|
</div> |
||||
|
<div class="traffic-row traffic-summary"><TotalFlowSummary/></div> |
||||
|
</div> |
||||
|
</template> |
||||
|
``` |
||||
|
|
||||
|
### 4. 启动验证一轮(`npm run serve`) |
||||
|
确认:Header + "客流量展示" + 三卡片(两折线图初始化、一翻牌)+ 汇总翻牌滚动;控制台无 echarts 报错。 |
||||
|
|
||||
|
### 5. 删除旧组件目录 |
||||
|
- `src/views/accessToLibrary/`、`totalLending/`、`lengingRanking/`、`newBookRecommend/`、`notice/`、`readStar/`、`todayBorrowed/` |
||||
|
|
||||
|
### 6. 可选清理 |
||||
|
- 删 `src/components/echart/`(删旧组件后无引用)、`src/common/scrollMixins.js`(全项目无引用)。 |
||||
|
- 清 `index.scss` 中三栏 `.screen-left/.screen-right/.screen-middle` 及旧组件死 CSS(留着不影响,但建议删三栏那三条以免误导)。 |
||||
|
|
||||
|
## 接口对接预留(mock 阶段不做) |
||||
|
- `src/api/library.js` 的 `FetchInitIntoNum` 已存在,保留不删。后续在 4 个组件的 `fetchData()`(已注释)中对接,字段参考 `historyPeopleNum`(累计)、`dayPeopleNum`/`monthPeopleNum`(当日/当月总数);按小时/按日粒度折线数据需后端补接口。 |
||||
|
|
||||
|
## 验证 |
||||
|
1. `npm run serve` → `http://localhost:8080`: |
||||
|
- Header 显示"黄石图书馆" + 天气 + 每秒跳的时间 |
||||
|
- 顶部"客流量展示"标签 |
||||
|
- 第一行三列:左/中蓝色平滑折线图(带渐变面积)、右"当年进馆"0→1286500 翻牌动画 |
||||
|
- 第二行"累计客流量汇总"8 位翻牌(num-img.png 蓝牌背景)横向滚动 |
||||
|
2. F12 控制台无 echarts init/id 重复/resize 报错 |
||||
|
3. 拖拽窗口:折线图随 resizeMixins 自动 resize,rem 尺寸随 flexible 重算 |
||||
|
4. 改 mock 数字热更新不残留旧 echarts 实例(dispose 生效) |
||||
|
5. `npm run build` 无打包错误 |
||||
|
|
||||
|
## 风险规避 |
||||
|
- echarts 容器高度坍缩:`.traffic-row-3 { min-height:0 }` + `.traffic-card .module-content { flex:1 }` 必须同时有;必要时给 `.chart-box` 兜底 `min-height:2rem`。 |
||||
|
- `seamless` 横向滚动可能因 8 位内容不够触发:调小 `limitMoveNum:1`,或不要滚动直接静态居中("可滚动"非硬需求)。 |
||||
|
- 顺序:先建新→改入口→验证→再删旧,避免白屏。 |
||||
@ -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' |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
{ |
||||
|
"name": "intelligence-screen", |
||||
|
"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": "SET NODE_OPTIONS=--openssl-legacy-provider && 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.10", |
||||
|
"vue": "^2.6.11", |
||||
|
"vue-awesome": "^4.0.2", |
||||
|
"vue-count-to": "^1.0.13", |
||||
|
"vue-router": "^3.1.5", |
||||
|
"vue-seamless-scroll": "^1.1.23", |
||||
|
"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-awesome-swiper": "^3.1.3", |
||||
|
"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" |
||||
|
] |
||||
|
} |
||||
@ -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> |
||||
|
</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> |
||||
|
<script src="<%= BASE_URL %>static/config.js"></script> |
||||
|
<!-- built files will be auto injected --> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,19 @@ |
|||||
|
{ |
||||
|
"schemaVersion": "1.0", |
||||
|
"description": "图书馆大屏网点配置:网点编号与网点名称映射", |
||||
|
"branches": [ |
||||
|
{ "code": "HWXD", "name": "宏伟星都城市书房" }, |
||||
|
{ "code": "JTSF", "name": "中窑江滩城市书房" }, |
||||
|
{ "code": "QYG", "name": "群艺馆城市书房" }, |
||||
|
{ "code": "QSH", "name": "青山湖城市书房" }, |
||||
|
{ "code": "HSGQ", "name": "黄石港区城市书房" }, |
||||
|
{ "code": "SDGY", "name": "湿地公园城市书房" }, |
||||
|
{ "code": "SNSQ", "name": "神牛社区城市书房" }, |
||||
|
{ "code": "TSSF", "name": "铁山城市书房" }, |
||||
|
{ "code": "WSW", "name": "未苏湾城市书房" }, |
||||
|
{ "code": "SMZJ", "name": "市民之家城市书房" }, |
||||
|
{ "code": "RTFLY", "name": "儿童福利院城市书房" }, |
||||
|
{ "code": "D3、D4", "name": "保利时光绘城市书房" }, |
||||
|
{ "code": "D5", "name": "春湖公园城市书房" } |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,4 @@ |
|||||
|
window.g = { |
||||
|
AXIOS_TIMEOUT: 10000, |
||||
|
ApiUrl: 'http://192.168.99.63:8080' // 配置服务器地址
|
||||
|
} |
||||
@ -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> |
||||
@ -0,0 +1,13 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
export function FetchInitIntoNumHS(params) { |
||||
|
return request({ |
||||
|
url: '/dxhtsg/initIntoNumHS', |
||||
|
method: 'get', |
||||
|
params: params |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export default { |
||||
|
FetchInitIntoNumHS |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
@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; |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
@font-face { |
||||
|
font-family: "iconfont"; /* Project id 3646564 */ |
||||
|
src: url('iconfont.woff2?t=1663379994606') format('woff2'), |
||||
|
url('iconfont.woff?t=1663379994606') format('woff'), |
||||
|
url('iconfont.ttf?t=1663379994606') format('truetype'), |
||||
|
url('iconfont.svg?t=1663379994606#iconfont') format('svg'); |
||||
|
} |
||||
|
|
||||
|
.iconfont { |
||||
|
font-family: "iconfont" !important; |
||||
|
font-size: 16px; |
||||
|
font-style: normal; |
||||
|
-webkit-font-smoothing: antialiased; |
||||
|
-moz-osx-font-smoothing: grayscale; |
||||
|
} |
||||
|
|
||||
|
.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
@ -0,0 +1,100 @@ |
|||||
|
{ |
||||
|
"id": "3646564", |
||||
|
"name": "智慧屏", |
||||
|
"font_family": "iconfont", |
||||
|
"css_prefix_text": "icon-", |
||||
|
"description": "", |
||||
|
"glyphs": [ |
||||
|
{ |
||||
|
"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 |
||||
|
} |
||||
|
] |
||||
|
} |
||||
45
src/assets/iconfont/iconfont.svg
File diff suppressed because it is too large
View File
|
After Width: 66 | Height: 83 | Size: 4.2 KiB |
|
After Width: 1268 | Height: 86 | Size: 26 KiB |
|
After Width: 496 | Height: 84 | Size: 3.8 KiB |
|
After Width: 496 | Height: 84 | Size: 3.0 KiB |
|
After Width: 1024 | Height: 920 | Size: 129 KiB |
|
After Width: 220 | Height: 68 | Size: 6.1 KiB |
|
After Width: 700 | Height: 40 | Size: 6.4 KiB |
|
After Width: 700 | Height: 40 | Size: 5.5 KiB |
|
After Width: 700 | Height: 40 | Size: 6.7 KiB |
|
After Width: 740 | Height: 38 | Size: 5.9 KiB |
|
After Width: 510 | Height: 38 | Size: 4.2 KiB |
|
After Width: 634 | Height: 86 | Size: 2.7 KiB |
|
After Width: 1896 | Height: 91 | Size: 44 KiB |
|
After Width: 200 | Height: 200 | Size: 6.7 KiB |
@ -0,0 +1,116 @@ |
|||||
|
.ball-running-dots { |
||||
|
position: absolute; |
||||
|
top: 0.2rem; |
||||
|
} |
||||
|
.left-dots { |
||||
|
left: 136px; |
||||
|
} |
||||
|
.right-dots { |
||||
|
right: 200px; |
||||
|
} |
||||
|
.ball-running-dots > div { |
||||
|
position: relative; |
||||
|
-webkit-box-sizing: border-box; |
||||
|
-moz-box-sizing: border-box; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
.ball-running-dots { |
||||
|
display: block; |
||||
|
font-size: 0; |
||||
|
color: #316fd9; |
||||
|
} |
||||
|
.ball-running-dots > div { |
||||
|
display: inline-block; |
||||
|
float: none; |
||||
|
background-color: currentColor; |
||||
|
border: 0 solid currentColor; |
||||
|
} |
||||
|
.ball-running-dots { |
||||
|
width: 16px; |
||||
|
height: 16px; |
||||
|
} |
||||
|
.ball-running-dots > div { |
||||
|
position: absolute; |
||||
|
margin-left: 30px; |
||||
|
border-radius: 100%; |
||||
|
} |
||||
|
.left-dots > div { |
||||
|
animation: ball-running-dots-animate 2s linear infinite; |
||||
|
} |
||||
|
.right-dots > div { |
||||
|
animation: ball-running-dots-animate2 2s linear infinite; |
||||
|
} |
||||
|
.ball-running-dots > div:nth-child(1) { |
||||
|
animation-delay: 0s; |
||||
|
} |
||||
|
.ball-running-dots > div:nth-child(2) { |
||||
|
animation-delay: -0.8s; |
||||
|
} |
||||
|
.ball-running-dots > div:nth-child(3) { |
||||
|
animation-delay: -1.2s; |
||||
|
} |
||||
|
.ball-running-dots > div:nth-child(4) { |
||||
|
animation-delay: -1.6s; |
||||
|
} |
||||
|
@keyframes ball-running-dots-animate { |
||||
|
0%{ |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
transform: translateY(0) translateX(-500%) scale(-.1); |
||||
|
opacity: 0.5; |
||||
|
} |
||||
|
80% { |
||||
|
transform: translateY(0) translateX(0); |
||||
|
} |
||||
|
85% { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
transform: translateY(-125%) translateX(0); |
||||
|
} |
||||
|
90% { |
||||
|
width: 200%; |
||||
|
height: 75%; |
||||
|
} |
||||
|
95% { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
transform: translateY(-100%) translateX(-500%); |
||||
|
} |
||||
|
100% { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
transform: translateY(0) translateX(-500%); |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
@keyframes ball-running-dots-animate2 { |
||||
|
0%{ |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
transform: translateY(0) translateX(500%) scale(-.1); |
||||
|
opacity: 0.5; |
||||
|
} |
||||
|
80% { |
||||
|
transform: translateY(0) translateX(0); |
||||
|
} |
||||
|
85% { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
transform: translateY(-125%) translateX(0); |
||||
|
} |
||||
|
90% { |
||||
|
width: 200%; |
||||
|
height: 75%; |
||||
|
} |
||||
|
95% { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
transform: translateY(-100%) translateX(500%); |
||||
|
} |
||||
|
100% { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
transform: translateY(0) translateX(500%); |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
.chartNum{ |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
margin-top: 0.25rem; |
||||
|
} |
||||
|
.box-items { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
height: 0.6rem; |
||||
|
text-align: center; |
||||
|
} |
||||
|
/*滚动数字设置*/ |
||||
|
.number-item { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
text-align: center; |
||||
|
width: 0.45rem; |
||||
|
height: 0.6rem; |
||||
|
line-height: 0.6rem; |
||||
|
margin-right: 0.125rem; |
||||
|
border: 1px solid #2C75E0; |
||||
|
} |
||||
|
.number-item span{ |
||||
|
position: relative; |
||||
|
display: inline-block; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
writing-mode: vertical-rl; |
||||
|
text-orientation: upright; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
.number-item span i{ |
||||
|
font-style: normal; |
||||
|
position: absolute; |
||||
|
top: 0.1rem; |
||||
|
left: 50%; |
||||
|
font-size: 0.325rem; |
||||
|
font-weight: bold; |
||||
|
color: #1AC9FF; |
||||
|
transform: translate(-50%,0); |
||||
|
transition: transform 1s ease-in-out; |
||||
|
letter-spacing: 10px; |
||||
|
} |
||||
|
.number-item:last-child { |
||||
|
margin-right: 0; |
||||
|
} |
||||
@ -0,0 +1,710 @@ |
|||||
|
#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; |
||||
|
} |
||||
|
|
||||
|
// 头部主题标题 |
||||
|
.header-container{ |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
height: 1.125rem; |
||||
|
font-size: 0.3rem; |
||||
|
color: #fff; |
||||
|
&::before, |
||||
|
&::after{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
width: 6.2rem; |
||||
|
height: 1.05rem; |
||||
|
} |
||||
|
&::before{ |
||||
|
left: 0; |
||||
|
background: url('~@/assets/images/header-left.png') no-repeat top center; |
||||
|
} |
||||
|
&::after{ |
||||
|
right: 0; |
||||
|
background: url('~@/assets/images/header-right.png') no-repeat top center; |
||||
|
} |
||||
|
.header-title{ |
||||
|
position: relative; |
||||
|
width: 15.85rem; |
||||
|
height: 1.075rem; |
||||
|
margin: 0 auto; |
||||
|
background: url('~@/assets/images/header-bg.png') no-repeat top center; |
||||
|
background-size: cover; |
||||
|
h2{ |
||||
|
font-size: 0.6rem; |
||||
|
font-family: "ZhenyanGB"; |
||||
|
font-weight: normal; |
||||
|
line-height: 1.125rem; |
||||
|
text-align: center; |
||||
|
letter-spacing: 0.2rem; |
||||
|
background: linear-gradient(180deg, #FFFFFF 0%, #1AC9FF 100%); |
||||
|
background-clip: text; |
||||
|
color: transparent; |
||||
|
} |
||||
|
} |
||||
|
.header-text{ |
||||
|
position: absolute; |
||||
|
top: 0.65rem; |
||||
|
} |
||||
|
.header-weather { |
||||
|
left: 0.6rem; |
||||
|
} |
||||
|
.header-date { |
||||
|
right: 0.375rem; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 头部动态效果 |
||||
|
.line1, |
||||
|
.line2{ |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
} |
||||
|
|
||||
|
.line1{ |
||||
|
right: 632px; |
||||
|
transform: rotateY(-180deg); |
||||
|
} |
||||
|
|
||||
|
.line2{ |
||||
|
left: 632px; |
||||
|
} |
||||
|
|
||||
|
.top-line1, |
||||
|
.top-line2{ |
||||
|
display: block; |
||||
|
width: 7.925rem; |
||||
|
height: 1.075rem; |
||||
|
animation: topline 4s linear infinite both; |
||||
|
} |
||||
|
|
||||
|
.top-line1{ |
||||
|
background: url('~@/assets/images/top-line.png') no-repeat; |
||||
|
} |
||||
|
|
||||
|
.top-line2{ |
||||
|
background: url('~@/assets/images/top-line.png') no-repeat; |
||||
|
} |
||||
|
|
||||
|
@keyframes topline { |
||||
|
from { |
||||
|
width: 0; |
||||
|
} |
||||
|
50%, |
||||
|
to { |
||||
|
width: 643px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 主题内部部分 |
||||
|
.screen-main{ |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
height: calc(100% - 1.38rem); |
||||
|
margin: 0.0625rem 0 0.5rem 0; |
||||
|
padding: 0 0.325rem; |
||||
|
overflow: hidden; |
||||
|
.screen-left, |
||||
|
.screen-right{ |
||||
|
width: 6.375rem; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
.screen-middle{ |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: space-between; |
||||
|
margin: 0 0.5rem; |
||||
|
} |
||||
|
.screen-item{ |
||||
|
margin-top: 0.25rem; |
||||
|
} |
||||
|
.common-title{ |
||||
|
width: 100%; |
||||
|
height: 0.475rem; |
||||
|
line-height: 0.475rem; |
||||
|
padding-left: 0.525rem; |
||||
|
font-size: 0.325rem; |
||||
|
background: url('~@/assets/images/title.png') no-repeat center center; |
||||
|
background-size: cover; |
||||
|
font-family: 'YouSheBiaoTiHei'; |
||||
|
letter-spacing: 0.1rem; |
||||
|
} |
||||
|
.module-content{ |
||||
|
border: 1px solid #2C75E0; |
||||
|
box-shadow: inset 0px 0px 38px 1px rgba(44,117,224,0.5); |
||||
|
} |
||||
|
.small-module{ |
||||
|
height: 3.75rem; |
||||
|
} |
||||
|
.medium-module{ |
||||
|
height: 4.25rem; |
||||
|
} |
||||
|
.big-module{ |
||||
|
// height: 6.8125rem; |
||||
|
} |
||||
|
|
||||
|
//排行榜-推荐 - list |
||||
|
.lending-ranking{ |
||||
|
flex: 1; |
||||
|
.module-content{ |
||||
|
padding: 0 0.25rem; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
} |
||||
|
.book-list-item{ |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 0.1rem 0.25rem; |
||||
|
.book-img{ |
||||
|
width: 0.825rem; |
||||
|
height: 1.075rem; |
||||
|
margin-right: 0.35rem; |
||||
|
background: url('~@/assets/images/default-img.png') no-repeat center center; |
||||
|
background-size: contain; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
overflow: hidden; |
||||
|
img{ |
||||
|
display: block; |
||||
|
width: 100%; |
||||
|
// height: 100%; |
||||
|
} |
||||
|
} |
||||
|
.book-info{ |
||||
|
flex: 1; |
||||
|
margin-right: 0.35rem; |
||||
|
h4{ |
||||
|
font-size: 0.25rem; |
||||
|
font-weight: normal; |
||||
|
} |
||||
|
p{ |
||||
|
font-size: 0.2rem; |
||||
|
} |
||||
|
} |
||||
|
.ranking-num{ |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
.icon{ |
||||
|
width: 0.5625rem; |
||||
|
height: 0.5625rem; |
||||
|
margin-right: 0.175rem; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 进出馆人数 |
||||
|
.accessToLib-container{ |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
text-align: center; |
||||
|
align-items: center; |
||||
|
margin: 0.375rem 0 .25rem 0; |
||||
|
.accessToLib-item{ |
||||
|
width: 2.75rem; |
||||
|
.panel-num{ |
||||
|
display: block; |
||||
|
height: 0.85rem; |
||||
|
margin-top: 0.125rem; |
||||
|
line-height: 0.85rem; |
||||
|
font-size: 0.325rem; |
||||
|
font-weight: bold; |
||||
|
text-align: center; |
||||
|
color: #1AC9FF; |
||||
|
background: url('~@/assets/images/num-img.png') no-repeat center center; |
||||
|
background-size: cover; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 视频 |
||||
|
.video-box{ |
||||
|
width: 100%; |
||||
|
height: 5.15rem; |
||||
|
} |
||||
|
|
||||
|
// 阅读之星 |
||||
|
.read-star{ |
||||
|
flex: 1; |
||||
|
.common-title{ |
||||
|
background: url('~@/assets/images/title-big.png') no-repeat center center; |
||||
|
background-size: cover; |
||||
|
} |
||||
|
.module-content{ |
||||
|
padding: .25rem; |
||||
|
overflow: hidden; |
||||
|
.readstart-item{ |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
height: 0.5rem; |
||||
|
padding: 0 0.25rem; |
||||
|
font-size: 0.2rem; |
||||
|
margin-bottom: 0.125rem; |
||||
|
background: linear-gradient(90deg, #0C2157 0%, rgba(44,117,224,0) 51%, #0C2157 100%); |
||||
|
.icon-star-1{ |
||||
|
width: 0.35rem; |
||||
|
height: 0.3rem; |
||||
|
margin-left: -0.05rem; |
||||
|
margin-right: 0.2rem; |
||||
|
} |
||||
|
.icon-star-2{ |
||||
|
width: 0.375rem; |
||||
|
height: 0.3rem; |
||||
|
margin-left: -0.0625rem; |
||||
|
margin-right: 0.2rem; |
||||
|
} |
||||
|
.icon-star-3{ |
||||
|
width: 0.275rem; |
||||
|
height: 0.3rem; |
||||
|
margin-right: 0.25rem; |
||||
|
} |
||||
|
.star-num{ |
||||
|
display: block; |
||||
|
width: 0.25rem; |
||||
|
height: 0.25rem; |
||||
|
margin-right: 0.25rem; |
||||
|
font-size: 0.2rem; |
||||
|
line-height: 0.25rem; |
||||
|
text-align: center; |
||||
|
border-radius: 50%; |
||||
|
color: #1AC9FF; |
||||
|
border: 1px solid #1AC9FF; |
||||
|
} |
||||
|
.star-info{ |
||||
|
flex: 1; |
||||
|
margin-right: 0.25rem; |
||||
|
} |
||||
|
&.star1-bg{ |
||||
|
background: url('~@/assets/images/star-1.png') no-repeat left top; |
||||
|
background-size: contain; |
||||
|
} |
||||
|
&.star2-bg{ |
||||
|
background: url('~@/assets/images/star-2.png') no-repeat left top; |
||||
|
background-size: contain; |
||||
|
} |
||||
|
&.star3-bg{ |
||||
|
background: url('~@/assets/images/star-3.png') no-repeat left top; |
||||
|
background-size: contain; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 通知公告 |
||||
|
// .notice{ |
||||
|
// .module-content{ |
||||
|
// display: flex; |
||||
|
// justify-content: space-between; |
||||
|
// width: 100%; |
||||
|
// padding: 0.25rem 0.375rem 0.5rem 0.375rem; |
||||
|
// .notice-icon-gif{ |
||||
|
// display: block; |
||||
|
// width: 2.375rem; |
||||
|
// height: 2.875rem; |
||||
|
// background: url('~@/assets/images/notice.gif') no-repeat left top; |
||||
|
// background-size: contain; |
||||
|
// margin-right: 0.15rem; |
||||
|
// } |
||||
|
// .seamless-warp{ |
||||
|
// width: calc(100vw); |
||||
|
// height: 3.1rem; |
||||
|
// overflow: hidden; |
||||
|
// .notice-txt { |
||||
|
// animation: myMove 20s linear infinite; |
||||
|
// animation-fill-mode: forwards; |
||||
|
// } |
||||
|
// @keyframes myMove { |
||||
|
// 0% { |
||||
|
// transform: translateY(2rem); |
||||
|
// } |
||||
|
// 100% { |
||||
|
// transform: translateY(-6rem); |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// 通知公告 |
||||
|
.notice{ |
||||
|
.module-content{ |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
width: 100%; |
||||
|
padding: 0.25rem 0.375rem 0.5rem 0.375rem; |
||||
|
.notice-icon-gif{ |
||||
|
display: block; |
||||
|
width: 2.375rem; |
||||
|
height: 2.875rem; |
||||
|
background: url('~@/assets/images/notice.gif') no-repeat left top; |
||||
|
background-size: contain; |
||||
|
margin-right: 0.15rem; |
||||
|
} |
||||
|
.seamless-warp{ |
||||
|
width: calc(100vw); |
||||
|
height: 3.1rem; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ===== 客流量大屏布局 ===== |
||||
|
// 单栏新布局,叠加在 .screen-main 基础上,不改动原有三栏规则 |
||||
|
.traffic-main{ |
||||
|
flex-direction: column; |
||||
|
justify-content: flex-start; |
||||
|
gap: 0.3rem; |
||||
|
padding: 0.25rem; |
||||
|
} |
||||
|
|
||||
|
// 顶部分类标签 |
||||
|
.traffic-section-title{ |
||||
|
height: 0.55rem; |
||||
|
line-height: 0.55rem; |
||||
|
font-size: 0.375rem; |
||||
|
background: url('~@/assets/images/title-big.png') no-repeat center center; |
||||
|
background-size: cover; |
||||
|
} |
||||
|
|
||||
|
// 行容器 |
||||
|
.traffic-row{ |
||||
|
display: flex; |
||||
|
} |
||||
|
.traffic-row-3{ |
||||
|
flex: 1; |
||||
|
gap: 0.25rem; |
||||
|
min-height: 0; |
||||
|
} |
||||
|
|
||||
|
// 卡片:参考 HTML 的 card 样式,角标 + 顶部渐变线 |
||||
|
.traffic-card{ |
||||
|
position: relative; |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
min-width: 0; |
||||
|
background: rgba(0, 18, 52, 0.45); |
||||
|
border: 1px solid rgba(26, 201, 255, 0.22); |
||||
|
border-radius: 0.2rem; |
||||
|
box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.06); |
||||
|
overflow: hidden; |
||||
|
&::before{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 2px; |
||||
|
background: linear-gradient(90deg, transparent, #1AC9FF, transparent); |
||||
|
opacity: 0.6; |
||||
|
z-index: 2; |
||||
|
} |
||||
|
.card-corner{ |
||||
|
position: absolute; |
||||
|
width: 0.175rem; |
||||
|
height: 0.175rem; |
||||
|
border: 2px solid #1AC9FF; |
||||
|
opacity: 0.8; |
||||
|
z-index: 2; |
||||
|
&.tl{ top: -1px; left: -1px; border-right: none; border-bottom: none; } |
||||
|
&.tr{ top: -1px; right: -1px; border-left: none; border-bottom: none; } |
||||
|
&.bl{ bottom: -1px; left: -1px; border-right: none; border-top: none; } |
||||
|
&.br{ bottom: -1px; right: -1px; border-left: none; border-top: none; } |
||||
|
} |
||||
|
.card-header{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
padding: 0.2rem 0.275rem 0; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
.card-label{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 0.15rem; |
||||
|
font-size: 0.3rem; |
||||
|
font-weight: 600; |
||||
|
color: #f1f5f9; |
||||
|
&::before{ |
||||
|
content: ""; |
||||
|
width: 0.075rem; |
||||
|
height: 0.3rem; |
||||
|
background: #1AC9FF; |
||||
|
border-radius: 3px; |
||||
|
box-shadow: 0 0 0.1rem #1AC9FF; |
||||
|
} |
||||
|
} |
||||
|
.card-date{ |
||||
|
font-size: 0.225rem; |
||||
|
color: #64748b; |
||||
|
font-variant-numeric: tabular-nums; |
||||
|
} |
||||
|
.module-content{ |
||||
|
flex: 1; |
||||
|
overflow: hidden; |
||||
|
padding: 0.15rem 0.275rem 0.275rem; |
||||
|
border: none; |
||||
|
box-shadow: none; |
||||
|
} |
||||
|
.year-content{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 折线图容器 |
||||
|
.chart-box{ |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
min-height: 2rem; |
||||
|
} |
||||
|
|
||||
|
// 当日进馆 hero 卡片:特殊背景 + 发光边框 + 数字脉冲(仿参考页) |
||||
|
.card-hero{ |
||||
|
background: |
||||
|
radial-gradient(130% 110% at 50% 0%, rgba(26, 201, 255, 0.22) 0%, rgba(26, 201, 255, 0.05) 42%, transparent 72%), |
||||
|
linear-gradient(160deg, #042a52 0%, #021a36 58%, #020f24 100%); |
||||
|
border: 1px solid rgba(26, 201, 255, 0.55); |
||||
|
box-shadow: |
||||
|
0 0 0.3rem rgba(26, 201, 255, 0.28), |
||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.08); |
||||
|
&::before{ |
||||
|
height: 3px; |
||||
|
opacity: 1; |
||||
|
background: linear-gradient(90deg, transparent, #1AC9FF, #7dd3fc, #1AC9FF, transparent); |
||||
|
} |
||||
|
.card-corner{ |
||||
|
border-color: #1AC9FF; |
||||
|
opacity: 1; |
||||
|
width: 0.2rem; |
||||
|
height: 0.2rem; |
||||
|
} |
||||
|
.card-label::before{ |
||||
|
box-shadow: 0 0 0.14rem #1AC9FF; |
||||
|
} |
||||
|
.stat-number{ |
||||
|
text-shadow: 0 0 0.55rem rgba(26, 201, 255, 0.65); |
||||
|
animation: heroPulse 2.6s ease-in-out infinite; |
||||
|
} |
||||
|
} |
||||
|
@keyframes heroPulse{ |
||||
|
0%, 100% { text-shadow: 0 0 0.42rem rgba(26, 201, 255, 0.42); } |
||||
|
50% { text-shadow: 0 0 0.62rem rgba(26, 201, 255, 0.82); } |
||||
|
} |
||||
|
|
||||
|
// 当日进馆 - 数字卡片 |
||||
|
.stat-card{ |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
height: 100%; |
||||
|
text-align: center; |
||||
|
.live-row{ |
||||
|
margin-bottom: 0.225rem; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
.live-text{ font-size: 0.275rem; color: #94a3b8; } |
||||
|
} |
||||
|
.stat-number{ |
||||
|
font-size: 1.5rem; |
||||
|
font-weight: 600; |
||||
|
line-height: 1; |
||||
|
font-variant-numeric: tabular-nums; |
||||
|
background: linear-gradient(180deg, #fff 0%, #7dd3fc 60%, #1AC9FF 100%); |
||||
|
-webkit-background-clip: text; |
||||
|
-webkit-text-fill-color: transparent; |
||||
|
background-clip: text; |
||||
|
text-shadow: 0 0 0.5rem rgba(26, 201, 255, 0.25); |
||||
|
margin-bottom: 0.15rem; |
||||
|
} |
||||
|
.stat-unit{ |
||||
|
font-size: 0.3rem; |
||||
|
color: #94a3b8; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.live-dot{ |
||||
|
display: inline-block; |
||||
|
width: 0.15rem; |
||||
|
height: 0.15rem; |
||||
|
background: #34d399; |
||||
|
border-radius: 50%; |
||||
|
box-shadow: 0 0 0.125rem #34d399; |
||||
|
margin-right: 0.1rem; |
||||
|
animation: livePulse 1.4s infinite; |
||||
|
} |
||||
|
@keyframes livePulse{ |
||||
|
0%, 100%{ opacity: 1; transform: scale(1); } |
||||
|
50%{ opacity: 0.6; transform: scale(1.3); } |
||||
|
} |
||||
|
|
||||
|
// 汇总行 |
||||
|
.traffic-summary{ |
||||
|
flex: 1.1; |
||||
|
min-height: 0; |
||||
|
.traffic-summary-inner{ |
||||
|
position: relative; |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
width: 100%; |
||||
|
background: rgba(0, 18, 52, 0.45); |
||||
|
border: 1px solid rgba(26, 201, 255, 0.22); |
||||
|
border-radius: 0.2rem; |
||||
|
box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.06); |
||||
|
overflow: hidden; |
||||
|
&::before{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 2px; |
||||
|
background: linear-gradient(90deg, transparent, #1AC9FF, transparent); |
||||
|
opacity: 0.6; |
||||
|
z-index: 2; |
||||
|
} |
||||
|
.card-corner{ |
||||
|
position: absolute; |
||||
|
width: 0.175rem; |
||||
|
height: 0.175rem; |
||||
|
border: 2px solid #1AC9FF; |
||||
|
opacity: 0.8; |
||||
|
z-index: 2; |
||||
|
&.tl{ top: -1px; left: -1px; border-right: none; border-bottom: none; } |
||||
|
&.tr{ top: -1px; right: -1px; border-left: none; border-bottom: none; } |
||||
|
&.bl{ bottom: -1px; left: -1px; border-right: none; border-top: none; } |
||||
|
&.br{ bottom: -1px; right: -1px; border-left: none; border-top: none; } |
||||
|
} |
||||
|
} |
||||
|
.module-content{ |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
overflow: hidden; |
||||
|
border: none; |
||||
|
box-shadow: none; |
||||
|
padding: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 累计汇总 - 三栏 |
||||
|
.cumulative-card{ |
||||
|
display: grid; |
||||
|
grid-template-columns: 4.25rem 1fr 3.25rem; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
padding: 0.35rem 0.45rem; |
||||
|
gap: 0.4rem; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.cumulative-left{ |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
gap: 0.2rem; |
||||
|
// padding-right: 0.4rem; |
||||
|
border-right: 1px solid rgba(26, 201, 255, 0.22); |
||||
|
.cumulative-title{ font-size: 0.5rem; font-weight: 700; color: #f1f5f9; } |
||||
|
.cumulative-subtitle{ font-size: 0.24rem; color: #94a3b8; line-height: 1.5; } |
||||
|
} |
||||
|
.mini-bars{ |
||||
|
display: flex; |
||||
|
align-items: flex-end; |
||||
|
gap: 0.2rem; |
||||
|
margin-top: 0.125rem; |
||||
|
.mini-item{ display: flex; flex-direction: column; align-items: center; gap: 0.1rem; } |
||||
|
.mini-col{ height: 1.3rem; display: flex; align-items: flex-end; } |
||||
|
.mini-bar{ |
||||
|
width: 0.375rem; |
||||
|
border-radius: 4px 4px 0 0; |
||||
|
background: linear-gradient(180deg, #1AC9FF 0%, rgba(26, 201, 255, 0.15) 100%); |
||||
|
box-shadow: 0 0 0.125rem rgba(26, 201, 255, 0.25); |
||||
|
} |
||||
|
.mini-label{ font-size: 0.15rem; color: #64748b; } |
||||
|
} |
||||
|
.cumulative-right{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
gap: 0.2rem; |
||||
|
position: relative; |
||||
|
&::before{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
width: 6.5rem; |
||||
|
height: 3.25rem; |
||||
|
background: radial-gradient(ellipse at center, rgba(26, 201, 255, 0.12) 0%, transparent 70%); |
||||
|
pointer-events: none; |
||||
|
} |
||||
|
.digit-box{ |
||||
|
width: 1.5rem; |
||||
|
height: 2.05rem; |
||||
|
background: linear-gradient(180deg, rgba(26, 201, 255, 0.22) 0%, rgba(26, 201, 255, 0.06) 100%); |
||||
|
border: 1px solid rgba(26, 201, 255, 0.22); |
||||
|
border-radius: 0.125rem; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
font-size: 1.35rem; |
||||
|
font-weight: 800; |
||||
|
font-variant-numeric: tabular-nums; |
||||
|
color: #f1f5f9; |
||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 4px 0.25rem rgba(0, 0, 0, 0.25); |
||||
|
text-shadow: 0 0 0.25rem rgba(26, 201, 255, 0.35); |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
&::after{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
height: 1px; |
||||
|
background: rgba(0, 0, 0, 0.25); |
||||
|
z-index: 2; |
||||
|
} |
||||
|
// 翻牌滚动列表 |
||||
|
.digit-roller{ |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
transition: transform 1.4s cubic-bezier(0.22, 1, 0.36, 1); |
||||
|
.digit-cell{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 2.05rem; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.cumulative-illustration{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding-left: 0.25rem; |
||||
|
svg{ |
||||
|
width: 3rem; |
||||
|
height: 3rem; |
||||
|
filter: drop-shadow(0 0 0.3rem rgba(26, 201, 255, 0.3)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,93 @@ |
|||||
|
@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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
img[src=""],img:not([src]){ |
||||
|
opacity: 0; |
||||
|
border:none; |
||||
|
visibility: hidden; |
||||
|
max-width: none; |
||||
|
} |
||||
|
|
||||
|
.clearfix { |
||||
|
&::after { |
||||
|
content: ""; |
||||
|
display: table; |
||||
|
height: 0; |
||||
|
line-height: 0; |
||||
|
visibility: hidden; |
||||
|
clear: both; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pre{ |
||||
|
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; |
||||
|
word-wrap: break-word; |
||||
|
white-space: pre-wrap; |
||||
|
} |
||||
|
|
||||
|
/**滚动条的宽度*/ |
||||
|
::-webkit-scrollbar { |
||||
|
width: 4px; |
||||
|
height: 4px; |
||||
|
} |
||||
|
|
||||
|
//滚动条的滑块 |
||||
|
::-webkit-scrollbar-thumb { |
||||
|
background-color: #13439E; |
||||
|
border-radius: 3px; |
||||
|
} |
||||
|
|
||||
|
.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; |
||||
|
} |
||||
@ -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'] = {})) |
||||
@ -0,0 +1,49 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import App from './App.vue' |
||||
|
import router from './router' |
||||
|
import store from './store' |
||||
|
|
||||
|
import ElementUI from 'element-ui' |
||||
|
import 'element-ui/lib/theme-chalk/index.css' |
||||
|
Vue.use(ElementUI) |
||||
|
|
||||
|
// 适配flex
|
||||
|
import '@/common/flexible.js' |
||||
|
|
||||
|
// 引入全局css
|
||||
|
import './assets/styles/style.scss' |
||||
|
import './assets/iconfont/iconfont.js' |
||||
|
import './assets/fonts/fonts.css' |
||||
|
|
||||
|
// 按需引入然后注册在vue原型上
|
||||
|
import { Message } from 'element-ui' |
||||
|
Vue.prototype.$message = Message |
||||
|
|
||||
|
// 全局注册过滤 - 时间
|
||||
|
import { parseTime, getFormattedDate } from '@/utils/index.js' |
||||
|
Vue.filter('parseTime', function(time, cFormat) { |
||||
|
return parseTime(time, cFormat) |
||||
|
}) |
||||
|
Vue.prototype.getFormattedDate = getFormattedDate |
||||
|
|
||||
|
// 馆代码 1201为东西湖馆代码 / 本地测试用FTZN
|
||||
|
// Vue.prototype.libcode = 'FTZN'
|
||||
|
Vue.prototype.libcode = '1201' |
||||
|
|
||||
|
import axios from 'axios' |
||||
|
Vue.prototype.$axios = axios |
||||
|
|
||||
|
// 引入echart
|
||||
|
import echarts from 'echarts' |
||||
|
Vue.prototype.$echarts = echarts |
||||
|
|
||||
|
import scroll from 'vue-seamless-scroll' |
||||
|
Vue.use(scroll) |
||||
|
|
||||
|
Vue.config.productionTip = false |
||||
|
Vue.component(Message.name, Message) |
||||
|
new Vue({ |
||||
|
router, |
||||
|
store, |
||||
|
render: (h) => h(App) |
||||
|
}).$mount('#app') |
||||
@ -0,0 +1,22 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import VueRouter from 'vue-router' |
||||
|
|
||||
|
Vue.use(VueRouter) |
||||
|
|
||||
|
const routes = [ |
||||
|
{ |
||||
|
path: '/', |
||||
|
name: 'index', |
||||
|
component: () => import('../views/index.vue'), |
||||
|
meta: { |
||||
|
title: '人流数据大屏' |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
const router = new VueRouter({ |
||||
|
mode: 'hash', |
||||
|
routes |
||||
|
}) |
||||
|
|
||||
|
export default router |
||||
@ -0,0 +1,15 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import Vuex from 'vuex' |
||||
|
|
||||
|
Vue.use(Vuex) |
||||
|
|
||||
|
export default new Vuex.Store({ |
||||
|
state: { |
||||
|
}, |
||||
|
mutations: { |
||||
|
}, |
||||
|
actions: { |
||||
|
}, |
||||
|
modules: { |
||||
|
} |
||||
|
}) |
||||
@ -0,0 +1,100 @@ |
|||||
|
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 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 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 |
||||
|
} |
||||
|
|
||||
|
// 获取当前日期时间
|
||||
|
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 |
||||
|
} |
||||
|
|
||||
|
export function getFormattedDate(date, yearOffset = 0) { |
||||
|
date.setFullYear(date.getFullYear() + yearOffset) |
||||
|
const year = date.getFullYear() |
||||
|
const month = (date.getMonth() + 1).toString().padStart(2, '0') |
||||
|
const day = date.getDate().toString().padStart(2, '0') |
||||
|
return `${year}-${month}-${day}` |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
import axios from 'axios' |
||||
|
// import { Message } from 'element-ui'
|
||||
|
|
||||
|
// 创建axios实例
|
||||
|
// 生产环境 baseURL 走 public/static/config.js 的 window.g.ApiUrl,部署后改 config 即可切换后端,无需重新打包
|
||||
|
const service = axios.create({ |
||||
|
baseURL: process.env.NODE_ENV === 'production' ? ((window.g && window.g.ApiUrl) || '') : '/', |
||||
|
timeout: (window.g && window.g.AXIOS_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 => { |
||||
|
return response.data |
||||
|
}, |
||||
|
error => { |
||||
|
return Promise.reject(error) |
||||
|
} |
||||
|
) |
||||
|
export default service |
||||
@ -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) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
<template> |
||||
|
<div class="header-container"> |
||||
|
<div class="header-title"> |
||||
|
<div class="line1"> |
||||
|
<span class="top-line1" /> |
||||
|
</div> |
||||
|
<div class="ball-running-dots left-dots"> |
||||
|
<div /> |
||||
|
<div /> |
||||
|
<div /> |
||||
|
<div /> |
||||
|
</div> |
||||
|
<h2>{{ headerName }}</h2> |
||||
|
<div class="ball-running-dots right-dots"> |
||||
|
<div /> |
||||
|
<div /> |
||||
|
<div /> |
||||
|
<div /> |
||||
|
</div> |
||||
|
<div class="line2"> |
||||
|
<span class="top-line2" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 天气api: https://www.tianqi.com/plugin --> |
||||
|
<!-- <div class="header-text header-weather"> |
||||
|
<iframe id="weather" width="400" scrolling="no" height="30" frameborder="0" allowtransparency="true" src="https://i.tianqi.com?c=code&id=34&color=%23FFFFFF&icon=1&py=wuhan&site=18" /> |
||||
|
</div> --> |
||||
|
<div class="header-text header-date">{{ nowDate }}</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getCurrentTime } from '@/utils/index' |
||||
|
export default { |
||||
|
name: 'Header', |
||||
|
data() { |
||||
|
return { |
||||
|
nowDate: '', |
||||
|
branches: [] |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
// 根据 URL query 的 doorCodes 查 branches.json,展示对应网点名称 |
||||
|
headerName() { |
||||
|
const code = (this.$route.query && this.$route.query.doorCodes) || '' |
||||
|
const hit = this.branches.find((b) => b.code === code) |
||||
|
return hit ? hit.name : '' |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
this.timer = setInterval(() => { |
||||
|
this.nowDate = getCurrentTime() |
||||
|
}, 1000) |
||||
|
fetch(`${process.env.BASE_URL}static/branches.json`) |
||||
|
.then((r) => r.json()) |
||||
|
.then((res) => { |
||||
|
this.branches = res.branches || [] |
||||
|
}) |
||||
|
.catch((err) => { |
||||
|
console.error('加载 branches.json 失败', err) |
||||
|
}) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
if (this.timer) { |
||||
|
clearInterval(this.timer) |
||||
|
this.timer = null |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
@import "~@/assets/styles/index.scss"; |
||||
|
@import "~@/assets/styles/ball-running-animate.css"; |
||||
|
</style> |
||||
@ -0,0 +1,26 @@ |
|||||
|
<template> |
||||
|
<div id="screen-container"> |
||||
|
<Header /> |
||||
|
<TrafficFlow /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import Header from '@/views/header/index.vue' |
||||
|
import TrafficFlow from '@/views/trafficFlow/index.vue' |
||||
|
export default { |
||||
|
name: 'Home', |
||||
|
components: { |
||||
|
Header, |
||||
|
TrafficFlow |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
@import "~@/assets/styles/index.scss"; |
||||
|
</style> |
||||
@ -0,0 +1,29 @@ |
|||||
|
<template> |
||||
|
<div class="stat-card"> |
||||
|
<div class="live-row"> |
||||
|
<span class="live-dot" /> |
||||
|
<span class="live-text">实时客流</span> |
||||
|
</div> |
||||
|
<count-to class="stat-number" :start-val="0" :end-val="todayTotal" :duration="3200" separator="," /> |
||||
|
<div class="stat-unit">人次</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import CountTo from 'vue-count-to' |
||||
|
|
||||
|
export default { |
||||
|
name: 'DayInFlowCard', |
||||
|
components: { CountTo }, |
||||
|
props: { |
||||
|
todayTotal: { |
||||
|
type: Number, |
||||
|
default: 0 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
@import "~@/assets/styles/index.scss"; |
||||
|
</style> |
||||
@ -0,0 +1,159 @@ |
|||||
|
<template> |
||||
|
<div ref="chart" class="chart-box" /> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { debounce } from '@/utils/index' |
||||
|
|
||||
|
export default { |
||||
|
name: 'MonthInFlowChart', |
||||
|
props: { |
||||
|
monthData: { |
||||
|
type: Array, |
||||
|
default: () => [] |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
chart: null, |
||||
|
ro: null |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
xAxis() { |
||||
|
return this.monthData.map((i) => i.day) |
||||
|
}, |
||||
|
series() { |
||||
|
return this.monthData.map((i) => i.count || 0) |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
monthData() { |
||||
|
this.setOption() |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.$nextTick(() => this.initChart()) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
this.stopTipLoop() |
||||
|
if (this.ro) { |
||||
|
this.ro.disconnect() |
||||
|
this.ro = null |
||||
|
} |
||||
|
if (this.chart) { |
||||
|
this.chart.dispose() |
||||
|
this.chart = null |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
initChart() { |
||||
|
this.chart = this.$echarts.init(this.$refs.chart) |
||||
|
this.setOption() |
||||
|
// 用 ResizeObserver 监听容器自身尺寸变化,比 window.resize 更可靠 |
||||
|
const doResize = debounce(() => { |
||||
|
if (this.chart) this.chart.resize() |
||||
|
}, 100) |
||||
|
this.ro = new ResizeObserver(() => doResize()) |
||||
|
this.ro.observe(this.$refs.chart) |
||||
|
}, |
||||
|
setOption() { |
||||
|
if (!this.chart) return |
||||
|
// 最新数据点:取最后一个有效值(非零优先,否则末尾) |
||||
|
const idx = Math.max(0, this.series.length - 1) |
||||
|
const lastVal = this.series[idx] |
||||
|
this.chart.setOption({ |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
backgroundColor: 'rgba(11, 26, 48, 0.92)', |
||||
|
borderColor: '#FACC15', |
||||
|
textStyle: { color: '#e2e8f0', fontSize: 14 }, |
||||
|
axisPointer: { type: 'line', lineStyle: { color: '#FACC15' }} |
||||
|
}, |
||||
|
grid: { left: 40, right: 20, top: 50, bottom: 30, containLabel: false }, |
||||
|
xAxis: { |
||||
|
type: 'category', |
||||
|
data: this.xAxis, |
||||
|
boundaryGap: false, |
||||
|
axisLine: { lineStyle: { color: 'rgba(250,204,21,0.4)' }}, |
||||
|
axisTick: { show: false }, |
||||
|
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12, interval: 'auto' }, |
||||
|
splitLine: { show: false } |
||||
|
}, |
||||
|
yAxis: { |
||||
|
type: 'value', |
||||
|
min: 0, |
||||
|
max: (v) => v.max === 0 ? 100 : Math.ceil(v.max * 1.25), |
||||
|
axisLine: { show: false }, |
||||
|
axisTick: { show: false }, |
||||
|
axisLabel: { color: 'rgba(255,255,255,0.6)', fontSize: 12 }, |
||||
|
splitLine: { lineStyle: { color: 'rgba(250,204,21,0.12)', type: 'dashed' }} |
||||
|
}, |
||||
|
series: [{ |
||||
|
name: '进馆人次', |
||||
|
type: 'line', |
||||
|
smooth: true, |
||||
|
symbol: 'none', |
||||
|
showSymbol: false, |
||||
|
data: this.series, |
||||
|
lineStyle: { color: '#FACC15', width: 3, shadowColor: '#FACC15', shadowBlur: 12 }, |
||||
|
itemStyle: { color: '#FACC15' }, |
||||
|
areaStyle: { |
||||
|
color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
|
{ offset: 0, color: 'rgba(250,204,21,0.45)' }, |
||||
|
{ offset: 1, color: 'rgba(250,204,21,0.02)' } |
||||
|
]) |
||||
|
}, |
||||
|
// 最新点高亮 + 数值常显(仿参考页 markPoint) |
||||
|
markPoint: { |
||||
|
symbol: 'circle', |
||||
|
symbolSize: 12, |
||||
|
label: { |
||||
|
position: 'top', |
||||
|
distance: 12, |
||||
|
formatter: () => String(lastVal), |
||||
|
fontSize: 26, |
||||
|
fontWeight: 800, |
||||
|
color: '#fff', |
||||
|
textShadowColor: 'rgba(250,204,21,0.8)', |
||||
|
textShadowBlur: 10 |
||||
|
}, |
||||
|
itemStyle: { |
||||
|
color: '#FACC15', |
||||
|
shadowColor: 'rgba(250,204,21,1)', |
||||
|
shadowBlur: 12 |
||||
|
}, |
||||
|
data: [{ coord: [idx, lastVal] }] |
||||
|
} |
||||
|
}] |
||||
|
}) |
||||
|
this.startTipLoop() |
||||
|
}, |
||||
|
// 动态轮播 tooltip,适配大屏无人交互场景 |
||||
|
startTipLoop() { |
||||
|
this.stopTipLoop() |
||||
|
if (!this.series.length) return |
||||
|
this.tipIndex = -1 |
||||
|
this.tipTimer = setInterval(() => { |
||||
|
if (!this.chart || !this.series.length) return |
||||
|
this.tipIndex = (this.tipIndex + 1) % this.series.length |
||||
|
this.chart.dispatchAction({ |
||||
|
type: 'showTip', |
||||
|
seriesIndex: 0, |
||||
|
dataIndex: this.tipIndex |
||||
|
}) |
||||
|
}, 2500) |
||||
|
}, |
||||
|
stopTipLoop() { |
||||
|
if (this.tipTimer) { |
||||
|
clearInterval(this.tipTimer) |
||||
|
this.tipTimer = null |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
@import "~@/assets/styles/index.scss"; |
||||
|
</style> |
||||
@ -0,0 +1,106 @@ |
|||||
|
<template> |
||||
|
<div class="traffic-summary-inner"> |
||||
|
<span class="card-corner tl" /> |
||||
|
<span class="card-corner tr" /> |
||||
|
<span class="card-corner bl" /> |
||||
|
<span class="card-corner br" /> |
||||
|
<div class="module-content"> |
||||
|
<div class="cumulative-card"> |
||||
|
<!-- 左:统计标题 + 副标题 + 近6个月柱状 --> |
||||
|
<div class="cumulative-left"> |
||||
|
<div class="cumulative-title">统计</div> |
||||
|
<div class="cumulative-subtitle">累计客流量汇总</div> |
||||
|
<div class="mini-bars"> |
||||
|
<div v-for="(h, i) in miniBars" :key="i" class="mini-item"> |
||||
|
<div class="mini-col"> |
||||
|
<div class="mini-bar" :style="{ height: h + '%' }" /> |
||||
|
</div> |
||||
|
<div class="mini-label">{{ i + 5 }}月</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 中:数字翻牌 --> |
||||
|
<div class="cumulative-right"> |
||||
|
<span v-for="(d, i) in digitList" :key="i" class="digit-box"> |
||||
|
<count-to :start-val="0" :end-val="Number(d)" :duration="2000" /> |
||||
|
</span> |
||||
|
</div> |
||||
|
<!-- 右:书本插画 --> |
||||
|
<div class="cumulative-illustration" v-html="svgIcon" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import CountTo from 'vue-count-to' |
||||
|
|
||||
|
const SVG_ICON = `<svg viewBox="0 0 240 240" fill="none" xmlns="http://www.w3.org/2000/svg"> |
||||
|
<defs> |
||||
|
<radialGradient id="illuGlow" cx="50%" cy="50%" r="50%"> |
||||
|
<stop offset="0%" stop-color="rgba(26,201,255,0.40)"/> |
||||
|
<stop offset="55%" stop-color="rgba(26,201,255,0.10)"/> |
||||
|
<stop offset="100%" stop-color="rgba(26,201,255,0)"/> |
||||
|
</radialGradient> |
||||
|
<linearGradient id="illuBook" x1="0" y1="0" x2="0" y2="1"> |
||||
|
<stop offset="0%" stop-color="#7dd3fc"/> |
||||
|
<stop offset="100%" stop-color="#0ea5e9"/> |
||||
|
</linearGradient> |
||||
|
<linearGradient id="illuPage" x1="0" y1="0" x2="0" y2="1"> |
||||
|
<stop offset="0%" stop-color="#f0fbff"/> |
||||
|
<stop offset="100%" stop-color="#bae6fd"/> |
||||
|
</linearGradient> |
||||
|
<linearGradient id="illuRibbon" x1="0" y1="0" x2="0" y2="1"> |
||||
|
<stop offset="0%" stop-color="#38bdf8"/> |
||||
|
<stop offset="100%" stop-color="#0369a1"/> |
||||
|
</linearGradient> |
||||
|
</defs> |
||||
|
<circle cx="120" cy="120" r="112" fill="url(#illuGlow)"/> |
||||
|
<circle cx="120" cy="120" r="98" stroke="rgba(26,201,255,0.12)" stroke-width="1.5"/> |
||||
|
<circle cx="120" cy="120" r="86" stroke="rgba(26,201,255,0.20)" stroke-width="1.5" stroke-dasharray="2 8"/> |
||||
|
<path d="M120,172 C94,159 60,157 34,166 L34,176 C60,167 94,169 120,182 C146,169 180,167 206,176 L206,166 C180,157 146,159 120,172 Z" fill="url(#illuBook)"/> |
||||
|
<path d="M120,166 C95,154 62,152 36,160 L36,118 C62,110 95,112 120,126 Z" fill="url(#illuPage)" stroke="#bae6fd" stroke-width="1.5"/> |
||||
|
<path d="M120,166 C145,154 178,152 204,160 L204,118 C178,110 145,112 120,126 Z" fill="url(#illuPage)" stroke="#bae6fd" stroke-width="1.5"/> |
||||
|
<path d="M52,134 C76,128 100,130 116,138" stroke="#38bdf8" stroke-width="1.6" stroke-linecap="round" opacity="0.75"/> |
||||
|
<path d="M52,144 C76,138 100,140 116,148" stroke="#38bdf8" stroke-width="1.6" stroke-linecap="round" opacity="0.5"/> |
||||
|
<path d="M52,154 C76,148 100,150 116,158" stroke="#38bdf8" stroke-width="1.6" stroke-linecap="round" opacity="0.35"/> |
||||
|
<path d="M124,138 C140,130 164,128 188,134" stroke="#38bdf8" stroke-width="1.6" stroke-linecap="round" opacity="0.75"/> |
||||
|
<path d="M124,148 C140,140 164,138 188,144" stroke="#38bdf8" stroke-width="1.6" stroke-linecap="round" opacity="0.5"/> |
||||
|
<path d="M124,158 C140,150 164,148 188,154" stroke="#38bdf8" stroke-width="1.6" stroke-linecap="round" opacity="0.35"/> |
||||
|
<path d="M120,126 L120,172" stroke="#0284c7" stroke-width="2.5" stroke-linecap="round"/> |
||||
|
<path d="M152,114 L152,150 L158,143 L164,150 L164,114 Z" fill="url(#illuRibbon)"/> |
||||
|
<circle cx="202" cy="66" r="3" fill="#bae6fd"/> |
||||
|
<circle cx="40" cy="76" r="2.5" fill="#bae6fd"/> |
||||
|
<circle cx="212" cy="150" r="2" fill="#bae6fd"/> |
||||
|
<circle cx="32" cy="158" r="2" fill="#bae6fd"/> |
||||
|
<path d="M188,94 l3,9 9,3 -9,3 -3,9 -3,-9 -9,-3 9,-3 z" fill="#7dd3fc" opacity="0.9"/> |
||||
|
<path d="M54,102 l2.5,7 7,2.5 -7,2.5 -2.5,7 -2.5,-7 -7,-2.5 7,-2.5 z" fill="#38bdf8" opacity="0.85"/> |
||||
|
</svg>` |
||||
|
|
||||
|
export default { |
||||
|
name: 'TotalFlowSummary', |
||||
|
components: { CountTo }, |
||||
|
props: { |
||||
|
historyTotal: { |
||||
|
type: Number, |
||||
|
default: 0 |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
svgIcon: SVG_ICON, |
||||
|
// 静态示意柱状(仅装饰图示,不绑定数据) |
||||
|
miniBars: [42, 68, 55, 82, 73, 95] |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
digitList() { |
||||
|
return String(this.historyTotal).padStart(8, '0').split('') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
@import "~@/assets/styles/index.scss"; |
||||
|
</style> |
||||
@ -0,0 +1,157 @@ |
|||||
|
<template> |
||||
|
<div ref="chart" class="chart-box" /> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { debounce } from '@/utils/index' |
||||
|
|
||||
|
export default { |
||||
|
name: 'YearInFlowChart', |
||||
|
props: { |
||||
|
yearData: { |
||||
|
type: Array, |
||||
|
default: () => [] |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
chart: null, |
||||
|
ro: null |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
xAxis() { |
||||
|
return this.yearData.map((i) => i.month + '月') |
||||
|
}, |
||||
|
series() { |
||||
|
return this.yearData.map((i) => i.count || 0) |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
yearData() { |
||||
|
this.setOption() |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.$nextTick(() => this.initChart()) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
this.stopTipLoop() |
||||
|
if (this.ro) { |
||||
|
this.ro.disconnect() |
||||
|
this.ro = null |
||||
|
} |
||||
|
if (this.chart) { |
||||
|
this.chart.dispose() |
||||
|
this.chart = null |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
initChart() { |
||||
|
this.chart = this.$echarts.init(this.$refs.chart) |
||||
|
this.setOption() |
||||
|
// 用 ResizeObserver 监听容器自身尺寸变化,比 window.resize 更可靠 |
||||
|
const doResize = debounce(() => { |
||||
|
if (this.chart) this.chart.resize() |
||||
|
}, 100) |
||||
|
this.ro = new ResizeObserver(() => doResize()) |
||||
|
this.ro.observe(this.$refs.chart) |
||||
|
}, |
||||
|
setOption() { |
||||
|
if (!this.chart) return |
||||
|
const idx = Math.max(0, this.series.length - 1) |
||||
|
const lastVal = this.series[idx] |
||||
|
this.chart.setOption({ |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
backgroundColor: 'rgba(11, 26, 48, 0.92)', |
||||
|
borderColor: '#18E3A7', |
||||
|
textStyle: { color: '#e2e8f0', fontSize: 14 }, |
||||
|
axisPointer: { type: 'line', lineStyle: { color: '#18E3A7' }} |
||||
|
}, |
||||
|
grid: { left: 40, right: 20, top: 50, bottom: 30, containLabel: false }, |
||||
|
xAxis: { |
||||
|
type: 'category', |
||||
|
data: this.xAxis, |
||||
|
boundaryGap: false, |
||||
|
axisLine: { lineStyle: { color: 'rgba(24,227,167,0.4)' }}, |
||||
|
axisTick: { show: false }, |
||||
|
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12, interval: 'auto' }, |
||||
|
splitLine: { show: false } |
||||
|
}, |
||||
|
yAxis: { |
||||
|
type: 'value', |
||||
|
min: 0, |
||||
|
max: (v) => v.max === 0 ? 100 : Math.ceil(v.max * 1.25), |
||||
|
axisLine: { show: false }, |
||||
|
axisTick: { show: false }, |
||||
|
axisLabel: { color: 'rgba(255,255,255,0.6)', fontSize: 12 }, |
||||
|
splitLine: { lineStyle: { color: 'rgba(24,227,167,0.12)', type: 'dashed' }} |
||||
|
}, |
||||
|
series: [{ |
||||
|
name: '进馆人次', |
||||
|
type: 'line', |
||||
|
smooth: true, |
||||
|
symbol: 'none', |
||||
|
showSymbol: false, |
||||
|
data: this.series, |
||||
|
lineStyle: { color: '#18E3A7', width: 3, shadowColor: '#18E3A7', shadowBlur: 12 }, |
||||
|
itemStyle: { color: '#18E3A7' }, |
||||
|
areaStyle: { |
||||
|
color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
||||
|
{ offset: 0, color: 'rgba(24,227,167,0.45)' }, |
||||
|
{ offset: 1, color: 'rgba(24,227,167,0.02)' } |
||||
|
]) |
||||
|
}, |
||||
|
markPoint: { |
||||
|
symbol: 'circle', |
||||
|
symbolSize: 12, |
||||
|
label: { |
||||
|
position: 'top', |
||||
|
distance: 12, |
||||
|
formatter: () => String(lastVal), |
||||
|
fontSize: 26, |
||||
|
fontWeight: 800, |
||||
|
color: '#fff', |
||||
|
textShadowColor: 'rgba(24,227,167,0.8)', |
||||
|
textShadowBlur: 10 |
||||
|
}, |
||||
|
itemStyle: { |
||||
|
color: '#18E3A7', |
||||
|
shadowColor: 'rgba(24,227,167,1)', |
||||
|
shadowBlur: 12 |
||||
|
}, |
||||
|
data: [{ coord: [idx, lastVal] }] |
||||
|
} |
||||
|
}] |
||||
|
}) |
||||
|
this.startTipLoop() |
||||
|
}, |
||||
|
// 动态轮播 tooltip,适配大屏无人交互场景 |
||||
|
startTipLoop() { |
||||
|
this.stopTipLoop() |
||||
|
if (!this.series.length) return |
||||
|
this.tipIndex = -1 |
||||
|
this.tipTimer = setInterval(() => { |
||||
|
if (!this.chart || !this.series.length) return |
||||
|
this.tipIndex = (this.tipIndex + 1) % this.series.length |
||||
|
this.chart.dispatchAction({ |
||||
|
type: 'showTip', |
||||
|
seriesIndex: 0, |
||||
|
dataIndex: this.tipIndex |
||||
|
}) |
||||
|
}, 2500) |
||||
|
}, |
||||
|
stopTipLoop() { |
||||
|
if (this.tipTimer) { |
||||
|
clearInterval(this.tipTimer) |
||||
|
this.tipTimer = null |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
@import "~@/assets/styles/index.scss"; |
||||
|
</style> |
||||
@ -0,0 +1,118 @@ |
|||||
|
<template> |
||||
|
<div class="screen-main traffic-main"> |
||||
|
|
||||
|
<!-- 第一行:三列等宽 --> |
||||
|
<div class="traffic-row traffic-row-3"> |
||||
|
<div class="traffic-card card-hero"> |
||||
|
<span class="card-corner tl" /> |
||||
|
<span class="card-corner tr" /> |
||||
|
<span class="card-corner bl" /> |
||||
|
<span class="card-corner br" /> |
||||
|
<div class="card-header"> |
||||
|
<div class="card-label">进馆 · 当日进馆</div> |
||||
|
<div class="card-date">{{ todayDate }}</div> |
||||
|
</div> |
||||
|
<div class="module-content year-content"> |
||||
|
<DayInFlowCard :today-total="todayTotal" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="traffic-card"> |
||||
|
<span class="card-corner tl" /> |
||||
|
<span class="card-corner tr" /> |
||||
|
<span class="card-corner bl" /> |
||||
|
<span class="card-corner br" /> |
||||
|
<div class="card-header"> |
||||
|
<div class="card-label">进馆 · 当月进馆</div> |
||||
|
<div class="card-date">{{ monthDate }}</div> |
||||
|
</div> |
||||
|
<div class="module-content"> |
||||
|
<MonthInFlowChart :month-data="monthData" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="traffic-card"> |
||||
|
<span class="card-corner tl" /> |
||||
|
<span class="card-corner tr" /> |
||||
|
<span class="card-corner bl" /> |
||||
|
<span class="card-corner br" /> |
||||
|
<div class="card-header"> |
||||
|
<div class="card-label">进馆 · 本年进馆</div> |
||||
|
<div class="card-date">{{ yearDate }}</div> |
||||
|
</div> |
||||
|
<div class="module-content"> |
||||
|
<YearInFlowChart :year-data="yearData" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 第二行:全宽汇总 --> |
||||
|
<div class="traffic-row traffic-summary"> |
||||
|
<TotalFlowSummary :history-total="historyTotal" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import DayInFlowCard from './components/DayInFlowCard.vue' |
||||
|
import MonthInFlowChart from './components/MonthInFlowChart.vue' |
||||
|
import YearInFlowChart from './components/YearInFlowChart.vue' |
||||
|
import TotalFlowSummary from './components/TotalFlowSummary.vue' |
||||
|
import { FetchInitIntoNumHS } from '@/api/library' |
||||
|
|
||||
|
export default { |
||||
|
name: 'TrafficFlow', |
||||
|
components: { DayInFlowCard, MonthInFlowChart, YearInFlowChart, TotalFlowSummary }, |
||||
|
data() { |
||||
|
return { |
||||
|
todayDate: '', |
||||
|
monthDate: '', |
||||
|
yearDate: '', |
||||
|
// 统一请求一次的数据 |
||||
|
todayTotal: 0, |
||||
|
monthData: [], |
||||
|
yearData: [], |
||||
|
historyTotal: 0, |
||||
|
timer: null |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
const now = new Date() |
||||
|
const y = now.getFullYear() |
||||
|
const m = String(now.getMonth() + 1).padStart(2, '0') |
||||
|
const d = String(now.getDate()).padStart(2, '0') |
||||
|
this.todayDate = `${y}-${m}-${d}` |
||||
|
this.monthDate = `${y}-${m}` |
||||
|
this.yearDate = `${y}` |
||||
|
this.fetchData() |
||||
|
this.timer = setInterval(() => this.fetchData(), 15000) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
if (this.timer) { |
||||
|
clearInterval(this.timer) |
||||
|
this.timer = null |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
fetchData() { |
||||
|
// doorCodes 从 URL query 获取,如 http://localhost:8080/#/?doorCodes=D3、D4 |
||||
|
const doorCodes = this.$route.query.doorCodes || '' |
||||
|
const params = { |
||||
|
'doorCodes': doorCodes |
||||
|
} |
||||
|
FetchInitIntoNumHS(params).then((res) => { |
||||
|
if (res.errCode === 0 && res.data) { |
||||
|
this.todayTotal = res.data.todayTotal || 0 |
||||
|
this.monthData = res.data.monthData || [] |
||||
|
this.yearData = res.data.yearData || [] |
||||
|
this.historyTotal = res.data.historyTotal || 0 |
||||
|
} |
||||
|
}).catch((err) => { |
||||
|
console.error('客流量接口异常', err) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
@import "~@/assets/styles/index.scss"; |
||||
|
</style> |
||||
@ -0,0 +1,68 @@ |
|||||
|
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: 8080, |
||||
|
open: true, |
||||
|
overlay: { |
||||
|
warnings: false, |
||||
|
errors: true |
||||
|
}, |
||||
|
proxy: { |
||||
|
'/dxhtsg/': { |
||||
|
target: process.env.VUE_APP_BASE_API, |
||||
|
changeOrigin: true, |
||||
|
pathRewrite: { |
||||
|
'^/dxhtsg': 'dxhtsg' |
||||
|
} |
||||
|
}, |
||||
|
'/qyzt/': { |
||||
|
target: process.env.VUE_APP_BASE_API, |
||||
|
changeOrigin: true, |
||||
|
pathRewrite: { |
||||
|
'^/qyzt': 'qyzt' |
||||
|
} |
||||
|
}, |
||||
|
'/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 |
||||
|
}) |
||||
|
} |
||||
|
} |
||||