代码提交
This commit is contained in:
parent
01d8de7f11
commit
0f1740f301
|
@ -1,23 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
**/*.log
|
||||
|
||||
tests/**/coverage/
|
||||
tests/e2e/reports
|
||||
selenium-debug.log
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.local
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
**/*.log
|
||||
|
||||
tests/**/coverage/
|
||||
tests/e2e/reports
|
||||
selenium-debug.log
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.local
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[less]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"i18n-ally.localesPaths": ["src/assets/locales"]
|
||||
}
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[less]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"i18n-ally.localesPaths": ["src/assets/locales"]
|
||||
}
|
||||
|
|
42
LICENSE
42
LICENSE
|
@ -1,21 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 daidai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 daidai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
626
README.md
626
README.md
|
@ -1,313 +1,313 @@
|
|||
|
||||
## 项目描述
|
||||
|
||||
[IofTV-Screen](https://gitee.com/daidaibg/IofTV-Screen/tree/main) 的 Vue3+vite版本,
|
||||
|
||||
### 与vue2版本对比
|
||||
|
||||
#### 功能
|
||||
|
||||
功能采用与vue2版本相同功能
|
||||
|
||||
因为要与vue2版本相同功能,有些组件不兼容vue3版本,例如:胶囊柱图,数字滚动皆重新封装为组件,整体来说,功能属实相同。根据自己需求选择[vue2](#vue2版本地址)版本与[vue3](#本项目地址 vue3+vite)版本
|
||||
|
||||
#### 样式
|
||||
|
||||
进行微调,整体看着更加美观
|
||||
|
||||
|
||||
|
||||
- 项目需要全屏展示(按 F11)。
|
||||
|
||||
- 项目部分区域使用了全局注册方式,增加了打包体积,在实际运用中请使用 **按需引入**。
|
||||
|
||||
- 项目环境:Vite、Echarts、Npm、Node,axios,mock,vue3。
|
||||
|
||||
- 请拉取 master 分支的代码,其余分支是开发分支。
|
||||
|
||||
- 在项目public目录下存放地图数据合集,根据地市编存放。
|
||||
|
||||
|
||||
友情链接:
|
||||
|
||||
1. [Vue 官方文档](https://cn.vuejs.org/)
|
||||
3. [echarts 实例](https://gitee.com/link?target=https%3A%2F%2Fecharts.apache.org%2Fexamples%2Fzh%2Findex.html),[echarts API 文档](https://gitee.com/link?target=https%3A%2F%2Fecharts.apache.org%2Fzh%2Fapi.html%23echarts)
|
||||
4. [mock.js官网](http://mockjs.com/examples.html)
|
||||
5. [axios官网](https://axios-http.com/)
|
||||
|
||||
**项目展示**
|
||||
|
||||

|
||||
|
||||
### 项目预览地址
|
||||
|
||||
[https://www.daidaibg.com/bigscreen-vue3](https://www.daidaibg.com/bigscreen-vue3)
|
||||
|
||||
### 项目仓库地址
|
||||
|
||||
#### 本项目地址 vue3+vite
|
||||
|
||||
**github地址**
|
||||
|
||||
[https://github.com/daidaibg/IofTV-Screen-Vue3](https://github.com/daidaibg/IofTV-Screen-Vue3)
|
||||
|
||||
**Gitee地址**
|
||||
|
||||
[https://gitee.com/daidaibg/IofTV-Screen-Vue3](https://gitee.com/daidaibg/IofTV-Screen-Vue3)
|
||||
|
||||
#### vue2版本地址
|
||||
|
||||
**github地址**
|
||||
|
||||
[https://github.com/daidaibg/IofTV-Screen](https://github.com/daidaibg/IofTV-Screen)
|
||||
|
||||
**Gitee地址**
|
||||
|
||||
[https://gitee.com/daidaibg/IofTV-Screen](https://gitee.com/daidaibg/IofTV-Screen)
|
||||
|
||||
|
||||
|
||||
### 采用自适应组件方式,
|
||||
|
||||
### 滚动设置,自适应设置
|
||||
|
||||
项目中可以进行滚动配置,内容是否滚动
|
||||
|
||||
点击右上角设置按钮
|
||||

|
||||
|
||||
|
||||
|
||||
可以进行以下配置,可以自行代码中进行修改或增加配置
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## 2、主要文件介绍
|
||||
|
||||
| 文件 | 作用/功能 |
|
||||
| ----------------- | ------------------------------------------------------------ |
|
||||
| main.js | 主目录文件,引入 Echart/DataV 等文件 |
|
||||
| utils | 工具函数与 mixins 函数等 |
|
||||
| views/ home.vue | 项目主结构 |
|
||||
| views/其余文件 | 界面各个区域组件(按照位置来命名) |
|
||||
| assets | 静态资源目录,放置 logo 与背景图片 |
|
||||
| assets / css/ | 通用 CSS 文件,全局项目快捷样式调节 |
|
||||
| components/echart | 所有 echart 图表(按照位置来命名) |
|
||||
| common/... | 全局封装的 ECharts 和 flexible 插件代码(适配屏幕尺寸,可定制化修改) |
|
||||
| api/api.js | 接口封装文件 |
|
||||
| mock | 模拟数据接口地址 |
|
||||
|
||||
###
|
||||
|
||||
## 使用介绍
|
||||
|
||||
### 安装
|
||||
|
||||
```npm
|
||||
npm install
|
||||
```
|
||||
### 启动
|
||||
|
||||
```npm
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 取消mock模拟数据
|
||||
|
||||
```javascript
|
||||
// src\main.ts文件
|
||||
把下面两行代码注释掉就可以了。
|
||||
import { mockXHR } from "@/mock/index";
|
||||
mockXHR()
|
||||
```
|
||||
|
||||
##
|
||||
|
||||
## 公用组件
|
||||
|
||||
封装了除面条外个别用到的组件
|
||||
|
||||
### 自适应缩放组件
|
||||
|
||||
#### 注意
|
||||
|
||||
采用Scale方式,会自动给组件父元素添加overflow:hidden
|
||||
|
||||
#### 使用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<scale-screen width="1920" height="1080">
|
||||
<div>
|
||||
content
|
||||
</div>
|
||||
</scale-screen>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ScaleScreen from 'scale-screen'
|
||||
|
||||
export default {
|
||||
name:'Demo',
|
||||
components:{
|
||||
VScaleScreen
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
#### API
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| ------------ | ------------------------------------------------------------ | -------------------------------- | ------ |
|
||||
| selfAdaption | 是否进行自适应 | Boolean | true |
|
||||
| width | 大屏宽度 | `Number` or `String` | 1920 |
|
||||
| height | 大屏高度 | `Number` or `String` | 1080 |
|
||||
| autoScale | 自适应配置,配置为boolean类型时,为启动或者关闭自适应,配置为对象时,若x为true,x轴产生边距,y为true时,y轴产生边距,启用fullScreen时此配置失效 | Boolean or {x:boolean,y:boolean} | true |
|
||||
| delay | 窗口变化防抖延迟时间 | Number | 500 |
|
||||
| fullScreen | 全屏自适应,启用此配置项时会存在拉伸效果,同时autoScale失效,非必要情况下不建议开启 | Boolean | false |
|
||||
| boxStyle | 修改容器样式,如居中展示时侧边背景色,符合Vue双向绑定style标准格式 | Object | null |
|
||||
| wrapperStyle | 修改自适应区域样式,符合Vue双向绑定style标准格式 | Object | null |
|
||||
|
||||
|
||||
### 外边框
|
||||
|
||||
因为我的项目外边框几乎一样,还有title,所以封装了此组件。
|
||||
|
||||
根据自己需求更改,更换外边框(src\components\item-wrap\item-wrap.vue)下更换。
|
||||
|
||||
```vue
|
||||
<ItemWrap
|
||||
title="我是title"
|
||||
>
|
||||
<div>我是谁?</div>
|
||||
</ItemWrap>
|
||||
```
|
||||
|
||||
| 参数 | 描述 | 默认值 | 类型 | 可选值 |
|
||||
| :---: | :--: | :----: | :----: | :----: |
|
||||
| title | 标头 | - | string | - |
|
||||
|
||||
### CountUp 数字滚动
|
||||
|
||||
以下属性同 coutup.js 配置项(same as countup.js properties)
|
||||
|
||||
#### Props
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| -------- | ---------------- | ------- | ------------------------------------------------------------ |
|
||||
| endVal | Number \| String | - | 结束值 |
|
||||
| startVal | Number \| String | 0 | 起始值 |
|
||||
| duration | Number | 2.5 | 动画时长,单位:秒 |
|
||||
| options | Object | - | [countUp.js](https://github.com/inorganik/countUp.js) options 配置项 |
|
||||
|
||||
以下为组件特有属性(extension properties)
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| -------- | ----------------- | ------- | ----------------------------- |
|
||||
| autoplay | Boolean | true | 是否自动计数 |
|
||||
| loop | Boolean \| Number | false | 循环次数,有限次数 / 无限循环 |
|
||||
| delay | Number | 0 | loop 循环的间隔时间,单位:秒 |
|
||||
|
||||
#### 插槽(slots)
|
||||
|
||||
| Name | Description |
|
||||
| ------ | ----------- |
|
||||
| prefix | 前缀 |
|
||||
| suffix | 后缀 |
|
||||
|
||||
#### 事件(Events)
|
||||
|
||||
| Name | Description | return |
|
||||
| --------- | -------------------------- | ------------ |
|
||||
| @init | CountUp 实例初始化完成触发 | CountUp 实例 |
|
||||
| @finished | 计数结束时触发 | - |
|
||||
|
||||
#### countup.js 配置项说明
|
||||
```ts
|
||||
interface CountUpOptions {
|
||||
startVal?: number // number to start at (0) 开始数值,默认 0
|
||||
decimalPlaces?: number // number of decimal places (0) 小数点 位数
|
||||
duration?: number // animation duration in seconds (2) 动画时长
|
||||
useGrouping?: boolean // example: 1,000 vs 1000 (true) 是否使用千分位
|
||||
useEasing?: boolean // ease animation (true) 是否开启动画过渡,默认动画函数为easeOutExpo
|
||||
smartEasingThreshold?: number // smooth easing for large numbers above this if useEasing (999)
|
||||
smartEasingAmount?: number // amount to be eased for numbers above threshold (333)
|
||||
separator?: string // grouping separator (',') 千分位分隔符
|
||||
decimal?: string // decimal ('.') 小数点分隔符
|
||||
// easingFn: easing function for animation (easeOutExpo) 动画函数
|
||||
easingFn?: (t: number, b: number, c: number, d: number) => number
|
||||
formattingFn?: (n: number) => string // this function formats result 格式化结果
|
||||
prefix?: string // text prepended to result 数值前缀
|
||||
suffix?: string // text appended to result 数值后缀
|
||||
numerals?: string[] // numeral glyph substitution 数字符号替换 0 - 9,例如替换为 [a,b,c,d,e,f,g,h,i,j]
|
||||
enableScrollSpy?: boolean // start animation when target is in view 在可视范围内才开始动画
|
||||
scrollSpyDelay?: number // delay (ms) after target comes into view 目标进入可视范围内后的延迟时间(毫秒)
|
||||
}
|
||||
```
|
||||
|
||||
### 胶囊柱图
|
||||
|
||||
#### Props
|
||||
|
||||
| 属性 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
| :----: | :------: | :-------------: | :-----------------------: | :-----: |
|
||||
| data | 柱数据 | `Array<Object>` | [data属性](#data属性) | `[]` |
|
||||
| config | 基础配置 | Object | [config属性](#config属性) | `false` |
|
||||
|
||||
#### config属性
|
||||
|
||||
| 属性 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
| :-------: | :------: | :-------------: | :----: | :-----: |
|
||||
| unit | 单位 | `String` | --- | `''` |
|
||||
| colors | 环颜色 | `Array<String>` | [1] | [2] |
|
||||
| showValue | 显示数值 | `Boolean` | --- | `false` |
|
||||
|
||||
#### 注释config注释
|
||||
|
||||
[1] 颜色支持`hex|rgb|rgba|颜色关键字`等四种类型。
|
||||
|
||||
[2] 默认配色为`['#37a2da', '#32c5e9', '#67e0e3', '#9fe6b8', '#ffdb5c', '#ff9f7f', '#fb7293']`。
|
||||
|
||||
#### data属性
|
||||
|
||||
| 属性 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
| :---: | :------: | :------: | :----: | :----: |
|
||||
| name | 柱名称 | `String` | --- | --- |
|
||||
| value | 柱对应值 | `Number` | --- | --- |
|
||||
|
||||
### 无缝轮播组件
|
||||
|
||||
看此文档 优化次源码
|
||||
|
||||
[https://doc.wssio.com/opensource/vue3-seamless-scroll/](https://doc.wssio.com/opensource/vue3-seamless-scroll/)
|
||||
|
||||
## 中间地图
|
||||
|
||||
### 南海显隐控制
|
||||
|
||||
根据需求来,**修改此值请刷新页面**
|
||||
|
||||
```indexs/center-map.vue``` 文件中```isSouthChinaSea```变量 默认不显示南海(false),为```true```的时候显示南海
|
||||
|
||||
```
|
||||
isSouthChinaSea:false,//默认不显示南海,改为true可显示南海
|
||||
```
|
||||
|
||||
|
||||
## 大屏交流反馈(面条的群)
|
||||
|
||||
### 大屏QQ群
|
||||
|
||||
QQ群号:
|
||||
|
||||
一群:713105837 (已满)
|
||||
|
||||
二群:495755841
|
||||
|
||||
|
||||
|
||||
|
||||
## 项目描述
|
||||
|
||||
[IofTV-Screen](https://gitee.com/daidaibg/IofTV-Screen/tree/main) 的 Vue3+vite版本,
|
||||
|
||||
### 与vue2版本对比
|
||||
|
||||
#### 功能
|
||||
|
||||
功能采用与vue2版本相同功能
|
||||
|
||||
因为要与vue2版本相同功能,有些组件不兼容vue3版本,例如:胶囊柱图,数字滚动皆重新封装为组件,整体来说,功能属实相同。根据自己需求选择[vue2](#vue2版本地址)版本与[vue3](#本项目地址 vue3+vite)版本
|
||||
|
||||
#### 样式
|
||||
|
||||
进行微调,整体看着更加美观
|
||||
|
||||
|
||||
|
||||
- 项目需要全屏展示(按 F11)。
|
||||
|
||||
- 项目部分区域使用了全局注册方式,增加了打包体积,在实际运用中请使用 **按需引入**。
|
||||
|
||||
- 项目环境:Vite、Echarts、Npm、Node,axios,mock,vue3。
|
||||
|
||||
- 请拉取 master 分支的代码,其余分支是开发分支。
|
||||
|
||||
- 在项目public目录下存放地图数据合集,根据地市编存放。
|
||||
|
||||
|
||||
友情链接:
|
||||
|
||||
1. [Vue 官方文档](https://cn.vuejs.org/)
|
||||
3. [echarts 实例](https://gitee.com/link?target=https%3A%2F%2Fecharts.apache.org%2Fexamples%2Fzh%2Findex.html),[echarts API 文档](https://gitee.com/link?target=https%3A%2F%2Fecharts.apache.org%2Fzh%2Fapi.html%23echarts)
|
||||
4. [mock.js官网](http://mockjs.com/examples.html)
|
||||
5. [axios官网](https://axios-http.com/)
|
||||
|
||||
**项目展示**
|
||||
|
||||

|
||||
|
||||
### 项目预览地址
|
||||
|
||||
[https://www.daidaibg.com/bigscreen-vue3](https://www.daidaibg.com/bigscreen-vue3)
|
||||
|
||||
### 项目仓库地址
|
||||
|
||||
#### 本项目地址 vue3+vite
|
||||
|
||||
**github地址**
|
||||
|
||||
[https://github.com/daidaibg/IofTV-Screen-Vue3](https://github.com/daidaibg/IofTV-Screen-Vue3)
|
||||
|
||||
**Gitee地址**
|
||||
|
||||
[https://gitee.com/daidaibg/IofTV-Screen-Vue3](https://gitee.com/daidaibg/IofTV-Screen-Vue3)
|
||||
|
||||
#### vue2版本地址
|
||||
|
||||
**github地址**
|
||||
|
||||
[https://github.com/daidaibg/IofTV-Screen](https://github.com/daidaibg/IofTV-Screen)
|
||||
|
||||
**Gitee地址**
|
||||
|
||||
[https://gitee.com/daidaibg/IofTV-Screen](https://gitee.com/daidaibg/IofTV-Screen)
|
||||
|
||||
|
||||
|
||||
### 采用自适应组件方式,
|
||||
|
||||
### 滚动设置,自适应设置
|
||||
|
||||
项目中可以进行滚动配置,内容是否滚动
|
||||
|
||||
点击右上角设置按钮
|
||||

|
||||
|
||||
|
||||
|
||||
可以进行以下配置,可以自行代码中进行修改或增加配置
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## 2、主要文件介绍
|
||||
|
||||
| 文件 | 作用/功能 |
|
||||
| ----------------- | ------------------------------------------------------------ |
|
||||
| main.js | 主目录文件,引入 Echart/DataV 等文件 |
|
||||
| utils | 工具函数与 mixins 函数等 |
|
||||
| views/ home.vue | 项目主结构 |
|
||||
| views/其余文件 | 界面各个区域组件(按照位置来命名) |
|
||||
| assets | 静态资源目录,放置 logo 与背景图片 |
|
||||
| assets / css/ | 通用 CSS 文件,全局项目快捷样式调节 |
|
||||
| components/echart | 所有 echart 图表(按照位置来命名) |
|
||||
| common/... | 全局封装的 ECharts 和 flexible 插件代码(适配屏幕尺寸,可定制化修改) |
|
||||
| api/api.js | 接口封装文件 |
|
||||
| mock | 模拟数据接口地址 |
|
||||
|
||||
###
|
||||
|
||||
## 使用介绍
|
||||
|
||||
### 安装
|
||||
|
||||
```npm
|
||||
npm install
|
||||
```
|
||||
### 启动
|
||||
|
||||
```npm
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 取消mock模拟数据
|
||||
|
||||
```javascript
|
||||
// src\main.ts文件
|
||||
把下面两行代码注释掉就可以了。
|
||||
import { mockXHR } from "@/mock/index";
|
||||
mockXHR()
|
||||
```
|
||||
|
||||
##
|
||||
|
||||
## 公用组件
|
||||
|
||||
封装了除面条外个别用到的组件
|
||||
|
||||
### 自适应缩放组件
|
||||
|
||||
#### 注意
|
||||
|
||||
采用Scale方式,会自动给组件父元素添加overflow:hidden
|
||||
|
||||
#### 使用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<scale-screen width="1920" height="1080">
|
||||
<div>
|
||||
content
|
||||
</div>
|
||||
</scale-screen>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ScaleScreen from 'scale-screen'
|
||||
|
||||
export default {
|
||||
name:'Demo',
|
||||
components:{
|
||||
VScaleScreen
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
#### API
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| ------------ | ------------------------------------------------------------ | -------------------------------- | ------ |
|
||||
| selfAdaption | 是否进行自适应 | Boolean | true |
|
||||
| width | 大屏宽度 | `Number` or `String` | 1920 |
|
||||
| height | 大屏高度 | `Number` or `String` | 1080 |
|
||||
| autoScale | 自适应配置,配置为boolean类型时,为启动或者关闭自适应,配置为对象时,若x为true,x轴产生边距,y为true时,y轴产生边距,启用fullScreen时此配置失效 | Boolean or {x:boolean,y:boolean} | true |
|
||||
| delay | 窗口变化防抖延迟时间 | Number | 500 |
|
||||
| fullScreen | 全屏自适应,启用此配置项时会存在拉伸效果,同时autoScale失效,非必要情况下不建议开启 | Boolean | false |
|
||||
| boxStyle | 修改容器样式,如居中展示时侧边背景色,符合Vue双向绑定style标准格式 | Object | null |
|
||||
| wrapperStyle | 修改自适应区域样式,符合Vue双向绑定style标准格式 | Object | null |
|
||||
|
||||
|
||||
### 外边框
|
||||
|
||||
因为我的项目外边框几乎一样,还有title,所以封装了此组件。
|
||||
|
||||
根据自己需求更改,更换外边框(src\components\item-wrap\item-wrap.vue)下更换。
|
||||
|
||||
```vue
|
||||
<ItemWrap
|
||||
title="我是title"
|
||||
>
|
||||
<div>我是谁?</div>
|
||||
</ItemWrap>
|
||||
```
|
||||
|
||||
| 参数 | 描述 | 默认值 | 类型 | 可选值 |
|
||||
| :---: | :--: | :----: | :----: | :----: |
|
||||
| title | 标头 | - | string | - |
|
||||
|
||||
### CountUp 数字滚动
|
||||
|
||||
以下属性同 coutup.js 配置项(same as countup.js properties)
|
||||
|
||||
#### Props
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| -------- | ---------------- | ------- | ------------------------------------------------------------ |
|
||||
| endVal | Number \| String | - | 结束值 |
|
||||
| startVal | Number \| String | 0 | 起始值 |
|
||||
| duration | Number | 2.5 | 动画时长,单位:秒 |
|
||||
| options | Object | - | [countUp.js](https://github.com/inorganik/countUp.js) options 配置项 |
|
||||
|
||||
以下为组件特有属性(extension properties)
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| -------- | ----------------- | ------- | ----------------------------- |
|
||||
| autoplay | Boolean | true | 是否自动计数 |
|
||||
| loop | Boolean \| Number | false | 循环次数,有限次数 / 无限循环 |
|
||||
| delay | Number | 0 | loop 循环的间隔时间,单位:秒 |
|
||||
|
||||
#### 插槽(slots)
|
||||
|
||||
| Name | Description |
|
||||
| ------ | ----------- |
|
||||
| prefix | 前缀 |
|
||||
| suffix | 后缀 |
|
||||
|
||||
#### 事件(Events)
|
||||
|
||||
| Name | Description | return |
|
||||
| --------- | -------------------------- | ------------ |
|
||||
| @init | CountUp 实例初始化完成触发 | CountUp 实例 |
|
||||
| @finished | 计数结束时触发 | - |
|
||||
|
||||
#### countup.js 配置项说明
|
||||
```ts
|
||||
interface CountUpOptions {
|
||||
startVal?: number // number to start at (0) 开始数值,默认 0
|
||||
decimalPlaces?: number // number of decimal places (0) 小数点 位数
|
||||
duration?: number // animation duration in seconds (2) 动画时长
|
||||
useGrouping?: boolean // example: 1,000 vs 1000 (true) 是否使用千分位
|
||||
useEasing?: boolean // ease animation (true) 是否开启动画过渡,默认动画函数为easeOutExpo
|
||||
smartEasingThreshold?: number // smooth easing for large numbers above this if useEasing (999)
|
||||
smartEasingAmount?: number // amount to be eased for numbers above threshold (333)
|
||||
separator?: string // grouping separator (',') 千分位分隔符
|
||||
decimal?: string // decimal ('.') 小数点分隔符
|
||||
// easingFn: easing function for animation (easeOutExpo) 动画函数
|
||||
easingFn?: (t: number, b: number, c: number, d: number) => number
|
||||
formattingFn?: (n: number) => string // this function formats result 格式化结果
|
||||
prefix?: string // text prepended to result 数值前缀
|
||||
suffix?: string // text appended to result 数值后缀
|
||||
numerals?: string[] // numeral glyph substitution 数字符号替换 0 - 9,例如替换为 [a,b,c,d,e,f,g,h,i,j]
|
||||
enableScrollSpy?: boolean // start animation when target is in view 在可视范围内才开始动画
|
||||
scrollSpyDelay?: number // delay (ms) after target comes into view 目标进入可视范围内后的延迟时间(毫秒)
|
||||
}
|
||||
```
|
||||
|
||||
### 胶囊柱图
|
||||
|
||||
#### Props
|
||||
|
||||
| 属性 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
| :----: | :------: | :-------------: | :-----------------------: | :-----: |
|
||||
| data | 柱数据 | `Array<Object>` | [data属性](#data属性) | `[]` |
|
||||
| config | 基础配置 | Object | [config属性](#config属性) | `false` |
|
||||
|
||||
#### config属性
|
||||
|
||||
| 属性 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
| :-------: | :------: | :-------------: | :----: | :-----: |
|
||||
| unit | 单位 | `String` | --- | `''` |
|
||||
| colors | 环颜色 | `Array<String>` | [1] | [2] |
|
||||
| showValue | 显示数值 | `Boolean` | --- | `false` |
|
||||
|
||||
#### 注释config注释
|
||||
|
||||
[1] 颜色支持`hex|rgb|rgba|颜色关键字`等四种类型。
|
||||
|
||||
[2] 默认配色为`['#37a2da', '#32c5e9', '#67e0e3', '#9fe6b8', '#ffdb5c', '#ff9f7f', '#fb7293']`。
|
||||
|
||||
#### data属性
|
||||
|
||||
| 属性 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
| :---: | :------: | :------: | :----: | :----: |
|
||||
| name | 柱名称 | `String` | --- | --- |
|
||||
| value | 柱对应值 | `Number` | --- | --- |
|
||||
|
||||
### 无缝轮播组件
|
||||
|
||||
看此文档 优化次源码
|
||||
|
||||
[https://doc.wssio.com/opensource/vue3-seamless-scroll/](https://doc.wssio.com/opensource/vue3-seamless-scroll/)
|
||||
|
||||
## 中间地图
|
||||
|
||||
### 南海显隐控制
|
||||
|
||||
根据需求来,**修改此值请刷新页面**
|
||||
|
||||
```indexs/center-map.vue``` 文件中```isSouthChinaSea```变量 默认不显示南海(false),为```true```的时候显示南海
|
||||
|
||||
```
|
||||
isSouthChinaSea:false,//默认不显示南海,改为true可显示南海
|
||||
```
|
||||
|
||||
|
||||
## 大屏交流反馈(面条的群)
|
||||
|
||||
### 大屏QQ群
|
||||
|
||||
QQ群号:
|
||||
|
||||
一群:713105837 (已满)
|
||||
|
||||
二群:495755841
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite/client" />
|
||||
|
|
38
index.html
38
index.html
|
@ -1,19 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>IofTV-Screen-Vue3</title>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="<%= BASE_URL %>static/map_load.js"
|
||||
></script>
|
||||
<!-- <script src="https://api.map.baidu.com/api?v=3.0&ak=a76Liq52IWX8S5WkSi2z5WvUYRMaNMRe"></script>
|
||||
<script src="https://api.map.baidu.com/getscript?v=3.0&ak=a76Liq52IWX8S5WkSi2z5WvUYRMaNMRe&s=1"></script> -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>IofTV-Screen-Vue3</title>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="<%= BASE_URL %>static/map_load.js"
|
||||
></script>
|
||||
<!-- <script src="https://api.map.baidu.com/api?v=3.0&ak=a76Liq52IWX8S5WkSi2z5WvUYRMaNMRe"></script>
|
||||
<script src="https://api.map.baidu.com/getscript?v=3.0&ak=a76Liq52IWX8S5WkSi2z5WvUYRMaNMRe&s=1"></script> -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
96
package.json
96
package.json
|
@ -1,48 +1,48 @@
|
|||
{
|
||||
"name": "test",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build:old": "run-p type-check build-only",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 4173",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"countup.js": "^2.8.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.6.2",
|
||||
"hls.js": "^1.6.5",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"mockjs": "^1.1.0",
|
||||
"pinia": "^2.1.7",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vue": "^3.4.21",
|
||||
"vue-echarts": "^6.6.9",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mockjs": "^1.0.10",
|
||||
"@types/node": "^20.11.30",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.38",
|
||||
"sass": "^1.72.0",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "~5.4.3",
|
||||
"unplugin-auto-import": "^0.17.5",
|
||||
"unplugin-element-plus": "^0.8.0",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.2.6",
|
||||
"vue-tsc": "^2.0.7"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "test",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build:old": "run-p type-check build-only",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 4173",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"countup.js": "^2.8.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.6.2",
|
||||
"hls.js": "^1.6.5",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"mockjs": "^1.1.0",
|
||||
"pinia": "^2.1.7",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vue": "^3.4.21",
|
||||
"vue-echarts": "^6.6.9",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mockjs": "^1.0.10",
|
||||
"@types/node": "^20.11.30",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.38",
|
||||
"sass": "^1.72.0",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "~5.4.3",
|
||||
"unplugin-auto-import": "^0.17.5",
|
||||
"unplugin-element-plus": "^0.8.0",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.2.6",
|
||||
"vue-tsc": "^2.0.7"
|
||||
}
|
||||
}
|
||||
|
|
6568
pnpm-lock.yaml
6568
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -4,13 +4,20 @@
|
|||
var bmapcfg = {
|
||||
'imgext': '.jpg', //瓦片图的后缀 ------ 根据需要修改,一般是 .png .jpg
|
||||
//这里我直接用的路径是/static/bmap_offline_demo/tiles,如果瓦片数量较大,可改为瓦片服务的地址
|
||||
tiles_dir: "images", //普通瓦片图的地址,为空默认在 offlinemap/tiles/ 目
|
||||
tiles_path : 'http://172.16.1.162:8123/HeBeiDarkMap/',
|
||||
tiles_hybrid: 'http://172.16.1.162:8123/HeBeiDarkMap/', //卫星瓦片图的地址,为空默认在 offlinemap/tiles_hybrid/ 目
|
||||
tiles_self : 'http://172.16.1.162:8123/HeBeiDarkMap/' //自定义图层的地址,为空默认在 offlinemap/tiles_self/ 目录
|
||||
// tiles_dir: "images", //普通瓦片图的地址,为空默认在 offlinemap/tiles/ 目
|
||||
//现场
|
||||
tiles_dir: "map", //普通瓦片图的地址,为空默认在 offlinemap/tiles/ 目
|
||||
// tiles_path : 'http://172.16.1.162:8123/HeBeiDarkMap/',
|
||||
// tiles_hybrid: 'http://172.16.1.162:8123/HeBeiDarkMap/', //卫星瓦片图的地址,为空默认在 offlinemap/tiles_hybrid/ 目
|
||||
// tiles_self : 'http://172.16.1.162:8123/HeBeiDarkMap/' //自定义图层的地址,为空默认在 offlinemap/tiles_self/ 目录
|
||||
// tiles_path: "http://111.229.30.246:4008/HeBeiDarkMap/",
|
||||
// tiles_hybrid: "http://111.229.30.246:4008/HeBeiDarkMap/", //卫星瓦片图的地址,为空默认在 offlinemap/tiles_hybrid/ 目
|
||||
// tiles_self: "http://111.229.30.246:4008/HeBeiDarkMap/", //自定义图层的地址,为空默认在 offlinemap/tiles_self/ 目录
|
||||
|
||||
//现场
|
||||
tiles_path: "http://13.14.64.150:3638/",
|
||||
tiles_hybrid: "http://13.14.64.150:3638/", //卫星瓦片图的地址,为空默认在 offlinemap/tiles_hybrid/ 目
|
||||
tiles_self: "http://13.14.64.150:3638/", //自定义图层的地址,为空默认在 offlinemap/tiles_self/ 目录
|
||||
};
|
||||
|
||||
//下面的保持不动///
|
||||
|
|
24
src/App.vue
24
src/App.vue
|
@ -1,12 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import { RouterView } from 'vue-router'
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { RouterView } from 'vue-router'
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import MessageContent from './index.vue';
|
||||
|
||||
export default MessageContent ;
|
||||
import MessageContent from './index.vue';
|
||||
|
||||
export default MessageContent ;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<!-- eslint-disable vue/valid-template-root -->
|
||||
<template></template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElMessage } from 'element-plus'
|
||||
//挂载在 window 方便与在js中使用
|
||||
window['$message'] = ElMessage
|
||||
</script>
|
||||
<!-- eslint-disable vue/valid-template-root -->
|
||||
<template></template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElMessage } from 'element-plus'
|
||||
//挂载在 window 方便与在js中使用
|
||||
window['$message'] = ElMessage
|
||||
</script>
|
||||
|
|
40
src/main.ts
40
src/main.ts
|
@ -1,20 +1,20 @@
|
|||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import '@/assets/css/main.scss'
|
||||
import '@/assets/css/tailwind.css'
|
||||
|
||||
import {registerEcharts} from "@/plugins/echarts"
|
||||
//不使用mock 请注释掉
|
||||
import { mockXHR } from "@/mock/index";
|
||||
mockXHR()
|
||||
|
||||
const app = createApp(App)
|
||||
registerEcharts(app)
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import '@/assets/css/main.scss'
|
||||
import '@/assets/css/tailwind.css'
|
||||
|
||||
import {registerEcharts} from "@/plugins/echarts"
|
||||
//不使用mock 请注释掉
|
||||
import { mockXHR } from "@/mock/index";
|
||||
mockXHR()
|
||||
|
||||
const app = createApp(App)
|
||||
registerEcharts(app)
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
|
||||
import type {RouteRecordRaw} from "vue-router"
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/index',
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
component: () => import('@/views/HomeView.vue'),
|
||||
children:[
|
||||
{
|
||||
path: '/index',
|
||||
name: 'index',
|
||||
component: () => import('@/views/index/index.vue'),
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||
routes,
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
next();
|
||||
})
|
||||
|
||||
export default router
|
||||
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
|
||||
import type {RouteRecordRaw} from "vue-router"
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/index',
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
component: () => import('@/views/HomeView.vue'),
|
||||
children:[
|
||||
{
|
||||
path: '/index',
|
||||
name: 'index',
|
||||
component: () => import('@/views/index/index.vue'),
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||
routes,
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
next();
|
||||
})
|
||||
|
||||
export default router
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
import mitt from 'mitt';
|
||||
import mitt from 'mitt';
|
||||
export const emitter = mitt();
|
|
@ -1,27 +1,27 @@
|
|||
import { ref, onMounted } from "vue";
|
||||
|
||||
/**
|
||||
* 获取当前日期并格式化为 YYYY-MM-DD 星期几
|
||||
*/
|
||||
export const useTodayTime = () => {
|
||||
const todayTime = ref<string>("");
|
||||
|
||||
const getTodayTime = () => {
|
||||
const date = new Date();
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const weekDay = date.toLocaleDateString('zh-CN', { weekday: 'long' }); // 星期几
|
||||
|
||||
todayTime.value = `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getTodayTime();
|
||||
});
|
||||
|
||||
return {
|
||||
todayTime,
|
||||
getTodayTime,
|
||||
};
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
/**
|
||||
* 获取当前日期并格式化为 YYYY-MM-DD 星期几
|
||||
*/
|
||||
export const useTodayTime = () => {
|
||||
const todayTime = ref<string>("");
|
||||
|
||||
const getTodayTime = () => {
|
||||
const date = new Date();
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const weekDay = date.toLocaleDateString('zh-CN', { weekday: 'long' }); // 星期几
|
||||
|
||||
todayTime.value = `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getTodayTime();
|
||||
});
|
||||
|
||||
return {
|
||||
todayTime,
|
||||
getTodayTime,
|
||||
};
|
||||
};
|
|
@ -1,102 +1,102 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { RouterView } from "vue-router";
|
||||
import ScaleScreen from "@/components/scale-screen";
|
||||
import Left from "./component/left.vue";
|
||||
import Right from "./component/right.vue";
|
||||
import Center from "./component/center.vue";
|
||||
import Headers from "./header.vue";
|
||||
import Setting from "./setting.vue";
|
||||
import { useSettingStore } from "@/stores/index";
|
||||
import { storeToRefs } from "pinia";
|
||||
import MessageContent from "@/components/Plugins/MessageContent";
|
||||
import Map from "./component/map.vue";
|
||||
import Home from "./index/index.vue"
|
||||
const settingStore = useSettingStore();
|
||||
const { isScale } = storeToRefs(settingStore);
|
||||
const wrapperStyle = {};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<scale-screen
|
||||
width="12960"
|
||||
height="2430"
|
||||
:delay="500"
|
||||
:fullScreen="false"
|
||||
:boxStyle="{
|
||||
background: '#03050C',
|
||||
overflow: isScale ? 'hidden' : 'auto',
|
||||
}"
|
||||
:wrapperStyle="wrapperStyle"
|
||||
:autoScale="isScale"
|
||||
>
|
||||
<div class="content_wrap">
|
||||
<!-- 背景层 -->
|
||||
<div class="background_layer">
|
||||
<Map />
|
||||
<div class="header_layer">
|
||||
<Headers />
|
||||
</div>
|
||||
<div class="left">
|
||||
<Left title=""></Left>
|
||||
</div>
|
||||
<div class="right">
|
||||
<Right title=""></Right>
|
||||
</div>
|
||||
<Center class="center" />
|
||||
</div>
|
||||
|
||||
<!-- 前景层 -->
|
||||
<!-- <div class="foreground_layer">
|
||||
<Headers />
|
||||
<RouterView />
|
||||
</div> -->
|
||||
<!-- <MessageContent /> -->
|
||||
</div>
|
||||
</scale-screen>
|
||||
<Setting />
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.content_wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background-image: url("@/assets/img/pageBg.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
overflow: hidden; // 防止地图溢出边界
|
||||
}
|
||||
|
||||
.background_layer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// z-index: 0; // 确保在前景层之下
|
||||
position: relative;
|
||||
.header_layer{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
.left{
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 0;
|
||||
}
|
||||
.right{
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
right: 0;
|
||||
}
|
||||
.center{
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.foreground_layer {
|
||||
position: relative;
|
||||
// z-index: 1; // 确保在背景层之上
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { RouterView } from "vue-router";
|
||||
import ScaleScreen from "@/components/scale-screen";
|
||||
import Left from "./component/left.vue";
|
||||
import Right from "./component/right.vue";
|
||||
import Center from "./component/center.vue";
|
||||
import Headers from "./header.vue";
|
||||
import Setting from "./setting.vue";
|
||||
import { useSettingStore } from "@/stores/index";
|
||||
import { storeToRefs } from "pinia";
|
||||
import MessageContent from "@/components/Plugins/MessageContent";
|
||||
import Map from "./component/map.vue";
|
||||
import Home from "./index/index.vue"
|
||||
const settingStore = useSettingStore();
|
||||
const { isScale } = storeToRefs(settingStore);
|
||||
const wrapperStyle = {};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<scale-screen
|
||||
width="12960"
|
||||
height="2430"
|
||||
:delay="500"
|
||||
:fullScreen="false"
|
||||
:boxStyle="{
|
||||
background: '#03050C',
|
||||
overflow: isScale ? 'hidden' : 'auto',
|
||||
}"
|
||||
:wrapperStyle="wrapperStyle"
|
||||
:autoScale="isScale"
|
||||
>
|
||||
<div class="content_wrap">
|
||||
<!-- 背景层 -->
|
||||
<div class="background_layer">
|
||||
<Map />
|
||||
<div class="header_layer">
|
||||
<Headers />
|
||||
</div>
|
||||
<div class="left">
|
||||
<Left title=""></Left>
|
||||
</div>
|
||||
<div class="right">
|
||||
<Right title=""></Right>
|
||||
</div>
|
||||
<Center class="center" />
|
||||
</div>
|
||||
|
||||
<!-- 前景层 -->
|
||||
<!-- <div class="foreground_layer">
|
||||
<Headers />
|
||||
<RouterView />
|
||||
</div> -->
|
||||
<!-- <MessageContent /> -->
|
||||
</div>
|
||||
</scale-screen>
|
||||
<Setting />
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.content_wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background-image: url("@/assets/img/pageBg.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
overflow: hidden; // 防止地图溢出边界
|
||||
}
|
||||
|
||||
.background_layer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// z-index: 0; // 确保在前景层之下
|
||||
position: relative;
|
||||
.header_layer{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
.left{
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 0;
|
||||
}
|
||||
.right{
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
right: 0;
|
||||
}
|
||||
.center{
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.foreground_layer {
|
||||
position: relative;
|
||||
// z-index: 1; // 确保在背景层之上
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,75 +1,75 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import useCharts from "@/hooks/useEcharts";
|
||||
import { todayTrafficCount } from "@/api/modules/index";
|
||||
import type { EChartsOption } from "echarts";
|
||||
|
||||
// 图表 DOM ref 和 ECharts 实例
|
||||
const chartRef = ref<HTMLElement | null>(null);
|
||||
let chart: ReturnType<typeof useCharts> | null = null;
|
||||
|
||||
// 车流数据
|
||||
const trafficData = reactive({
|
||||
total: 0,
|
||||
car: { value: 0, percentage: 0, name: "小型车" },
|
||||
truck: { value: 0, percentage: 0, name: "货车" },
|
||||
bus: { value: 0, percentage: 0, name: "客车" },
|
||||
});
|
||||
|
||||
// 图表初始配置
|
||||
const chartOptions: EChartsOption = {
|
||||
tooltip: { trigger: "item", formatter: "{b}: {c} ({d}%)" },
|
||||
legend: { show: false },
|
||||
series: [
|
||||
{
|
||||
name: "车流量分布",
|
||||
type: "pie",
|
||||
radius: ["65%", "80%"],
|
||||
center: ["50%", "50%"],
|
||||
data: [], // 初始化为空,稍后更新
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 初始化图表 + 获取数据
|
||||
onMounted(async () => {
|
||||
if (chartRef.value) {
|
||||
chart = useCharts(chartRef.value);
|
||||
chart.setOption(chartOptions); // 初始化
|
||||
}
|
||||
await loadTrafficData();
|
||||
});
|
||||
|
||||
// 获取接口数据并更新图表
|
||||
async function loadTrafficData() {
|
||||
try {
|
||||
const res = await todayTrafficCount(); // 假设返回 car、truck、bus 数值
|
||||
trafficData.total = res.total;
|
||||
trafficData.car.value = res.car;
|
||||
trafficData.truck.value = res.truck;
|
||||
trafficData.bus.value = res.bus;
|
||||
|
||||
const newChartData = [
|
||||
{ value: res.car, name: "小型车" },
|
||||
{ value: res.truck, name: "货车" },
|
||||
{ value: res.bus, name: "客车" },
|
||||
];
|
||||
|
||||
if (chart) {
|
||||
chart.setOption({
|
||||
series: [
|
||||
{
|
||||
data: newChartData,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取车流量数据失败:", error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 100%; height: 400px"></div>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import useCharts from "@/hooks/useEcharts";
|
||||
import { todayTrafficCount } from "@/api/modules/index";
|
||||
import type { EChartsOption } from "echarts";
|
||||
|
||||
// 图表 DOM ref 和 ECharts 实例
|
||||
const chartRef = ref<HTMLElement | null>(null);
|
||||
let chart: ReturnType<typeof useCharts> | null = null;
|
||||
|
||||
// 车流数据
|
||||
const trafficData = reactive({
|
||||
total: 0,
|
||||
car: { value: 0, percentage: 0, name: "小型车" },
|
||||
truck: { value: 0, percentage: 0, name: "货车" },
|
||||
bus: { value: 0, percentage: 0, name: "客车" },
|
||||
});
|
||||
|
||||
// 图表初始配置
|
||||
const chartOptions: EChartsOption = {
|
||||
tooltip: { trigger: "item", formatter: "{b}: {c} ({d}%)" },
|
||||
legend: { show: false },
|
||||
series: [
|
||||
{
|
||||
name: "车流量分布",
|
||||
type: "pie",
|
||||
radius: ["65%", "80%"],
|
||||
center: ["50%", "50%"],
|
||||
data: [], // 初始化为空,稍后更新
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 初始化图表 + 获取数据
|
||||
onMounted(async () => {
|
||||
if (chartRef.value) {
|
||||
chart = useCharts(chartRef.value);
|
||||
chart.setOption(chartOptions); // 初始化
|
||||
}
|
||||
await loadTrafficData();
|
||||
});
|
||||
|
||||
// 获取接口数据并更新图表
|
||||
async function loadTrafficData() {
|
||||
try {
|
||||
const res = await todayTrafficCount(); // 假设返回 car、truck、bus 数值
|
||||
trafficData.total = res.total;
|
||||
trafficData.car.value = res.car;
|
||||
trafficData.truck.value = res.truck;
|
||||
trafficData.bus.value = res.bus;
|
||||
|
||||
const newChartData = [
|
||||
{ value: res.car, name: "小型车" },
|
||||
{ value: res.truck, name: "货车" },
|
||||
{ value: res.bus, name: "客车" },
|
||||
];
|
||||
|
||||
if (chart) {
|
||||
chart.setOption({
|
||||
series: [
|
||||
{
|
||||
data: newChartData,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取车流量数据失败:", error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 100%; height: 400px"></div>
|
||||
</template>
|
|
@ -1,449 +1,449 @@
|
|||
<template>
|
||||
<div class="center-container">
|
||||
<div class="statistics-panel">
|
||||
<div v-for="(stat, index) in statistics" :key="index" class="stat-card" style="position: relative;">
|
||||
<div
|
||||
class="box"
|
||||
@mouseenter="stat.label === '服务区' && (showDropdown = true)"
|
||||
@mouseleave="stat.label === '服务区' && (showDropdown = false)"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div class="icon-wrapper">
|
||||
<img :src="stat.image" :alt="stat.label" />
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">
|
||||
{{ stat.label }}
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
<span class="number">{{ stat.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 只在服务区显示下拉选择 -->
|
||||
<template v-if="stat.label === '服务区'">
|
||||
<div
|
||||
v-show="showDropdown"
|
||||
class="dropdown large-dropdown"
|
||||
@mouseenter="showDropdown = true"
|
||||
@mouseleave="showDropdown = false"
|
||||
>
|
||||
<div
|
||||
class="dropdown-item"
|
||||
v-for="area in serviceAreas"
|
||||
:key="area"
|
||||
@click="selectServiceArea(area)"
|
||||
>
|
||||
{{ area }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 下面弹框标题用当前选中的服务区 -->
|
||||
<!-- <div v-if="showServiceArea" class="map-container">
|
||||
<div class="map-title">
|
||||
<div class="title-content">
|
||||
<span class="title-text">{{ selectedServiceArea }}</span>
|
||||
</div>
|
||||
<div class="close-btn" @click="closeDialog">×</div>
|
||||
</div>
|
||||
<div class="map-con">
|
||||
<RealTimeImageCenter />
|
||||
</div>
|
||||
</div> -->
|
||||
<div v-if="showServiceArea" style="width: 5664px;" class="map-container RealTimeImage">
|
||||
<div class="map-title">
|
||||
<div class="title-content">
|
||||
<span class="title-text">{{selectedServiceArea}}</span>
|
||||
</div>
|
||||
<div class="close-btn" @click="closeDialog">×</div>
|
||||
</div>
|
||||
<div class="map-con">
|
||||
<RealTimeImageCenter />
|
||||
</div>
|
||||
</div>
|
||||
<div class="map-container bg-header bg-header-one" style="width: 1800px;" v-if="showDeviceInfo">
|
||||
<div class="map-title">
|
||||
<div class="title-content">
|
||||
<span class="title-text">设备信息(NO:{{ deviceInfo.deviceId }})</span>
|
||||
</div>
|
||||
<div class="close-btn" @click="closeDeviceInfo">×</div>
|
||||
</div>
|
||||
<div class="map-con">
|
||||
<DeviceInfoComponent :deviceInfo="deviceInfo" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="map-container bg-header bg-header-two" style="width: 1800px;height: 1000px;" v-if="showDeviceInfoTwo">
|
||||
<div class="map-title">
|
||||
<div class="title-content">
|
||||
<span class="title-text">设备信息(NO:{{ deviceInfo.deviceId }})</span>
|
||||
</div>
|
||||
<div class="close-btn" @click="closeDeviceInfo">×</div>
|
||||
</div>
|
||||
<div class="map-con">
|
||||
<DeviceInfoComponentTwo :deviceInfo="deviceInfoTwo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import { onMounted } from 'vue';
|
||||
import fee from "@/assets/img/fee.png";
|
||||
import km from "@/assets/img/km.png";
|
||||
import toll from "@/assets/img/toll.png";
|
||||
import RealTimeImageCenter from "./RealTimeImageCenter.vue";
|
||||
import DeviceInfoComponent from "./DeviceInfoComponent.vue";
|
||||
import bridge from "@/assets/img/bridge.png";
|
||||
import hub from "@/assets/img/hub.png";
|
||||
import { getDeviceList } from "@/api/modules/index";
|
||||
import DeviceInfoComponentTwo from "./DeviceInfoComponentTwo.vue";
|
||||
import Map from "./map.vue"
|
||||
import { emitter } from '@/utils/eventBus';
|
||||
const showServiceArea = ref(false);
|
||||
const showMap = ref(true);
|
||||
const showDeviceInfo = ref(false);
|
||||
const showDeviceInfoTwo = ref(false);
|
||||
const closeDialog = () => {
|
||||
showServiceArea.value = false;
|
||||
};
|
||||
const showDropdown = ref(false);
|
||||
const serviceAreas = ["南皮服务区", "东光服务区"];
|
||||
const selectedServiceArea = ref("南皮服务区");
|
||||
|
||||
function selectServiceArea(area) {
|
||||
selectedServiceArea.value = area;
|
||||
showServiceArea.value = true;
|
||||
showDropdown.value = false;
|
||||
}
|
||||
// 统计数据配置
|
||||
const statistics = ref([
|
||||
{
|
||||
image: km,
|
||||
value: "78.338",
|
||||
label: "全里程(公里)",
|
||||
},
|
||||
{
|
||||
image: toll,
|
||||
value: "5",
|
||||
label: "收费站",
|
||||
},
|
||||
{
|
||||
image: fee,
|
||||
value: "2",
|
||||
label: "服务区",
|
||||
},
|
||||
{
|
||||
image: bridge,
|
||||
value: "7",
|
||||
label: "桥梁(座)",
|
||||
},
|
||||
{
|
||||
image: hub,
|
||||
value: "7",
|
||||
label: "隧道互通(座)",
|
||||
},
|
||||
]);
|
||||
|
||||
// 设备信息数据
|
||||
const deviceInfo = ref({
|
||||
deviceType: '视频监控',
|
||||
cameraType: '枪机',
|
||||
deviceCode: '3_CAM1_0',
|
||||
roadSection: '路段名称',
|
||||
direction: '邯郸方向',
|
||||
stakeNumber: 'K18+324',
|
||||
longitude: '44.230145',
|
||||
latitude: '45.65412',
|
||||
ipAddress: '13.106.2.10'
|
||||
});
|
||||
|
||||
const deviceInfoTwo = ref({
|
||||
deviceType: '视频监控',
|
||||
cameraType: '枪机',
|
||||
deviceCode: '3_CAM1_0',
|
||||
roadSection: '路段名称',
|
||||
direction: '邯郸方向',
|
||||
stakeNumber: 'K18+324',
|
||||
longitude: '44.230145',
|
||||
latitude: '45.65412',
|
||||
ipAddress: '13.106.2.10'
|
||||
});
|
||||
|
||||
// const deviceInfo = ref({});
|
||||
function handleShowDeviceInfo(data) {
|
||||
console.log("接收到设备信息:", data);
|
||||
|
||||
// 关闭其他面板
|
||||
showDeviceInfo.value = false;
|
||||
showDeviceInfoTwo.value = false;
|
||||
|
||||
if (data.deviceTypeId === "22" || data.deviceTypeId === "44") {
|
||||
showDeviceInfo.value = true;
|
||||
} else if (data.deviceTypeId === "15" || data.deviceTypeId === "53") {
|
||||
showDeviceInfoTwo.value = true;
|
||||
}
|
||||
deviceInfoTwo.value = data;
|
||||
deviceInfo.value = data;
|
||||
}
|
||||
const closeDeviceInfo = () => {
|
||||
showDeviceInfo.value = false;
|
||||
showDeviceInfoTwo.value = false;
|
||||
showMap.value = true;
|
||||
};
|
||||
const mapFlag = ref(true);
|
||||
onMounted(() => {
|
||||
emitter.on("show-device-info", handleShowDeviceInfo);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.center-container {
|
||||
width: 100%;
|
||||
// height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
.RealTimeImage{
|
||||
background-image: url("@/assets/img/eqm/fuwuqu.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.map-container.bg-header {
|
||||
position: absolute;
|
||||
top: 500px;
|
||||
left: 42.5%;
|
||||
// transform: translate(-50%, -50%);
|
||||
z-index: 2000;
|
||||
// box-shadow: 0 8px 40px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.bg-header-one {
|
||||
background-image: url("@/assets/img/eqm/eqminfo.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.bg-header-two {
|
||||
background-image: url("@/assets/img/eqm/eqminfo2.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-panel {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
width: 40%;
|
||||
padding: 20px;
|
||||
gap: 30px;
|
||||
padding-top: 200px;
|
||||
padding-bottom: 50px;
|
||||
.box{
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.card-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(0, 170, 255, 0.1) 0%,
|
||||
transparent 100%);
|
||||
border-radius: 8px;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
position: relative;
|
||||
margin-right: 15px;
|
||||
|
||||
img {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
.number {
|
||||
font-size: 64px !important;
|
||||
font-weight: bold;
|
||||
color: #1ae9ee;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 32px !important;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-top: 8px;
|
||||
letter-spacing: 1px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.map-container {
|
||||
width: 78%;
|
||||
margin-top: 30px;
|
||||
// background: linear-gradient(360deg,
|
||||
// rgba(15, 43, 89, 0.3) 0%,
|
||||
// rgba(15, 43, 89, 0) 100%),
|
||||
// rgba(15, 43, 89, 0.85);
|
||||
// box-shadow: inset 0px 0px 57px 0px rgba(0, 149, 255, 0.41),
|
||||
// inset 0px 0px 21px 0px rgba(49, 176, 255, 0.47);
|
||||
// border-radius: 8px;
|
||||
// border: 1px solid;
|
||||
// border-image: linear-gradient(180deg,
|
||||
// rgba(186, 213, 255, 0.3),
|
||||
// rgba(186, 213, 255, 0)) 1 1;
|
||||
// padding: 20px;
|
||||
// box-sizing: border-box;
|
||||
height: 1700px;
|
||||
}
|
||||
|
||||
.map-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding: 15px 40px;
|
||||
position: relative;
|
||||
// border-bottom: 1px solid rgba(26, 233, 238, 0.2);
|
||||
height: 150px;
|
||||
background: repeating-linear-gradient(-65deg,
|
||||
rgba(15, 43, 89, 0.3) 0px,
|
||||
rgba(15, 43, 89, 0.3) 50px,
|
||||
rgba(26, 233, 238, 0.05) 50px,
|
||||
rgba(26, 233, 238, 0.05) 100px);
|
||||
|
||||
.title-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 52px;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
position: relative;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 2px;
|
||||
background-color: #fff;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
&::before {
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
}
|
||||
|
||||
&::after {
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.map-con {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
/* background-color: #1ae9ee; */
|
||||
// 用户可以在这里添加地图相关的内容
|
||||
}
|
||||
|
||||
.eqm-info {
|
||||
width: 1788px;
|
||||
margin-top: 30px;
|
||||
background: linear-gradient(360deg,
|
||||
rgba(15, 43, 89, 0.3) 0%,
|
||||
rgba(15, 43, 89, 0) 100%),
|
||||
rgba(15, 43, 89, 0.85);
|
||||
box-shadow: inset 0px 0px 57px 0px rgba(0, 149, 255, 0.41),
|
||||
inset 0px 0px 21px 0px rgba(49, 176, 255, 0.47);
|
||||
border-radius: 8px;
|
||||
border: 1px solid;
|
||||
border-image: linear-gradient(180deg,
|
||||
rgba(186, 213, 255, 0.3),
|
||||
rgba(186, 213, 255, 0)) 1 1;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
height: 1700px;
|
||||
|
||||
.eqm-title {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
background-image: url("../../assets/img/eqm/eqmtitle.png");
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -190px;
|
||||
background: #112a44;
|
||||
border: 1px solid #1ae9ee;
|
||||
border-radius: 6px;
|
||||
z-index: 100;
|
||||
min-width: 220px;
|
||||
min-height: 120px;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.25);
|
||||
padding: 16px 0;
|
||||
|
||||
.dropdown-item {
|
||||
padding: 20px 32px;
|
||||
font-size: 28px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
|
||||
&:hover {
|
||||
background: #1ae9ee;
|
||||
color: #112a44;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="center-container">
|
||||
<div class="statistics-panel">
|
||||
<div v-for="(stat, index) in statistics" :key="index" class="stat-card" style="position: relative;">
|
||||
<div
|
||||
class="box"
|
||||
@mouseenter="stat.label === '服务区' && (showDropdown = true)"
|
||||
@mouseleave="stat.label === '服务区' && (showDropdown = false)"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div class="icon-wrapper">
|
||||
<img :src="stat.image" :alt="stat.label" />
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-label">
|
||||
{{ stat.label }}
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
<span class="number">{{ stat.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 只在服务区显示下拉选择 -->
|
||||
<template v-if="stat.label === '服务区'">
|
||||
<div
|
||||
v-show="showDropdown"
|
||||
class="dropdown large-dropdown"
|
||||
@mouseenter="showDropdown = true"
|
||||
@mouseleave="showDropdown = false"
|
||||
>
|
||||
<div
|
||||
class="dropdown-item"
|
||||
v-for="area in serviceAreas"
|
||||
:key="area"
|
||||
@click="selectServiceArea(area)"
|
||||
>
|
||||
{{ area }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 下面弹框标题用当前选中的服务区 -->
|
||||
<!-- <div v-if="showServiceArea" class="map-container">
|
||||
<div class="map-title">
|
||||
<div class="title-content">
|
||||
<span class="title-text">{{ selectedServiceArea }}</span>
|
||||
</div>
|
||||
<div class="close-btn" @click="closeDialog">×</div>
|
||||
</div>
|
||||
<div class="map-con">
|
||||
<RealTimeImageCenter />
|
||||
</div>
|
||||
</div> -->
|
||||
<div v-if="showServiceArea" style="width: 5664px;" class="map-container RealTimeImage">
|
||||
<div class="map-title">
|
||||
<div class="title-content">
|
||||
<span class="title-text">{{selectedServiceArea}}</span>
|
||||
</div>
|
||||
<div class="close-btn" @click="closeDialog">×</div>
|
||||
</div>
|
||||
<div class="map-con">
|
||||
<RealTimeImageCenter />
|
||||
</div>
|
||||
</div>
|
||||
<div class="map-container bg-header bg-header-one" style="width: 1800px;" v-if="showDeviceInfo">
|
||||
<div class="map-title">
|
||||
<div class="title-content">
|
||||
<span class="title-text">设备信息(NO:{{ deviceInfo.deviceId }})</span>
|
||||
</div>
|
||||
<div class="close-btn" @click="closeDeviceInfo">×</div>
|
||||
</div>
|
||||
<div class="map-con">
|
||||
<DeviceInfoComponent :deviceInfo="deviceInfo" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="map-container bg-header bg-header-two" style="width: 1800px;height: 1000px;" v-if="showDeviceInfoTwo">
|
||||
<div class="map-title">
|
||||
<div class="title-content">
|
||||
<span class="title-text">设备信息(NO:{{ deviceInfo.deviceId }})</span>
|
||||
</div>
|
||||
<div class="close-btn" @click="closeDeviceInfo">×</div>
|
||||
</div>
|
||||
<div class="map-con">
|
||||
<DeviceInfoComponentTwo :deviceInfo="deviceInfoTwo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import { onMounted } from 'vue';
|
||||
import fee from "@/assets/img/fee.png";
|
||||
import km from "@/assets/img/km.png";
|
||||
import toll from "@/assets/img/toll.png";
|
||||
import RealTimeImageCenter from "./RealTimeImageCenter.vue";
|
||||
import DeviceInfoComponent from "./DeviceInfoComponent.vue";
|
||||
import bridge from "@/assets/img/bridge.png";
|
||||
import hub from "@/assets/img/hub.png";
|
||||
import { getDeviceList } from "@/api/modules/index";
|
||||
import DeviceInfoComponentTwo from "./DeviceInfoComponentTwo.vue";
|
||||
import Map from "./map.vue"
|
||||
import { emitter } from '@/utils/eventBus';
|
||||
const showServiceArea = ref(false);
|
||||
const showMap = ref(true);
|
||||
const showDeviceInfo = ref(false);
|
||||
const showDeviceInfoTwo = ref(false);
|
||||
const closeDialog = () => {
|
||||
showServiceArea.value = false;
|
||||
};
|
||||
const showDropdown = ref(false);
|
||||
const serviceAreas = ["南皮服务区", "东光服务区"];
|
||||
const selectedServiceArea = ref("南皮服务区");
|
||||
|
||||
function selectServiceArea(area) {
|
||||
selectedServiceArea.value = area;
|
||||
showServiceArea.value = true;
|
||||
showDropdown.value = false;
|
||||
}
|
||||
// 统计数据配置
|
||||
const statistics = ref([
|
||||
{
|
||||
image: km,
|
||||
value: "78.338",
|
||||
label: "全里程(公里)",
|
||||
},
|
||||
{
|
||||
image: toll,
|
||||
value: "5",
|
||||
label: "收费站",
|
||||
},
|
||||
{
|
||||
image: fee,
|
||||
value: "2",
|
||||
label: "服务区",
|
||||
},
|
||||
{
|
||||
image: bridge,
|
||||
value: "7",
|
||||
label: "桥梁(座)",
|
||||
},
|
||||
{
|
||||
image: hub,
|
||||
value: "7",
|
||||
label: "隧道互通(座)",
|
||||
},
|
||||
]);
|
||||
|
||||
// 设备信息数据
|
||||
const deviceInfo = ref({
|
||||
deviceType: '视频监控',
|
||||
cameraType: '枪机',
|
||||
deviceCode: '3_CAM1_0',
|
||||
roadSection: '路段名称',
|
||||
direction: '邯郸方向',
|
||||
stakeNumber: 'K18+324',
|
||||
longitude: '44.230145',
|
||||
latitude: '45.65412',
|
||||
ipAddress: '13.106.2.10'
|
||||
});
|
||||
|
||||
const deviceInfoTwo = ref({
|
||||
deviceType: '视频监控',
|
||||
cameraType: '枪机',
|
||||
deviceCode: '3_CAM1_0',
|
||||
roadSection: '路段名称',
|
||||
direction: '邯郸方向',
|
||||
stakeNumber: 'K18+324',
|
||||
longitude: '44.230145',
|
||||
latitude: '45.65412',
|
||||
ipAddress: '13.106.2.10'
|
||||
});
|
||||
|
||||
// const deviceInfo = ref({});
|
||||
function handleShowDeviceInfo(data) {
|
||||
console.log("接收到设备信息:", data);
|
||||
|
||||
// 关闭其他面板
|
||||
showDeviceInfo.value = false;
|
||||
showDeviceInfoTwo.value = false;
|
||||
|
||||
if (data.deviceTypeId === "22" || data.deviceTypeId === "44") {
|
||||
showDeviceInfo.value = true;
|
||||
} else if (data.deviceTypeId === "15" || data.deviceTypeId === "53") {
|
||||
showDeviceInfoTwo.value = true;
|
||||
}
|
||||
deviceInfoTwo.value = data;
|
||||
deviceInfo.value = data;
|
||||
}
|
||||
const closeDeviceInfo = () => {
|
||||
showDeviceInfo.value = false;
|
||||
showDeviceInfoTwo.value = false;
|
||||
showMap.value = true;
|
||||
};
|
||||
const mapFlag = ref(true);
|
||||
onMounted(() => {
|
||||
emitter.on("show-device-info", handleShowDeviceInfo);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.center-container {
|
||||
width: 100%;
|
||||
// height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
.RealTimeImage{
|
||||
background-image: url("@/assets/img/eqm/fuwuqu.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.map-container.bg-header {
|
||||
position: absolute;
|
||||
top: 500px;
|
||||
left: 42.5%;
|
||||
// transform: translate(-50%, -50%);
|
||||
z-index: 2000;
|
||||
// box-shadow: 0 8px 40px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.bg-header-one {
|
||||
background-image: url("@/assets/img/eqm/eqminfo.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.bg-header-two {
|
||||
background-image: url("@/assets/img/eqm/eqminfo2.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-panel {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
width: 40%;
|
||||
padding: 20px;
|
||||
gap: 30px;
|
||||
padding-top: 200px;
|
||||
padding-bottom: 50px;
|
||||
.box{
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.card-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(0, 170, 255, 0.1) 0%,
|
||||
transparent 100%);
|
||||
border-radius: 8px;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
position: relative;
|
||||
margin-right: 15px;
|
||||
|
||||
img {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
.number {
|
||||
font-size: 64px !important;
|
||||
font-weight: bold;
|
||||
color: #1ae9ee;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 32px !important;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-top: 8px;
|
||||
letter-spacing: 1px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.map-container {
|
||||
width: 78%;
|
||||
margin-top: 30px;
|
||||
// background: linear-gradient(360deg,
|
||||
// rgba(15, 43, 89, 0.3) 0%,
|
||||
// rgba(15, 43, 89, 0) 100%),
|
||||
// rgba(15, 43, 89, 0.85);
|
||||
// box-shadow: inset 0px 0px 57px 0px rgba(0, 149, 255, 0.41),
|
||||
// inset 0px 0px 21px 0px rgba(49, 176, 255, 0.47);
|
||||
// border-radius: 8px;
|
||||
// border: 1px solid;
|
||||
// border-image: linear-gradient(180deg,
|
||||
// rgba(186, 213, 255, 0.3),
|
||||
// rgba(186, 213, 255, 0)) 1 1;
|
||||
// padding: 20px;
|
||||
// box-sizing: border-box;
|
||||
height: 1700px;
|
||||
}
|
||||
|
||||
.map-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding: 15px 40px;
|
||||
position: relative;
|
||||
// border-bottom: 1px solid rgba(26, 233, 238, 0.2);
|
||||
height: 150px;
|
||||
background: repeating-linear-gradient(-65deg,
|
||||
rgba(15, 43, 89, 0.3) 0px,
|
||||
rgba(15, 43, 89, 0.3) 50px,
|
||||
rgba(26, 233, 238, 0.05) 50px,
|
||||
rgba(26, 233, 238, 0.05) 100px);
|
||||
|
||||
.title-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 52px;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
position: relative;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 2px;
|
||||
background-color: #fff;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
&::before {
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
}
|
||||
|
||||
&::after {
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.map-con {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
/* background-color: #1ae9ee; */
|
||||
// 用户可以在这里添加地图相关的内容
|
||||
}
|
||||
|
||||
.eqm-info {
|
||||
width: 1788px;
|
||||
margin-top: 30px;
|
||||
background: linear-gradient(360deg,
|
||||
rgba(15, 43, 89, 0.3) 0%,
|
||||
rgba(15, 43, 89, 0) 100%),
|
||||
rgba(15, 43, 89, 0.85);
|
||||
box-shadow: inset 0px 0px 57px 0px rgba(0, 149, 255, 0.41),
|
||||
inset 0px 0px 21px 0px rgba(49, 176, 255, 0.47);
|
||||
border-radius: 8px;
|
||||
border: 1px solid;
|
||||
border-image: linear-gradient(180deg,
|
||||
rgba(186, 213, 255, 0.3),
|
||||
rgba(186, 213, 255, 0)) 1 1;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
height: 1700px;
|
||||
|
||||
.eqm-title {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
background-image: url("../../assets/img/eqm/eqmtitle.png");
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -190px;
|
||||
background: #112a44;
|
||||
border: 1px solid #1ae9ee;
|
||||
border-radius: 6px;
|
||||
z-index: 100;
|
||||
min-width: 220px;
|
||||
min-height: 120px;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.25);
|
||||
padding: 16px 0;
|
||||
|
||||
.dropdown-item {
|
||||
padding: 20px 32px;
|
||||
font-size: 28px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
|
||||
&:hover {
|
||||
background: #1ae9ee;
|
||||
color: #112a44;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,415 +1,415 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import EventStatus from "./EventStatus.vue";
|
||||
import EventTask from "./EventTask.vue";
|
||||
import RealTimeImage from "./RealTimeImage.vue";
|
||||
import { weatherForecast, weatherHourly, todayStatusCount, todayHourly, getDictData } from "@/api/modules/index";
|
||||
import { useTodayTime, } from "@/utils/packge";
|
||||
|
||||
const { todayTime, getTodayTime } = useTodayTime();
|
||||
// 模拟数据,实际项目中可能需要从API获取
|
||||
const pendingCount = ref<string>('');
|
||||
const processingCount = ref<string>('');
|
||||
const serviceArea = ref<any[]>([]);
|
||||
const Hub = ref<any[]>([]);
|
||||
const Intercommunication = ref<any[]>([]);
|
||||
// 服务区数据
|
||||
const selectedServiceArea = ref("全部");
|
||||
const serviceAreas = ref([]);
|
||||
// 服务区选择变更处理
|
||||
const handleServiceAreaChange = (area: string) => {
|
||||
selectedServiceArea.value = area;
|
||||
};
|
||||
|
||||
// 区域类型选择状态(服务区、互通、枢纽)
|
||||
const areaTypes = ref([
|
||||
{ id: "service", name: "服务区", selected: true },
|
||||
{ id: "interchange", name: "互通", selected: false },
|
||||
{ id: "hub", name: "枢纽", selected: false },
|
||||
]);
|
||||
|
||||
// 切换区域类型选中状态
|
||||
const toggleAreaType = (id: string) => {
|
||||
areaTypes.value.forEach((type) => {
|
||||
type.selected = type.id === id;
|
||||
});
|
||||
};
|
||||
onMounted(()=>{
|
||||
todayStatusCount({todayTime: todayTime.value}).then((res: any) => {
|
||||
if(res.code === 200){
|
||||
res.data.forEach((item: any) => {
|
||||
if(item.status == '0'){ //待处理
|
||||
pendingCount.value = item.count || 0
|
||||
} else if(item.status == '2'){ //处置中
|
||||
processingCount.value = item.count || 0
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
getDictData("hb_service_area").then((res: any) => { //服务区
|
||||
if (res.code === 200) {
|
||||
serviceArea.value = res.data || [];
|
||||
}
|
||||
});
|
||||
getDictData("hb_hub").then((res: any) => { //枢纽
|
||||
if (res.code === 200) {
|
||||
Hub.value = res.data || [];
|
||||
}
|
||||
});
|
||||
getDictData("hb_interchange").then((res: any) => { //互通
|
||||
if (res.code === 200) {
|
||||
Intercommunication.value = res.data || [];
|
||||
}
|
||||
});
|
||||
})
|
||||
const currentAreaList = computed(() => {
|
||||
const selectedType = areaTypes.value.find((type) => type.selected);
|
||||
if (selectedType?.id === "service") {
|
||||
return serviceArea.value;
|
||||
} else if (selectedType?.id === "interchange") {
|
||||
return Intercommunication.value;
|
||||
} else if (selectedType?.id === "hub") {
|
||||
return Hub.value;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const isDropdownOpen = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="traffic-flow-grid">
|
||||
<div class="top">
|
||||
<div class="traffic-flow-item">
|
||||
<div style="margin-left: 396px" class="item-title">今日事件处理</div>
|
||||
<div style="margin-left: 396px" class="item-content">
|
||||
<EventStatus
|
||||
:pending-count="pendingCount"
|
||||
:processing-count="processingCount"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="traffic-flow-item">
|
||||
<div class="item-title">事件处置任务</div>
|
||||
<div class="item-content">
|
||||
<EventTask />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="item-title">
|
||||
<div class="title-text">实时图像</div>
|
||||
|
||||
<div class="area-type-selector">
|
||||
<div
|
||||
v-for="type in areaTypes"
|
||||
:key="type.id"
|
||||
class="area-type-item"
|
||||
:class="{ active: type.selected }"
|
||||
@click="toggleAreaType(type.id)"
|
||||
>
|
||||
{{ type.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="service-area-selector">
|
||||
<div class="selected-area" @click="isDropdownOpen = !isDropdownOpen">
|
||||
{{ selectedServiceArea }} <span class="arrow-down">▼</span>
|
||||
</div>
|
||||
<div class="dropdown-menu" v-show="isDropdownOpen">
|
||||
<div
|
||||
v-for="(item, index) in currentAreaList"
|
||||
:key="index"
|
||||
class="dropdown-item"
|
||||
:class="{ active: selectedServiceArea === item.dictLabel }"
|
||||
@click="
|
||||
handleServiceAreaChange(item.dictLabel);
|
||||
isDropdownOpen = false;
|
||||
"
|
||||
>
|
||||
{{ item.dictLabel }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<RealTimeImage :service-area="selectedServiceArea" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.traffic-flow-grid {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: 20px;
|
||||
// justify-content: space-between;
|
||||
// align-items: flex-start;
|
||||
.top {
|
||||
width: 100%;
|
||||
height: 652px;
|
||||
display: flex;
|
||||
.traffic-flow-item {
|
||||
flex: 1;
|
||||
.item-title {
|
||||
width: 1080px;
|
||||
height: 104px;
|
||||
background-image: url("../../assets/img/leftoright/title.png");
|
||||
margin-left: 48px;
|
||||
margin-top: 48px;
|
||||
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
|
||||
font-weight: 700;
|
||||
font-size: 52px;
|
||||
color: #ffffff;
|
||||
line-height: 104px;
|
||||
padding-left: 56px;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0px 4px 0px #095a8c;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
.item-content {
|
||||
width: 1080px;
|
||||
height: 468px;
|
||||
margin-left: 48px;
|
||||
margin-top: 32px;
|
||||
background-image: url("../../assets/img/leftoright/rightcontent.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-top: 48px;
|
||||
.item-title {
|
||||
width: 2208px;
|
||||
height: 104px;
|
||||
margin-left: 396px;
|
||||
background-image: url("../../assets/img/leftoright/bigtitle.png");
|
||||
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
|
||||
font-weight: 700;
|
||||
font-size: 52px;
|
||||
color: #ffffff;
|
||||
line-height: 104px;
|
||||
padding-left: 56px;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0px 4px 0px #095a8c;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding-right: 56px;
|
||||
|
||||
.title-text {
|
||||
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
|
||||
font-weight: 700;
|
||||
font-size: 52px;
|
||||
color: #ffffff;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0px 4px 0px #095a8c;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.area-type-selector {
|
||||
position: absolute;
|
||||
right: 320px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
font-size: 32px;
|
||||
font-weight: normal;
|
||||
color: #ffffff;
|
||||
text-shadow: none;
|
||||
z-index: 99;
|
||||
|
||||
.area-type-item {
|
||||
width: 150px;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
color: #bad5ff;
|
||||
position: relative;
|
||||
border: 1px solid #bad5ff;
|
||||
border-radius: 4px;
|
||||
background-color: #304b75;
|
||||
box-shadow: inset 0 0 10px rgba(0, 79, 127, 0.3),
|
||||
0 0 5px rgba(0, 198, 255, 0.2);
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(0, 198, 255, 0.1) 0%,
|
||||
rgba(0, 118, 170, 0.05) 40%,
|
||||
transparent 100%
|
||||
);
|
||||
z-index: -1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #acc0df;
|
||||
border: 1px solid rgba(0, 198, 255, 0.7);
|
||||
box-shadow: inset 0 0 15px rgba(0, 79, 127, 0.6),
|
||||
0 0 10px rgba(0, 198, 255, 0.5);
|
||||
background-color: rgba(0, 67, 123, 0.7);
|
||||
text-shadow: 0 0 8px rgba(0, 198, 255, 0.5);
|
||||
|
||||
&::before {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(0, 198, 255, 0.3) 0%,
|
||||
rgba(0, 118, 170, 0.15) 50%,
|
||||
rgba(0, 79, 127, 0.05) 100%
|
||||
);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(0, 198, 255, 0.8) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover:not(.active) {
|
||||
border-color: rgba(0, 198, 255, 0.5);
|
||||
box-shadow: inset 0 0 12px rgba(0, 79, 127, 0.4),
|
||||
0 0 8px rgba(0, 198, 255, 0.3);
|
||||
background-color: rgba(32, 64, 113, 0.6);
|
||||
color: #d8e8ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.service-area-selector {
|
||||
position: absolute;
|
||||
right: 56px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 32px;
|
||||
font-weight: normal;
|
||||
color: #ffffff;
|
||||
display: inline-block;
|
||||
text-shadow: none;
|
||||
z-index: 99;
|
||||
|
||||
.selected-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #304b75;
|
||||
border: 1px solid #718cb6;
|
||||
border-radius: 4px;
|
||||
padding: 0 20px;
|
||||
height: 64px;
|
||||
cursor: pointer;
|
||||
width: 240px;
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
|
||||
.arrow-down {
|
||||
font-size: 18px;
|
||||
color: #ffffff;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 66px;
|
||||
right: 0;
|
||||
background: #003366;
|
||||
border: 1px solid rgba(0, 198, 255, 0.5);
|
||||
border-radius: 0 0 4px 4px;
|
||||
width: 240px;
|
||||
z-index: 100;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
padding-top: 8px;
|
||||
transform-origin: top right;
|
||||
|
||||
/* 自定义滚动条样式 */
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: rgba(0, 27, 61, 0.3);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 198, 255, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0, 198, 255, 0.7);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
height: 1px;
|
||||
border-top: 1px dashed rgba(0, 198, 255, 0.5);
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
text-align: center;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background: #304b75;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #00c6ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.item-content {
|
||||
width: 2208px;
|
||||
height: 1428px;
|
||||
margin-left: 396px;
|
||||
margin-top: 32px;
|
||||
background-image: url("../../assets/img/leftoright/rbcontent.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import EventStatus from "./EventStatus.vue";
|
||||
import EventTask from "./EventTask.vue";
|
||||
import RealTimeImage from "./RealTimeImage.vue";
|
||||
import { weatherForecast, weatherHourly, todayStatusCount, todayHourly, getDictData } from "@/api/modules/index";
|
||||
import { useTodayTime, } from "@/utils/packge";
|
||||
|
||||
const { todayTime, getTodayTime } = useTodayTime();
|
||||
// 模拟数据,实际项目中可能需要从API获取
|
||||
const pendingCount = ref<string>('');
|
||||
const processingCount = ref<string>('');
|
||||
const serviceArea = ref<any[]>([]);
|
||||
const Hub = ref<any[]>([]);
|
||||
const Intercommunication = ref<any[]>([]);
|
||||
// 服务区数据
|
||||
const selectedServiceArea = ref("全部");
|
||||
const serviceAreas = ref([]);
|
||||
// 服务区选择变更处理
|
||||
const handleServiceAreaChange = (area: string) => {
|
||||
selectedServiceArea.value = area;
|
||||
};
|
||||
|
||||
// 区域类型选择状态(服务区、互通、枢纽)
|
||||
const areaTypes = ref([
|
||||
{ id: "service", name: "服务区", selected: true },
|
||||
{ id: "interchange", name: "互通", selected: false },
|
||||
{ id: "hub", name: "枢纽", selected: false },
|
||||
]);
|
||||
|
||||
// 切换区域类型选中状态
|
||||
const toggleAreaType = (id: string) => {
|
||||
areaTypes.value.forEach((type) => {
|
||||
type.selected = type.id === id;
|
||||
});
|
||||
};
|
||||
onMounted(()=>{
|
||||
todayStatusCount({todayTime: todayTime.value}).then((res: any) => {
|
||||
if(res.code === 200){
|
||||
res.data.forEach((item: any) => {
|
||||
if(item.status == '0'){ //待处理
|
||||
pendingCount.value = item.count || 0
|
||||
} else if(item.status == '2'){ //处置中
|
||||
processingCount.value = item.count || 0
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
getDictData("hb_service_area").then((res: any) => { //服务区
|
||||
if (res.code === 200) {
|
||||
serviceArea.value = res.data || [];
|
||||
}
|
||||
});
|
||||
getDictData("hb_hub").then((res: any) => { //枢纽
|
||||
if (res.code === 200) {
|
||||
Hub.value = res.data || [];
|
||||
}
|
||||
});
|
||||
getDictData("hb_interchange").then((res: any) => { //互通
|
||||
if (res.code === 200) {
|
||||
Intercommunication.value = res.data || [];
|
||||
}
|
||||
});
|
||||
})
|
||||
const currentAreaList = computed(() => {
|
||||
const selectedType = areaTypes.value.find((type) => type.selected);
|
||||
if (selectedType?.id === "service") {
|
||||
return serviceArea.value;
|
||||
} else if (selectedType?.id === "interchange") {
|
||||
return Intercommunication.value;
|
||||
} else if (selectedType?.id === "hub") {
|
||||
return Hub.value;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const isDropdownOpen = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="traffic-flow-grid">
|
||||
<div class="top">
|
||||
<div class="traffic-flow-item">
|
||||
<div style="margin-left: 396px" class="item-title">今日事件处理</div>
|
||||
<div style="margin-left: 396px" class="item-content">
|
||||
<EventStatus
|
||||
:pending-count="pendingCount"
|
||||
:processing-count="processingCount"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="traffic-flow-item">
|
||||
<div class="item-title">事件处置任务</div>
|
||||
<div class="item-content">
|
||||
<EventTask />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="item-title">
|
||||
<div class="title-text">实时图像</div>
|
||||
|
||||
<div class="area-type-selector">
|
||||
<div
|
||||
v-for="type in areaTypes"
|
||||
:key="type.id"
|
||||
class="area-type-item"
|
||||
:class="{ active: type.selected }"
|
||||
@click="toggleAreaType(type.id)"
|
||||
>
|
||||
{{ type.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="service-area-selector">
|
||||
<div class="selected-area" @click="isDropdownOpen = !isDropdownOpen">
|
||||
{{ selectedServiceArea }} <span class="arrow-down">▼</span>
|
||||
</div>
|
||||
<div class="dropdown-menu" v-show="isDropdownOpen">
|
||||
<div
|
||||
v-for="(item, index) in currentAreaList"
|
||||
:key="index"
|
||||
class="dropdown-item"
|
||||
:class="{ active: selectedServiceArea === item.dictLabel }"
|
||||
@click="
|
||||
handleServiceAreaChange(item.dictLabel);
|
||||
isDropdownOpen = false;
|
||||
"
|
||||
>
|
||||
{{ item.dictLabel }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<RealTimeImage :service-area="selectedServiceArea" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.traffic-flow-grid {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: 20px;
|
||||
// justify-content: space-between;
|
||||
// align-items: flex-start;
|
||||
.top {
|
||||
width: 100%;
|
||||
height: 652px;
|
||||
display: flex;
|
||||
.traffic-flow-item {
|
||||
flex: 1;
|
||||
.item-title {
|
||||
width: 1080px;
|
||||
height: 104px;
|
||||
background-image: url("../../assets/img/leftoright/title.png");
|
||||
margin-left: 48px;
|
||||
margin-top: 48px;
|
||||
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
|
||||
font-weight: 700;
|
||||
font-size: 52px;
|
||||
color: #ffffff;
|
||||
line-height: 104px;
|
||||
padding-left: 56px;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0px 4px 0px #095a8c;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
.item-content {
|
||||
width: 1080px;
|
||||
height: 468px;
|
||||
margin-left: 48px;
|
||||
margin-top: 32px;
|
||||
background-image: url("../../assets/img/leftoright/rightcontent.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-top: 48px;
|
||||
.item-title {
|
||||
width: 2208px;
|
||||
height: 104px;
|
||||
margin-left: 396px;
|
||||
background-image: url("../../assets/img/leftoright/bigtitle.png");
|
||||
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
|
||||
font-weight: 700;
|
||||
font-size: 52px;
|
||||
color: #ffffff;
|
||||
line-height: 104px;
|
||||
padding-left: 56px;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0px 4px 0px #095a8c;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding-right: 56px;
|
||||
|
||||
.title-text {
|
||||
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
|
||||
font-weight: 700;
|
||||
font-size: 52px;
|
||||
color: #ffffff;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0px 4px 0px #095a8c;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.area-type-selector {
|
||||
position: absolute;
|
||||
right: 320px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
font-size: 32px;
|
||||
font-weight: normal;
|
||||
color: #ffffff;
|
||||
text-shadow: none;
|
||||
z-index: 99;
|
||||
|
||||
.area-type-item {
|
||||
width: 150px;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
color: #bad5ff;
|
||||
position: relative;
|
||||
border: 1px solid #bad5ff;
|
||||
border-radius: 4px;
|
||||
background-color: #304b75;
|
||||
box-shadow: inset 0 0 10px rgba(0, 79, 127, 0.3),
|
||||
0 0 5px rgba(0, 198, 255, 0.2);
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(0, 198, 255, 0.1) 0%,
|
||||
rgba(0, 118, 170, 0.05) 40%,
|
||||
transparent 100%
|
||||
);
|
||||
z-index: -1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #acc0df;
|
||||
border: 1px solid rgba(0, 198, 255, 0.7);
|
||||
box-shadow: inset 0 0 15px rgba(0, 79, 127, 0.6),
|
||||
0 0 10px rgba(0, 198, 255, 0.5);
|
||||
background-color: rgba(0, 67, 123, 0.7);
|
||||
text-shadow: 0 0 8px rgba(0, 198, 255, 0.5);
|
||||
|
||||
&::before {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(0, 198, 255, 0.3) 0%,
|
||||
rgba(0, 118, 170, 0.15) 50%,
|
||||
rgba(0, 79, 127, 0.05) 100%
|
||||
);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(0, 198, 255, 0.8) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover:not(.active) {
|
||||
border-color: rgba(0, 198, 255, 0.5);
|
||||
box-shadow: inset 0 0 12px rgba(0, 79, 127, 0.4),
|
||||
0 0 8px rgba(0, 198, 255, 0.3);
|
||||
background-color: rgba(32, 64, 113, 0.6);
|
||||
color: #d8e8ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.service-area-selector {
|
||||
position: absolute;
|
||||
right: 56px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 32px;
|
||||
font-weight: normal;
|
||||
color: #ffffff;
|
||||
display: inline-block;
|
||||
text-shadow: none;
|
||||
z-index: 99;
|
||||
|
||||
.selected-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #304b75;
|
||||
border: 1px solid #718cb6;
|
||||
border-radius: 4px;
|
||||
padding: 0 20px;
|
||||
height: 64px;
|
||||
cursor: pointer;
|
||||
width: 240px;
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
|
||||
.arrow-down {
|
||||
font-size: 18px;
|
||||
color: #ffffff;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 66px;
|
||||
right: 0;
|
||||
background: #003366;
|
||||
border: 1px solid rgba(0, 198, 255, 0.5);
|
||||
border-radius: 0 0 4px 4px;
|
||||
width: 240px;
|
||||
z-index: 100;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
padding-top: 8px;
|
||||
transform-origin: top right;
|
||||
|
||||
/* 自定义滚动条样式 */
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: rgba(0, 27, 61, 0.3);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 198, 255, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0, 198, 255, 0.7);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
height: 1px;
|
||||
border-top: 1px dashed rgba(0, 198, 255, 0.5);
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
text-align: center;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background: #304b75;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #00c6ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.item-content {
|
||||
width: 2208px;
|
||||
height: 1428px;
|
||||
margin-left: 396px;
|
||||
margin-top: 32px;
|
||||
background-image: url("../../assets/img/leftoright/rbcontent.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
// const defaultTheme = require('tailwindcss/defaultTheme')
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{vue,js,ts}'],
|
||||
theme: {
|
||||
extend: {
|
||||
// fontFamily: {
|
||||
// sans: ['"Inter var"', ...defaultTheme.fontFamily.sans],
|
||||
// },
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
// const defaultTheme = require('tailwindcss/defaultTheme')
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{vue,js,ts}'],
|
||||
theme: {
|
||||
extend: {
|
||||
// fontFamily: {
|
||||
// sans: ['"Inter var"', ...defaultTheme.fontFamily.sans],
|
||||
// },
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"exclude": ["node_modules","public"],
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"config/**/*.ts",
|
||||
"config/**/*.d.ts",
|
||||
]
|
||||
}
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"exclude": ["node_modules","public"],
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"config/**/*.ts",
|
||||
"config/**/*.d.ts",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,46 +1,46 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
],
|
||||
"baseUrl": "./",
|
||||
"types": [
|
||||
"node",
|
||||
"vite/client",
|
||||
"element-plus/global"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
],
|
||||
"api/*": [
|
||||
"src/api/*"
|
||||
],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"types/env.d.ts",
|
||||
"types/*.d.ts"
|
||||
],
|
||||
"exclude": ["node_modules","public"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.config.json"
|
||||
}
|
||||
]
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
],
|
||||
"baseUrl": "./",
|
||||
"types": [
|
||||
"node",
|
||||
"vite/client",
|
||||
"element-plus/global"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
],
|
||||
"api/*": [
|
||||
"src/api/*"
|
||||
],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"types/env.d.ts",
|
||||
"types/*.d.ts"
|
||||
],
|
||||
"exclude": ["node_modules","public"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.config.json"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
declare module "*.vue" {
|
||||
import { defineComponent } from "vue";
|
||||
const Component: ReturnType<typeof defineComponent>;
|
||||
export default Component;
|
||||
}
|
||||
|
||||
declare module "*.vue" {
|
||||
import { defineComponent } from "vue";
|
||||
const Component: ReturnType<typeof defineComponent>;
|
||||
export default Component;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
interface Window {
|
||||
$message: any
|
||||
}
|
||||
|
||||
interface Window {
|
||||
$message: any
|
||||
}
|
||||
|
||||
type TimeProp= NodeJS.Timeout
|
170
vite.config.ts
170
vite.config.ts
|
@ -1,86 +1,86 @@
|
|||
/*
|
||||
* @Author: 996555510 65213605+996555510@users.noreply.github.com
|
||||
* @Date: 2025-05-22 10:12:42
|
||||
* @LastEditors: 996555510 65213605+996555510@users.noreply.github.com
|
||||
* @LastEditTime: 2025-05-23 15:55:37
|
||||
* @FilePath: \IofTV-Screen-Vue3-master\vite.config.ts
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
*/
|
||||
|
||||
/// <reference types="vite/client" />
|
||||
import type { UserConfig, ConfigEnv } from 'vite';
|
||||
import { defineConfig, loadEnv } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { resolve } from "path";
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
//https://github.com/element-plus/unplugin-element-plus/blob/HEAD/README.zh-CN.md
|
||||
import ElementPlus from 'unplugin-element-plus/vite'
|
||||
import {createHtmlPlugin} from 'vite-plugin-html';
|
||||
export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
|
||||
|
||||
const viteEnv = loadEnv(mode, process.cwd());
|
||||
|
||||
return {
|
||||
base: '/',
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({ resolvers: [ElementPlusResolver()] }),
|
||||
Components({ resolvers: [ElementPlusResolver()] }),
|
||||
ElementPlus(),
|
||||
createHtmlPlugin({
|
||||
inject: {
|
||||
data: {
|
||||
injectMapScript: `<script src="./public/static/map_load.js"></script>`,
|
||||
},
|
||||
},
|
||||
minify: true,
|
||||
}),
|
||||
// Compression({
|
||||
// algorithm: 'gzip',
|
||||
// test: /\.(js|css|html|json|svg|jpe?g|png|gif)$/i,
|
||||
// threshold: 1024,
|
||||
// deleteOriginalAssets: false
|
||||
// }),
|
||||
// svgr()
|
||||
],
|
||||
publicDir: "public",
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
assetsDir: 'static'
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 8080,
|
||||
open: true,
|
||||
strictPort: false,
|
||||
proxy: {
|
||||
[viteEnv.VITE_APP_CONTROL_BASE_API]: {
|
||||
target: 'http://172.16.1.128:8090/xjIotApi',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^${viteEnv.VITE_APP_CONTROL_BASE_API}`), ''),
|
||||
},
|
||||
[viteEnv.VITE_APP_BASE_API]: {
|
||||
target: 'http://172.16.1.128:8090/iotApi',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^${viteEnv.VITE_APP_BASE_API}`), ''),
|
||||
}
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": resolve(__dirname, "./src"),
|
||||
"components": resolve(__dirname, "./src/components"),
|
||||
"api": resolve(__dirname, "./src/api")
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: `@use "./src/assets/css/variable.scss" as *;`,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
/*
|
||||
* @Author: 996555510 65213605+996555510@users.noreply.github.com
|
||||
* @Date: 2025-05-22 10:12:42
|
||||
* @LastEditors: 996555510 65213605+996555510@users.noreply.github.com
|
||||
* @LastEditTime: 2025-05-23 15:55:37
|
||||
* @FilePath: \IofTV-Screen-Vue3-master\vite.config.ts
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
*/
|
||||
|
||||
/// <reference types="vite/client" />
|
||||
import type { UserConfig, ConfigEnv } from 'vite';
|
||||
import { defineConfig, loadEnv } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { resolve } from "path";
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
//https://github.com/element-plus/unplugin-element-plus/blob/HEAD/README.zh-CN.md
|
||||
import ElementPlus from 'unplugin-element-plus/vite'
|
||||
import {createHtmlPlugin} from 'vite-plugin-html';
|
||||
export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
|
||||
|
||||
const viteEnv = loadEnv(mode, process.cwd());
|
||||
|
||||
return {
|
||||
base: '/',
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({ resolvers: [ElementPlusResolver()] }),
|
||||
Components({ resolvers: [ElementPlusResolver()] }),
|
||||
ElementPlus(),
|
||||
createHtmlPlugin({
|
||||
inject: {
|
||||
data: {
|
||||
injectMapScript: `<script src="./public/static/map_load.js"></script>`,
|
||||
},
|
||||
},
|
||||
minify: true,
|
||||
}),
|
||||
// Compression({
|
||||
// algorithm: 'gzip',
|
||||
// test: /\.(js|css|html|json|svg|jpe?g|png|gif)$/i,
|
||||
// threshold: 1024,
|
||||
// deleteOriginalAssets: false
|
||||
// }),
|
||||
// svgr()
|
||||
],
|
||||
publicDir: "public",
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
assetsDir: 'static'
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 8080,
|
||||
open: true,
|
||||
strictPort: false,
|
||||
proxy: {
|
||||
[viteEnv.VITE_APP_CONTROL_BASE_API]: {
|
||||
target: 'http://172.16.1.133:8081/xjIotApi',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^${viteEnv.VITE_APP_CONTROL_BASE_API}`), ''),
|
||||
},
|
||||
[viteEnv.VITE_APP_BASE_API]: {
|
||||
target: 'http://172.16.1.133:8081/iotApi',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^${viteEnv.VITE_APP_BASE_API}`), ''),
|
||||
}
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": resolve(__dirname, "./src"),
|
||||
"components": resolve(__dirname, "./src/components"),
|
||||
"api": resolve(__dirname, "./src/api")
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: `@use "./src/assets/css/variable.scss" as *;`,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
});
|
Loading…
Reference in New Issue