代码提交

This commit is contained in:
lixiaobang 2025-06-10 09:41:33 +08:00
parent 01d8de7f11
commit 0f1740f301
29 changed files with 10912 additions and 10905 deletions

46
.gitignore vendored
View File

@ -1,23 +1,23 @@
.DS_Store .DS_Store
node_modules/ node_modules/
dist/ dist/
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
**/*.log **/*.log
tests/**/coverage/ tests/**/coverage/
tests/e2e/reports tests/e2e/reports
selenium-debug.log selenium-debug.log
# Editor directories and files # Editor directories and files
.idea .idea
.vscode .vscode
*.suo *.suo
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
*.local *.local
package-lock.json package-lock.json
yarn.lock yarn.lock

View File

@ -1,3 +1,3 @@
{ {
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
} }

52
.vscode/settings.json vendored
View File

@ -1,26 +1,26 @@
{ {
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": { "[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[json]": { "[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[vue]": { "[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[css]": { "[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[less]": { "[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[scss]": { "[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"i18n-ally.localesPaths": ["src/assets/locales"] "i18n-ally.localesPaths": ["src/assets/locales"]
} }

42
LICENSE
View File

@ -1,21 +1,21 @@
MIT License MIT License
Copyright (c) 2022 daidai Copyright (c) 2022 daidai
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

626
README.md
View File

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

2
env.d.ts vendored
View File

@ -1 +1 @@
/// <reference types="vite/client" /> /// <reference types="vite/client" />

View File

@ -1,19 +1,19 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>IofTV-Screen-Vue3</title> <title>IofTV-Screen-Vue3</title>
<script <script
type="text/javascript" type="text/javascript"
src="<%= BASE_URL %>static/map_load.js" src="<%= BASE_URL %>static/map_load.js"
></script> ></script>
<!-- <script src="https://api.map.baidu.com/api?v=3.0&ak=a76Liq52IWX8S5WkSi2z5WvUYRMaNMRe"></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> --> <script src="https://api.map.baidu.com/getscript?v=3.0&ak=a76Liq52IWX8S5WkSi2z5WvUYRMaNMRe&s=1"></script> -->
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

11702
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +1,48 @@
{ {
"name": "test", "name": "test",
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build:old": "run-p type-check build-only", "build:old": "run-p type-check build-only",
"build": "vite build", "build": "vite build",
"preview": "vite preview --port 4173", "preview": "vite preview --port 4173",
"build-only": "vite build", "build-only": "vite build",
"type-check": "vue-tsc --noEmit" "type-check": "vue-tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"axios": "^1.6.8", "axios": "^1.6.8",
"countup.js": "^2.8.0", "countup.js": "^2.8.0",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"echarts": "^5.5.0", "echarts": "^5.5.0",
"element-plus": "^2.6.2", "element-plus": "^2.6.2",
"hls.js": "^1.6.5", "hls.js": "^1.6.5",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vite-plugin-html": "^3.2.2", "vite-plugin-html": "^3.2.2",
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-echarts": "^6.6.9", "vue-echarts": "^6.6.9",
"vue-router": "^4.3.0" "vue-router": "^4.3.0"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/mockjs": "^1.0.10", "@types/mockjs": "^1.0.10",
"@types/node": "^20.11.30", "@types/node": "^20.11.30",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vue/tsconfig": "^0.5.1", "@vue/tsconfig": "^0.5.1",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^8.4.38", "postcss": "^8.4.38",
"sass": "^1.72.0", "sass": "^1.72.0",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.3",
"typescript": "~5.4.3", "typescript": "~5.4.3",
"unplugin-auto-import": "^0.17.5", "unplugin-auto-import": "^0.17.5",
"unplugin-element-plus": "^0.8.0", "unplugin-element-plus": "^0.8.0",
"unplugin-vue-components": "^0.26.0", "unplugin-vue-components": "^0.26.0",
"vite": "^5.2.6", "vite": "^5.2.6",
"vue-tsc": "^2.0.7" "vue-tsc": "^2.0.7"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
export default { export default {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
}, },
} }

View File

@ -4,13 +4,20 @@
var bmapcfg = { var bmapcfg = {
'imgext': '.jpg', //瓦片图的后缀 ------ 根据需要修改,一般是 .png .jpg 'imgext': '.jpg', //瓦片图的后缀 ------ 根据需要修改,一般是 .png .jpg
//这里我直接用的路径是/static/bmap_offline_demo/tiles如果瓦片数量较大可改为瓦片服务的地址 //这里我直接用的路径是/static/bmap_offline_demo/tiles如果瓦片数量较大可改为瓦片服务的地址
tiles_dir: "images", //普通瓦片图的地址,为空默认在 offlinemap/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_dir: "map", //普通瓦片图的地址,为空默认在 offlinemap/tiles/ 目
tiles_self : 'http://172.16.1.162:8123/HeBeiDarkMap/' //自定义图层的地址,为空默认在 offlinemap/tiles_self/ 目录 // 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_path: "http://111.229.30.246:4008/HeBeiDarkMap/",
// tiles_hybrid: "http://111.229.30.246:4008/HeBeiDarkMap/", //卫星瓦片图的地址,为空默认在 offlinemap/tiles_hybrid/ 目 // 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_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/ 目录
}; };
//下面的保持不动/// //下面的保持不动///

View File

@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { RouterView } from 'vue-router' import { RouterView } from 'vue-router'
</script> </script>
<template> <template>
<RouterView /> <RouterView />
</template> </template>
<style scoped> <style scoped>
</style> </style>

View File

@ -1,3 +1,3 @@
import MessageContent from './index.vue'; import MessageContent from './index.vue';
export default MessageContent ; export default MessageContent ;

View File

@ -1,8 +1,8 @@
<!-- eslint-disable vue/valid-template-root --> <!-- eslint-disable vue/valid-template-root -->
<template></template> <template></template>
<script lang="ts" setup> <script lang="ts" setup>
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
// window 便js使 // window 便js使
window['$message'] = ElMessage window['$message'] = ElMessage
</script> </script>

View File

@ -1,20 +1,20 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import '@/assets/css/main.scss' import '@/assets/css/main.scss'
import '@/assets/css/tailwind.css' import '@/assets/css/tailwind.css'
import {registerEcharts} from "@/plugins/echarts" import {registerEcharts} from "@/plugins/echarts"
//不使用mock 请注释掉 //不使用mock 请注释掉
import { mockXHR } from "@/mock/index"; import { mockXHR } from "@/mock/index";
mockXHR() mockXHR()
const app = createApp(App) const app = createApp(App)
registerEcharts(app) registerEcharts(app)
app.use(createPinia()) app.use(createPinia())
app.use(router) app.use(router)
app.mount('#app') app.mount('#app')

View File

@ -1,30 +1,30 @@
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router' import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
import type {RouteRecordRaw} from "vue-router" import type {RouteRecordRaw} from "vue-router"
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
path: '/', path: '/',
redirect: '/index', redirect: '/index',
}, },
{ {
path: '/home', path: '/home',
name: 'home', name: 'home',
component: () => import('@/views/HomeView.vue'), component: () => import('@/views/HomeView.vue'),
children:[ children:[
{ {
path: '/index', path: '/index',
name: 'index', name: 'index',
component: () => import('@/views/index/index.vue'), component: () => import('@/views/index/index.vue'),
} }
] ]
}, },
] ]
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL), history: createWebHashHistory(import.meta.env.BASE_URL),
routes, routes,
}) })
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
next(); next();
}) })
export default router export default router

View File

@ -1,2 +1,2 @@
import mitt from 'mitt'; import mitt from 'mitt';
export const emitter = mitt(); export const emitter = mitt();

View File

@ -1,27 +1,27 @@
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
/** /**
* YYYY-MM-DD * YYYY-MM-DD
*/ */
export const useTodayTime = () => { export const useTodayTime = () => {
const todayTime = ref<string>(""); const todayTime = ref<string>("");
const getTodayTime = () => { const getTodayTime = () => {
const date = new Date(); const date = new Date();
const year = date.getFullYear(); const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始 const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始
const day = String(date.getDate()).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0');
const weekDay = date.toLocaleDateString('zh-CN', { weekday: 'long' }); // 星期几 const weekDay = date.toLocaleDateString('zh-CN', { weekday: 'long' }); // 星期几
todayTime.value = `${year}-${month}-${day}`; todayTime.value = `${year}-${month}-${day}`;
}; };
onMounted(() => { onMounted(() => {
getTodayTime(); getTodayTime();
}); });
return { return {
todayTime, todayTime,
getTodayTime, getTodayTime,
}; };
}; };

View File

@ -1,102 +1,102 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { RouterView } from "vue-router"; import { RouterView } from "vue-router";
import ScaleScreen from "@/components/scale-screen"; import ScaleScreen from "@/components/scale-screen";
import Left from "./component/left.vue"; import Left from "./component/left.vue";
import Right from "./component/right.vue"; import Right from "./component/right.vue";
import Center from "./component/center.vue"; import Center from "./component/center.vue";
import Headers from "./header.vue"; import Headers from "./header.vue";
import Setting from "./setting.vue"; import Setting from "./setting.vue";
import { useSettingStore } from "@/stores/index"; import { useSettingStore } from "@/stores/index";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import MessageContent from "@/components/Plugins/MessageContent"; import MessageContent from "@/components/Plugins/MessageContent";
import Map from "./component/map.vue"; import Map from "./component/map.vue";
import Home from "./index/index.vue" import Home from "./index/index.vue"
const settingStore = useSettingStore(); const settingStore = useSettingStore();
const { isScale } = storeToRefs(settingStore); const { isScale } = storeToRefs(settingStore);
const wrapperStyle = {}; const wrapperStyle = {};
</script> </script>
<template> <template>
<scale-screen <scale-screen
width="12960" width="12960"
height="2430" height="2430"
:delay="500" :delay="500"
:fullScreen="false" :fullScreen="false"
:boxStyle="{ :boxStyle="{
background: '#03050C', background: '#03050C',
overflow: isScale ? 'hidden' : 'auto', overflow: isScale ? 'hidden' : 'auto',
}" }"
:wrapperStyle="wrapperStyle" :wrapperStyle="wrapperStyle"
:autoScale="isScale" :autoScale="isScale"
> >
<div class="content_wrap"> <div class="content_wrap">
<!-- 背景层 --> <!-- 背景层 -->
<div class="background_layer"> <div class="background_layer">
<Map /> <Map />
<div class="header_layer"> <div class="header_layer">
<Headers /> <Headers />
</div> </div>
<div class="left"> <div class="left">
<Left title=""></Left> <Left title=""></Left>
</div> </div>
<div class="right"> <div class="right">
<Right title=""></Right> <Right title=""></Right>
</div> </div>
<Center class="center" /> <Center class="center" />
</div> </div>
<!-- 前景层 --> <!-- 前景层 -->
<!-- <div class="foreground_layer"> <!-- <div class="foreground_layer">
<Headers /> <Headers />
<RouterView /> <RouterView />
</div> --> </div> -->
<!-- <MessageContent /> --> <!-- <MessageContent /> -->
</div> </div>
</scale-screen> </scale-screen>
<Setting /> <Setting />
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.content_wrap { .content_wrap {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
background-image: url("@/assets/img/pageBg.png"); background-image: url("@/assets/img/pageBg.png");
background-size: cover; background-size: cover;
background-position: center center; background-position: center center;
overflow: hidden; // overflow: hidden; //
} }
.background_layer { .background_layer {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
// z-index: 0; // // z-index: 0; //
position: relative; position: relative;
.header_layer{ .header_layer{
position: absolute; position: absolute;
top: 0; top: 0;
} }
.left{ .left{
position: absolute; position: absolute;
top: 100px; top: 100px;
left: 0; left: 0;
} }
.right{ .right{
position: absolute; position: absolute;
top: 100px; top: 100px;
right: 0; right: 0;
} }
.center{ .center{
position: absolute; position: absolute;
top: 100px; top: 100px;
} }
} }
.foreground_layer { .foreground_layer {
position: relative; position: relative;
// z-index: 1; // // z-index: 1; //
} }
</style> </style>

View File

@ -1,75 +1,75 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from "vue"; import { ref, reactive, onMounted } from "vue";
import useCharts from "@/hooks/useEcharts"; import useCharts from "@/hooks/useEcharts";
import { todayTrafficCount } from "@/api/modules/index"; import { todayTrafficCount } from "@/api/modules/index";
import type { EChartsOption } from "echarts"; import type { EChartsOption } from "echarts";
// DOM ref ECharts // DOM ref ECharts
const chartRef = ref<HTMLElement | null>(null); const chartRef = ref<HTMLElement | null>(null);
let chart: ReturnType<typeof useCharts> | null = null; let chart: ReturnType<typeof useCharts> | null = null;
// //
const trafficData = reactive({ const trafficData = reactive({
total: 0, total: 0,
car: { value: 0, percentage: 0, name: "小型车" }, car: { value: 0, percentage: 0, name: "小型车" },
truck: { value: 0, percentage: 0, name: "货车" }, truck: { value: 0, percentage: 0, name: "货车" },
bus: { value: 0, percentage: 0, name: "客车" }, bus: { value: 0, percentage: 0, name: "客车" },
}); });
// //
const chartOptions: EChartsOption = { const chartOptions: EChartsOption = {
tooltip: { trigger: "item", formatter: "{b}: {c} ({d}%)" }, tooltip: { trigger: "item", formatter: "{b}: {c} ({d}%)" },
legend: { show: false }, legend: { show: false },
series: [ series: [
{ {
name: "车流量分布", name: "车流量分布",
type: "pie", type: "pie",
radius: ["65%", "80%"], radius: ["65%", "80%"],
center: ["50%", "50%"], center: ["50%", "50%"],
data: [], // data: [], //
}, },
], ],
}; };
// + // +
onMounted(async () => { onMounted(async () => {
if (chartRef.value) { if (chartRef.value) {
chart = useCharts(chartRef.value); chart = useCharts(chartRef.value);
chart.setOption(chartOptions); // chart.setOption(chartOptions); //
} }
await loadTrafficData(); await loadTrafficData();
}); });
// //
async function loadTrafficData() { async function loadTrafficData() {
try { try {
const res = await todayTrafficCount(); // cartruckbus const res = await todayTrafficCount(); // cartruckbus
trafficData.total = res.total; trafficData.total = res.total;
trafficData.car.value = res.car; trafficData.car.value = res.car;
trafficData.truck.value = res.truck; trafficData.truck.value = res.truck;
trafficData.bus.value = res.bus; trafficData.bus.value = res.bus;
const newChartData = [ const newChartData = [
{ value: res.car, name: "小型车" }, { value: res.car, name: "小型车" },
{ value: res.truck, name: "货车" }, { value: res.truck, name: "货车" },
{ value: res.bus, name: "客车" }, { value: res.bus, name: "客车" },
]; ];
if (chart) { if (chart) {
chart.setOption({ chart.setOption({
series: [ series: [
{ {
data: newChartData, data: newChartData,
}, },
], ],
}); });
} }
} catch (error) { } catch (error) {
console.error("获取车流量数据失败:", error); console.error("获取车流量数据失败:", error);
} }
} }
</script> </script>
<template> <template>
<div ref="chartRef" style="width: 100%; height: 400px"></div> <div ref="chartRef" style="width: 100%; height: 400px"></div>
</template> </template>

View File

@ -1,449 +1,449 @@
<template> <template>
<div class="center-container"> <div class="center-container">
<div class="statistics-panel"> <div class="statistics-panel">
<div v-for="(stat, index) in statistics" :key="index" class="stat-card" style="position: relative;"> <div v-for="(stat, index) in statistics" :key="index" class="stat-card" style="position: relative;">
<div <div
class="box" class="box"
@mouseenter="stat.label === '服务区' && (showDropdown = true)" @mouseenter="stat.label === '服务区' && (showDropdown = true)"
@mouseleave="stat.label === '服务区' && (showDropdown = false)" @mouseleave="stat.label === '服务区' && (showDropdown = false)"
style="position: relative;" style="position: relative;"
> >
<div class="icon-wrapper"> <div class="icon-wrapper">
<img :src="stat.image" :alt="stat.label" /> <img :src="stat.image" :alt="stat.label" />
</div> </div>
<div class="stat-content"> <div class="stat-content">
<div class="stat-label"> <div class="stat-label">
{{ stat.label }} {{ stat.label }}
</div> </div>
<div class="stat-value"> <div class="stat-value">
<span class="number">{{ stat.value }}</span> <span class="number">{{ stat.value }}</span>
</div> </div>
</div> </div>
<!-- 只在服务区显示下拉选择 --> <!-- 只在服务区显示下拉选择 -->
<template v-if="stat.label === '服务区'"> <template v-if="stat.label === '服务区'">
<div <div
v-show="showDropdown" v-show="showDropdown"
class="dropdown large-dropdown" class="dropdown large-dropdown"
@mouseenter="showDropdown = true" @mouseenter="showDropdown = true"
@mouseleave="showDropdown = false" @mouseleave="showDropdown = false"
> >
<div <div
class="dropdown-item" class="dropdown-item"
v-for="area in serviceAreas" v-for="area in serviceAreas"
:key="area" :key="area"
@click="selectServiceArea(area)" @click="selectServiceArea(area)"
> >
{{ area }} {{ area }}
</div> </div>
</div> </div>
</template> </template>
</div> </div>
</div> </div>
</div> </div>
<!-- 下面弹框标题用当前选中的服务区 --> <!-- 下面弹框标题用当前选中的服务区 -->
<!-- <div v-if="showServiceArea" class="map-container"> <!-- <div v-if="showServiceArea" class="map-container">
<div class="map-title"> <div class="map-title">
<div class="title-content"> <div class="title-content">
<span class="title-text">{{ selectedServiceArea }}</span> <span class="title-text">{{ selectedServiceArea }}</span>
</div> </div>
<div class="close-btn" @click="closeDialog">×</div> <div class="close-btn" @click="closeDialog">×</div>
</div> </div>
<div class="map-con"> <div class="map-con">
<RealTimeImageCenter /> <RealTimeImageCenter />
</div> </div>
</div> --> </div> -->
<div v-if="showServiceArea" style="width: 5664px;" class="map-container RealTimeImage"> <div v-if="showServiceArea" style="width: 5664px;" class="map-container RealTimeImage">
<div class="map-title"> <div class="map-title">
<div class="title-content"> <div class="title-content">
<span class="title-text">{{selectedServiceArea}}</span> <span class="title-text">{{selectedServiceArea}}</span>
</div> </div>
<div class="close-btn" @click="closeDialog">×</div> <div class="close-btn" @click="closeDialog">×</div>
</div> </div>
<div class="map-con"> <div class="map-con">
<RealTimeImageCenter /> <RealTimeImageCenter />
</div> </div>
</div> </div>
<div class="map-container bg-header bg-header-one" style="width: 1800px;" v-if="showDeviceInfo"> <div class="map-container bg-header bg-header-one" style="width: 1800px;" v-if="showDeviceInfo">
<div class="map-title"> <div class="map-title">
<div class="title-content"> <div class="title-content">
<span class="title-text">设备信息NO{{ deviceInfo.deviceId }}</span> <span class="title-text">设备信息NO{{ deviceInfo.deviceId }}</span>
</div> </div>
<div class="close-btn" @click="closeDeviceInfo">×</div> <div class="close-btn" @click="closeDeviceInfo">×</div>
</div> </div>
<div class="map-con"> <div class="map-con">
<DeviceInfoComponent :deviceInfo="deviceInfo" /> <DeviceInfoComponent :deviceInfo="deviceInfo" />
</div> </div>
</div> </div>
<div class="map-container bg-header bg-header-two" style="width: 1800px;height: 1000px;" v-if="showDeviceInfoTwo"> <div class="map-container bg-header bg-header-two" style="width: 1800px;height: 1000px;" v-if="showDeviceInfoTwo">
<div class="map-title"> <div class="map-title">
<div class="title-content"> <div class="title-content">
<span class="title-text">设备信息NO{{ deviceInfo.deviceId }}</span> <span class="title-text">设备信息NO{{ deviceInfo.deviceId }}</span>
</div> </div>
<div class="close-btn" @click="closeDeviceInfo">×</div> <div class="close-btn" @click="closeDeviceInfo">×</div>
</div> </div>
<div class="map-con"> <div class="map-con">
<DeviceInfoComponentTwo :deviceInfo="deviceInfoTwo" /> <DeviceInfoComponentTwo :deviceInfo="deviceInfoTwo" />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, nextTick } from "vue"; import { ref, nextTick } from "vue";
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import fee from "@/assets/img/fee.png"; import fee from "@/assets/img/fee.png";
import km from "@/assets/img/km.png"; import km from "@/assets/img/km.png";
import toll from "@/assets/img/toll.png"; import toll from "@/assets/img/toll.png";
import RealTimeImageCenter from "./RealTimeImageCenter.vue"; import RealTimeImageCenter from "./RealTimeImageCenter.vue";
import DeviceInfoComponent from "./DeviceInfoComponent.vue"; import DeviceInfoComponent from "./DeviceInfoComponent.vue";
import bridge from "@/assets/img/bridge.png"; import bridge from "@/assets/img/bridge.png";
import hub from "@/assets/img/hub.png"; import hub from "@/assets/img/hub.png";
import { getDeviceList } from "@/api/modules/index"; import { getDeviceList } from "@/api/modules/index";
import DeviceInfoComponentTwo from "./DeviceInfoComponentTwo.vue"; import DeviceInfoComponentTwo from "./DeviceInfoComponentTwo.vue";
import Map from "./map.vue" import Map from "./map.vue"
import { emitter } from '@/utils/eventBus'; import { emitter } from '@/utils/eventBus';
const showServiceArea = ref(false); const showServiceArea = ref(false);
const showMap = ref(true); const showMap = ref(true);
const showDeviceInfo = ref(false); const showDeviceInfo = ref(false);
const showDeviceInfoTwo = ref(false); const showDeviceInfoTwo = ref(false);
const closeDialog = () => { const closeDialog = () => {
showServiceArea.value = false; showServiceArea.value = false;
}; };
const showDropdown = ref(false); const showDropdown = ref(false);
const serviceAreas = ["南皮服务区", "东光服务区"]; const serviceAreas = ["南皮服务区", "东光服务区"];
const selectedServiceArea = ref("南皮服务区"); const selectedServiceArea = ref("南皮服务区");
function selectServiceArea(area) { function selectServiceArea(area) {
selectedServiceArea.value = area; selectedServiceArea.value = area;
showServiceArea.value = true; showServiceArea.value = true;
showDropdown.value = false; showDropdown.value = false;
} }
// //
const statistics = ref([ const statistics = ref([
{ {
image: km, image: km,
value: "78.338", value: "78.338",
label: "全里程(公里)", label: "全里程(公里)",
}, },
{ {
image: toll, image: toll,
value: "5", value: "5",
label: "收费站", label: "收费站",
}, },
{ {
image: fee, image: fee,
value: "2", value: "2",
label: "服务区", label: "服务区",
}, },
{ {
image: bridge, image: bridge,
value: "7", value: "7",
label: "桥梁(座)", label: "桥梁(座)",
}, },
{ {
image: hub, image: hub,
value: "7", value: "7",
label: "隧道互通(座)", label: "隧道互通(座)",
}, },
]); ]);
// //
const deviceInfo = ref({ const deviceInfo = ref({
deviceType: '视频监控', deviceType: '视频监控',
cameraType: '枪机', cameraType: '枪机',
deviceCode: '3_CAM1_0', deviceCode: '3_CAM1_0',
roadSection: '路段名称', roadSection: '路段名称',
direction: '邯郸方向', direction: '邯郸方向',
stakeNumber: 'K18+324', stakeNumber: 'K18+324',
longitude: '44.230145', longitude: '44.230145',
latitude: '45.65412', latitude: '45.65412',
ipAddress: '13.106.2.10' ipAddress: '13.106.2.10'
}); });
const deviceInfoTwo = ref({ const deviceInfoTwo = ref({
deviceType: '视频监控', deviceType: '视频监控',
cameraType: '枪机', cameraType: '枪机',
deviceCode: '3_CAM1_0', deviceCode: '3_CAM1_0',
roadSection: '路段名称', roadSection: '路段名称',
direction: '邯郸方向', direction: '邯郸方向',
stakeNumber: 'K18+324', stakeNumber: 'K18+324',
longitude: '44.230145', longitude: '44.230145',
latitude: '45.65412', latitude: '45.65412',
ipAddress: '13.106.2.10' ipAddress: '13.106.2.10'
}); });
// const deviceInfo = ref({}); // const deviceInfo = ref({});
function handleShowDeviceInfo(data) { function handleShowDeviceInfo(data) {
console.log("接收到设备信息:", data); console.log("接收到设备信息:", data);
// //
showDeviceInfo.value = false; showDeviceInfo.value = false;
showDeviceInfoTwo.value = false; showDeviceInfoTwo.value = false;
if (data.deviceTypeId === "22" || data.deviceTypeId === "44") { if (data.deviceTypeId === "22" || data.deviceTypeId === "44") {
showDeviceInfo.value = true; showDeviceInfo.value = true;
} else if (data.deviceTypeId === "15" || data.deviceTypeId === "53") { } else if (data.deviceTypeId === "15" || data.deviceTypeId === "53") {
showDeviceInfoTwo.value = true; showDeviceInfoTwo.value = true;
} }
deviceInfoTwo.value = data; deviceInfoTwo.value = data;
deviceInfo.value = data; deviceInfo.value = data;
} }
const closeDeviceInfo = () => { const closeDeviceInfo = () => {
showDeviceInfo.value = false; showDeviceInfo.value = false;
showDeviceInfoTwo.value = false; showDeviceInfoTwo.value = false;
showMap.value = true; showMap.value = true;
}; };
const mapFlag = ref(true); const mapFlag = ref(true);
onMounted(() => { onMounted(() => {
emitter.on("show-device-info", handleShowDeviceInfo); emitter.on("show-device-info", handleShowDeviceInfo);
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.center-container { .center-container {
width: 100%; width: 100%;
// height: 100%; // height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
position: relative; position: relative;
.RealTimeImage{ .RealTimeImage{
background-image: url("@/assets/img/eqm/fuwuqu.png"); background-image: url("@/assets/img/eqm/fuwuqu.png");
background-size: 100% 100%; background-size: 100% 100%;
} }
.map-container.bg-header { .map-container.bg-header {
position: absolute; position: absolute;
top: 500px; top: 500px;
left: 42.5%; left: 42.5%;
// transform: translate(-50%, -50%); // transform: translate(-50%, -50%);
z-index: 2000; z-index: 2000;
// box-shadow: 0 8px 40px rgba(0,0,0,0.3); // box-shadow: 0 8px 40px rgba(0,0,0,0.3);
} }
.bg-header-one { .bg-header-one {
background-image: url("@/assets/img/eqm/eqminfo.png"); background-image: url("@/assets/img/eqm/eqminfo.png");
background-size: 100% 100%; background-size: 100% 100%;
} }
.bg-header-two { .bg-header-two {
background-image: url("@/assets/img/eqm/eqminfo2.png"); background-image: url("@/assets/img/eqm/eqminfo2.png");
background-size: 100% 100%; background-size: 100% 100%;
} }
} }
.statistics-panel { .statistics-panel {
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
align-items: center; align-items: center;
width: 40%; width: 40%;
padding: 20px; padding: 20px;
gap: 30px; gap: 30px;
padding-top: 200px; padding-top: 200px;
padding-bottom: 50px; padding-bottom: 50px;
.box{ .box{
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
align-items: center; align-items: center;
} }
} }
.stat-card { .stat-card {
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 15px 20px; padding: 15px 20px;
} }
.card-background { .card-background {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background: linear-gradient(135deg, background: linear-gradient(135deg,
rgba(0, 170, 255, 0.1) 0%, rgba(0, 170, 255, 0.1) 0%,
transparent 100%); transparent 100%);
border-radius: 8px; border-radius: 8px;
opacity: 0.5; opacity: 0.5;
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
} }
.icon-wrapper { .icon-wrapper {
position: relative; position: relative;
margin-right: 15px; margin-right: 15px;
img { img {
width: 160px; width: 160px;
height: 160px; height: 160px;
} }
} }
.stat-content { .stat-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
} }
.stat-value { .stat-value {
.number { .number {
font-size: 64px !important; font-size: 64px !important;
font-weight: bold; font-weight: bold;
color: #1ae9ee; color: #1ae9ee;
} }
} }
.stat-label { .stat-label {
font-size: 32px !important; font-size: 32px !important;
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
margin-top: 8px; margin-top: 8px;
letter-spacing: 1px; letter-spacing: 1px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
} }
.map-container { .map-container {
width: 78%; width: 78%;
margin-top: 30px; margin-top: 30px;
// background: linear-gradient(360deg, // background: linear-gradient(360deg,
// rgba(15, 43, 89, 0.3) 0%, // rgba(15, 43, 89, 0.3) 0%,
// rgba(15, 43, 89, 0) 100%), // rgba(15, 43, 89, 0) 100%),
// rgba(15, 43, 89, 0.85); // rgba(15, 43, 89, 0.85);
// box-shadow: inset 0px 0px 57px 0px rgba(0, 149, 255, 0.41), // box-shadow: inset 0px 0px 57px 0px rgba(0, 149, 255, 0.41),
// inset 0px 0px 21px 0px rgba(49, 176, 255, 0.47); // inset 0px 0px 21px 0px rgba(49, 176, 255, 0.47);
// border-radius: 8px; // border-radius: 8px;
// border: 1px solid; // border: 1px solid;
// border-image: linear-gradient(180deg, // border-image: linear-gradient(180deg,
// rgba(186, 213, 255, 0.3), // rgba(186, 213, 255, 0.3),
// rgba(186, 213, 255, 0)) 1 1; // rgba(186, 213, 255, 0)) 1 1;
// padding: 20px; // padding: 20px;
// box-sizing: border-box; // box-sizing: border-box;
height: 1700px; height: 1700px;
} }
.map-title { .map-title {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 15px; margin-bottom: 15px;
padding: 15px 40px; padding: 15px 40px;
position: relative; position: relative;
// border-bottom: 1px solid rgba(26, 233, 238, 0.2); // border-bottom: 1px solid rgba(26, 233, 238, 0.2);
height: 150px; height: 150px;
background: repeating-linear-gradient(-65deg, background: repeating-linear-gradient(-65deg,
rgba(15, 43, 89, 0.3) 0px, rgba(15, 43, 89, 0.3) 0px,
rgba(15, 43, 89, 0.3) 50px, rgba(15, 43, 89, 0.3) 50px,
rgba(26, 233, 238, 0.05) 50px, rgba(26, 233, 238, 0.05) 50px,
rgba(26, 233, 238, 0.05) 100px); rgba(26, 233, 238, 0.05) 100px);
.title-content { .title-content {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.title-text { .title-text {
font-size: 52px; font-size: 52px;
color: #fff; color: #fff;
font-weight: 700; font-weight: 700;
letter-spacing: 1px; letter-spacing: 1px;
} }
.close-btn { .close-btn {
font-size: 24px; font-size: 24px;
color: #fff; color: #fff;
cursor: pointer; cursor: pointer;
width: 40px; width: 40px;
height: 40px; height: 40px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 50%; border-radius: 50%;
transition: all 0.3s ease; transition: all 0.3s ease;
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.2);
position: relative; position: relative;
&::before, &::before,
&::after { &::after {
content: ""; content: "";
position: absolute; position: absolute;
width: 20px; width: 20px;
height: 2px; height: 2px;
background-color: #fff; background-color: #fff;
top: 50%; top: 50%;
left: 50%; left: 50%;
} }
&::before { &::before {
transform: translate(-50%, -50%) rotate(45deg); transform: translate(-50%, -50%) rotate(45deg);
} }
&::after { &::after {
transform: translate(-50%, -50%) rotate(-45deg); transform: translate(-50%, -50%) rotate(-45deg);
} }
&:hover { &:hover {
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.3); border-color: rgba(255, 255, 255, 0.3);
transform: scale(1.05); transform: scale(1.05);
} }
} }
} }
.map-con { .map-con {
width: 100%; width: 100%;
height: 90%; height: 90%;
/* background-color: #1ae9ee; */ /* background-color: #1ae9ee; */
// //
} }
.eqm-info { .eqm-info {
width: 1788px; width: 1788px;
margin-top: 30px; margin-top: 30px;
background: linear-gradient(360deg, background: linear-gradient(360deg,
rgba(15, 43, 89, 0.3) 0%, rgba(15, 43, 89, 0.3) 0%,
rgba(15, 43, 89, 0) 100%), rgba(15, 43, 89, 0) 100%),
rgba(15, 43, 89, 0.85); rgba(15, 43, 89, 0.85);
box-shadow: inset 0px 0px 57px 0px rgba(0, 149, 255, 0.41), box-shadow: inset 0px 0px 57px 0px rgba(0, 149, 255, 0.41),
inset 0px 0px 21px 0px rgba(49, 176, 255, 0.47); inset 0px 0px 21px 0px rgba(49, 176, 255, 0.47);
border-radius: 8px; border-radius: 8px;
border: 1px solid; border: 1px solid;
border-image: linear-gradient(180deg, border-image: linear-gradient(180deg,
rgba(186, 213, 255, 0.3), rgba(186, 213, 255, 0.3),
rgba(186, 213, 255, 0)) 1 1; rgba(186, 213, 255, 0)) 1 1;
padding: 20px; padding: 20px;
box-sizing: border-box; box-sizing: border-box;
height: 1700px; height: 1700px;
.eqm-title { .eqm-title {
width: 100%; width: 100%;
height: 56px; height: 56px;
background-image: url("../../assets/img/eqm/eqmtitle.png"); background-image: url("../../assets/img/eqm/eqmtitle.png");
} }
} }
.dropdown { .dropdown {
position: absolute; position: absolute;
right: 0; right: 0;
bottom: -190px; bottom: -190px;
background: #112a44; background: #112a44;
border: 1px solid #1ae9ee; border: 1px solid #1ae9ee;
border-radius: 6px; border-radius: 6px;
z-index: 100; z-index: 100;
min-width: 220px; min-width: 220px;
min-height: 120px; min-height: 120px;
box-shadow: 0 4px 16px rgba(0,0,0,0.25); box-shadow: 0 4px 16px rgba(0,0,0,0.25);
padding: 16px 0; padding: 16px 0;
.dropdown-item { .dropdown-item {
padding: 20px 32px; padding: 20px 32px;
font-size: 28px; font-size: 28px;
color: #fff; color: #fff;
cursor: pointer; cursor: pointer;
text-align: left; text-align: left;
&:hover { &:hover {
background: #1ae9ee; background: #1ae9ee;
color: #112a44; color: #112a44;
} }
} }
} }
</style> </style>

View File

@ -1,415 +1,415 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from "vue"; import { ref, onMounted, computed } from "vue";
import EventStatus from "./EventStatus.vue"; import EventStatus from "./EventStatus.vue";
import EventTask from "./EventTask.vue"; import EventTask from "./EventTask.vue";
import RealTimeImage from "./RealTimeImage.vue"; import RealTimeImage from "./RealTimeImage.vue";
import { weatherForecast, weatherHourly, todayStatusCount, todayHourly, getDictData } from "@/api/modules/index"; import { weatherForecast, weatherHourly, todayStatusCount, todayHourly, getDictData } from "@/api/modules/index";
import { useTodayTime, } from "@/utils/packge"; import { useTodayTime, } from "@/utils/packge";
const { todayTime, getTodayTime } = useTodayTime(); const { todayTime, getTodayTime } = useTodayTime();
// API // API
const pendingCount = ref<string>(''); const pendingCount = ref<string>('');
const processingCount = ref<string>(''); const processingCount = ref<string>('');
const serviceArea = ref<any[]>([]); const serviceArea = ref<any[]>([]);
const Hub = ref<any[]>([]); const Hub = ref<any[]>([]);
const Intercommunication = ref<any[]>([]); const Intercommunication = ref<any[]>([]);
// //
const selectedServiceArea = ref("全部"); const selectedServiceArea = ref("全部");
const serviceAreas = ref([]); const serviceAreas = ref([]);
// //
const handleServiceAreaChange = (area: string) => { const handleServiceAreaChange = (area: string) => {
selectedServiceArea.value = area; selectedServiceArea.value = area;
}; };
// //
const areaTypes = ref([ const areaTypes = ref([
{ id: "service", name: "服务区", selected: true }, { id: "service", name: "服务区", selected: true },
{ id: "interchange", name: "互通", selected: false }, { id: "interchange", name: "互通", selected: false },
{ id: "hub", name: "枢纽", selected: false }, { id: "hub", name: "枢纽", selected: false },
]); ]);
// //
const toggleAreaType = (id: string) => { const toggleAreaType = (id: string) => {
areaTypes.value.forEach((type) => { areaTypes.value.forEach((type) => {
type.selected = type.id === id; type.selected = type.id === id;
}); });
}; };
onMounted(()=>{ onMounted(()=>{
todayStatusCount({todayTime: todayTime.value}).then((res: any) => { todayStatusCount({todayTime: todayTime.value}).then((res: any) => {
if(res.code === 200){ if(res.code === 200){
res.data.forEach((item: any) => { res.data.forEach((item: any) => {
if(item.status == '0'){ // if(item.status == '0'){ //
pendingCount.value = item.count || 0 pendingCount.value = item.count || 0
} else if(item.status == '2'){ // } else if(item.status == '2'){ //
processingCount.value = item.count || 0 processingCount.value = item.count || 0
} }
}); });
} }
}) })
getDictData("hb_service_area").then((res: any) => { // getDictData("hb_service_area").then((res: any) => { //
if (res.code === 200) { if (res.code === 200) {
serviceArea.value = res.data || []; serviceArea.value = res.data || [];
} }
}); });
getDictData("hb_hub").then((res: any) => { // getDictData("hb_hub").then((res: any) => { //
if (res.code === 200) { if (res.code === 200) {
Hub.value = res.data || []; Hub.value = res.data || [];
} }
}); });
getDictData("hb_interchange").then((res: any) => { // getDictData("hb_interchange").then((res: any) => { //
if (res.code === 200) { if (res.code === 200) {
Intercommunication.value = res.data || []; Intercommunication.value = res.data || [];
} }
}); });
}) })
const currentAreaList = computed(() => { const currentAreaList = computed(() => {
const selectedType = areaTypes.value.find((type) => type.selected); const selectedType = areaTypes.value.find((type) => type.selected);
if (selectedType?.id === "service") { if (selectedType?.id === "service") {
return serviceArea.value; return serviceArea.value;
} else if (selectedType?.id === "interchange") { } else if (selectedType?.id === "interchange") {
return Intercommunication.value; return Intercommunication.value;
} else if (selectedType?.id === "hub") { } else if (selectedType?.id === "hub") {
return Hub.value; return Hub.value;
} }
return []; return [];
}); });
const isDropdownOpen = ref(false); const isDropdownOpen = ref(false);
</script> </script>
<template> <template>
<div class="traffic-flow-grid"> <div class="traffic-flow-grid">
<div class="top"> <div class="top">
<div class="traffic-flow-item"> <div class="traffic-flow-item">
<div style="margin-left: 396px" class="item-title">今日事件处理</div> <div style="margin-left: 396px" class="item-title">今日事件处理</div>
<div style="margin-left: 396px" class="item-content"> <div style="margin-left: 396px" class="item-content">
<EventStatus <EventStatus
:pending-count="pendingCount" :pending-count="pendingCount"
:processing-count="processingCount" :processing-count="processingCount"
/> />
</div> </div>
</div> </div>
<div class="traffic-flow-item"> <div class="traffic-flow-item">
<div class="item-title">事件处置任务</div> <div class="item-title">事件处置任务</div>
<div class="item-content"> <div class="item-content">
<EventTask /> <EventTask />
</div> </div>
</div> </div>
</div> </div>
<div class="bottom"> <div class="bottom">
<div class="item-title"> <div class="item-title">
<div class="title-text">实时图像</div> <div class="title-text">实时图像</div>
<div class="area-type-selector"> <div class="area-type-selector">
<div <div
v-for="type in areaTypes" v-for="type in areaTypes"
:key="type.id" :key="type.id"
class="area-type-item" class="area-type-item"
:class="{ active: type.selected }" :class="{ active: type.selected }"
@click="toggleAreaType(type.id)" @click="toggleAreaType(type.id)"
> >
{{ type.name }} {{ type.name }}
</div> </div>
</div> </div>
<div class="service-area-selector"> <div class="service-area-selector">
<div class="selected-area" @click="isDropdownOpen = !isDropdownOpen"> <div class="selected-area" @click="isDropdownOpen = !isDropdownOpen">
{{ selectedServiceArea }} <span class="arrow-down"></span> {{ selectedServiceArea }} <span class="arrow-down"></span>
</div> </div>
<div class="dropdown-menu" v-show="isDropdownOpen"> <div class="dropdown-menu" v-show="isDropdownOpen">
<div <div
v-for="(item, index) in currentAreaList" v-for="(item, index) in currentAreaList"
:key="index" :key="index"
class="dropdown-item" class="dropdown-item"
:class="{ active: selectedServiceArea === item.dictLabel }" :class="{ active: selectedServiceArea === item.dictLabel }"
@click=" @click="
handleServiceAreaChange(item.dictLabel); handleServiceAreaChange(item.dictLabel);
isDropdownOpen = false; isDropdownOpen = false;
" "
> >
{{ item.dictLabel }} {{ item.dictLabel }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="item-content"> <div class="item-content">
<RealTimeImage :service-area="selectedServiceArea" /> <RealTimeImage :service-area="selectedServiceArea" />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.traffic-flow-grid { .traffic-flow-grid {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-right: 20px; margin-right: 20px;
// justify-content: space-between; // justify-content: space-between;
// align-items: flex-start; // align-items: flex-start;
.top { .top {
width: 100%; width: 100%;
height: 652px; height: 652px;
display: flex; display: flex;
.traffic-flow-item { .traffic-flow-item {
flex: 1; flex: 1;
.item-title { .item-title {
width: 1080px; width: 1080px;
height: 104px; height: 104px;
background-image: url("../../assets/img/leftoright/title.png"); background-image: url("../../assets/img/leftoright/title.png");
margin-left: 48px; margin-left: 48px;
margin-top: 48px; margin-top: 48px;
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
font-weight: 700; font-weight: 700;
font-size: 52px; font-size: 52px;
color: #ffffff; color: #ffffff;
line-height: 104px; line-height: 104px;
padding-left: 56px; padding-left: 56px;
letter-spacing: 2px; letter-spacing: 2px;
text-shadow: 0px 4px 0px #095a8c; text-shadow: 0px 4px 0px #095a8c;
text-align: left; text-align: left;
font-style: normal; font-style: normal;
text-transform: none; text-transform: none;
} }
.item-content { .item-content {
width: 1080px; width: 1080px;
height: 468px; height: 468px;
margin-left: 48px; margin-left: 48px;
margin-top: 32px; margin-top: 32px;
background-image: url("../../assets/img/leftoright/rightcontent.png"); background-image: url("../../assets/img/leftoright/rightcontent.png");
} }
} }
} }
.bottom { .bottom {
flex: 1; flex: 1;
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-top: 48px; margin-top: 48px;
.item-title { .item-title {
width: 2208px; width: 2208px;
height: 104px; height: 104px;
margin-left: 396px; margin-left: 396px;
background-image: url("../../assets/img/leftoright/bigtitle.png"); background-image: url("../../assets/img/leftoright/bigtitle.png");
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
font-weight: 700; font-weight: 700;
font-size: 52px; font-size: 52px;
color: #ffffff; color: #ffffff;
line-height: 104px; line-height: 104px;
padding-left: 56px; padding-left: 56px;
letter-spacing: 2px; letter-spacing: 2px;
text-shadow: 0px 4px 0px #095a8c; text-shadow: 0px 4px 0px #095a8c;
text-align: left; text-align: left;
font-style: normal; font-style: normal;
text-transform: none; text-transform: none;
position: relative; position: relative;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
padding-right: 56px; padding-right: 56px;
.title-text { .title-text {
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
font-weight: 700; font-weight: 700;
font-size: 52px; font-size: 52px;
color: #ffffff; color: #ffffff;
letter-spacing: 2px; letter-spacing: 2px;
text-shadow: 0px 4px 0px #095a8c; text-shadow: 0px 4px 0px #095a8c;
text-align: left; text-align: left;
font-style: normal; font-style: normal;
text-transform: none; text-transform: none;
} }
.area-type-selector { .area-type-selector {
position: absolute; position: absolute;
right: 320px; right: 320px;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
display: flex; display: flex;
gap: 20px; gap: 20px;
font-size: 32px; font-size: 32px;
font-weight: normal; font-weight: normal;
color: #ffffff; color: #ffffff;
text-shadow: none; text-shadow: none;
z-index: 99; z-index: 99;
.area-type-item { .area-type-item {
width: 150px; width: 150px;
height: 64px; height: 64px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
color: #bad5ff; color: #bad5ff;
position: relative; position: relative;
border: 1px solid #bad5ff; border: 1px solid #bad5ff;
border-radius: 4px; border-radius: 4px;
background-color: #304b75; background-color: #304b75;
box-shadow: inset 0 0 10px rgba(0, 79, 127, 0.3), box-shadow: inset 0 0 10px rgba(0, 79, 127, 0.3),
0 0 5px rgba(0, 198, 255, 0.2); 0 0 5px rgba(0, 198, 255, 0.2);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
overflow: hidden; overflow: hidden;
&::before { &::before {
content: ""; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background: linear-gradient( background: linear-gradient(
135deg, 135deg,
rgba(0, 198, 255, 0.1) 0%, rgba(0, 198, 255, 0.1) 0%,
rgba(0, 118, 170, 0.05) 40%, rgba(0, 118, 170, 0.05) 40%,
transparent 100% transparent 100%
); );
z-index: -1; z-index: -1;
border-radius: 3px; border-radius: 3px;
} }
&.active { &.active {
color: #acc0df; color: #acc0df;
border: 1px solid rgba(0, 198, 255, 0.7); border: 1px solid rgba(0, 198, 255, 0.7);
box-shadow: inset 0 0 15px rgba(0, 79, 127, 0.6), box-shadow: inset 0 0 15px rgba(0, 79, 127, 0.6),
0 0 10px rgba(0, 198, 255, 0.5); 0 0 10px rgba(0, 198, 255, 0.5);
background-color: rgba(0, 67, 123, 0.7); background-color: rgba(0, 67, 123, 0.7);
text-shadow: 0 0 8px rgba(0, 198, 255, 0.5); text-shadow: 0 0 8px rgba(0, 198, 255, 0.5);
&::before { &::before {
background: linear-gradient( background: linear-gradient(
135deg, 135deg,
rgba(0, 198, 255, 0.3) 0%, rgba(0, 198, 255, 0.3) 0%,
rgba(0, 118, 170, 0.15) 50%, rgba(0, 118, 170, 0.15) 50%,
rgba(0, 79, 127, 0.05) 100% rgba(0, 79, 127, 0.05) 100%
); );
} }
&::after { &::after {
content: ""; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 2px; height: 2px;
background: linear-gradient( background: linear-gradient(
90deg, 90deg,
transparent 0%, transparent 0%,
rgba(0, 198, 255, 0.8) 50%, rgba(0, 198, 255, 0.8) 50%,
transparent 100% transparent 100%
); );
} }
} }
&:hover:not(.active) { &:hover:not(.active) {
border-color: rgba(0, 198, 255, 0.5); border-color: rgba(0, 198, 255, 0.5);
box-shadow: inset 0 0 12px rgba(0, 79, 127, 0.4), box-shadow: inset 0 0 12px rgba(0, 79, 127, 0.4),
0 0 8px rgba(0, 198, 255, 0.3); 0 0 8px rgba(0, 198, 255, 0.3);
background-color: rgba(32, 64, 113, 0.6); background-color: rgba(32, 64, 113, 0.6);
color: #d8e8ff; color: #d8e8ff;
} }
} }
} }
.service-area-selector { .service-area-selector {
position: absolute; position: absolute;
right: 56px; right: 56px;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
font-size: 32px; font-size: 32px;
font-weight: normal; font-weight: normal;
color: #ffffff; color: #ffffff;
display: inline-block; display: inline-block;
text-shadow: none; text-shadow: none;
z-index: 99; z-index: 99;
.selected-area { .selected-area {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
background: #304b75; background: #304b75;
border: 1px solid #718cb6; border: 1px solid #718cb6;
border-radius: 4px; border-radius: 4px;
padding: 0 20px; padding: 0 20px;
height: 64px; height: 64px;
cursor: pointer; cursor: pointer;
width: 240px; width: 240px;
color: #ffffff; color: #ffffff;
position: relative; position: relative;
.arrow-down { .arrow-down {
font-size: 18px; font-size: 18px;
color: #ffffff; color: #ffffff;
margin-left: 4px; margin-left: 4px;
} }
} }
.dropdown-menu { .dropdown-menu {
position: absolute; position: absolute;
top: 66px; top: 66px;
right: 0; right: 0;
background: #003366; background: #003366;
border: 1px solid rgba(0, 198, 255, 0.5); border: 1px solid rgba(0, 198, 255, 0.5);
border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px;
width: 240px; width: 240px;
z-index: 100; z-index: 100;
max-height: 200px; max-height: 200px;
overflow-y: auto; overflow-y: auto;
padding-top: 8px; padding-top: 8px;
transform-origin: top right; transform-origin: top right;
/* 自定义滚动条样式 */ /* 自定义滚动条样式 */
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 6px; width: 6px;
} }
&::-webkit-scrollbar-track { &::-webkit-scrollbar-track {
background: rgba(0, 27, 61, 0.3); background: rgba(0, 27, 61, 0.3);
border-radius: 3px; border-radius: 3px;
} }
&::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
background: rgba(0, 198, 255, 0.5); background: rgba(0, 198, 255, 0.5);
border-radius: 3px; border-radius: 3px;
} }
&::-webkit-scrollbar-thumb:hover { &::-webkit-scrollbar-thumb:hover {
background: rgba(0, 198, 255, 0.7); background: rgba(0, 198, 255, 0.7);
} }
&::before { &::before {
content: ""; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 10px; left: 10px;
right: 10px; right: 10px;
height: 1px; height: 1px;
border-top: 1px dashed rgba(0, 198, 255, 0.5); border-top: 1px dashed rgba(0, 198, 255, 0.5);
} }
.dropdown-item { .dropdown-item {
cursor: pointer; cursor: pointer;
transition: background 0.2s; transition: background 0.2s;
text-align: center; text-align: center;
&:hover, &:hover,
&.active { &.active {
background: #304b75; background: #304b75;
} }
&.active { &.active {
color: #00c6ff; color: #00c6ff;
} }
} }
} }
} }
} }
.item-content { .item-content {
width: 2208px; width: 2208px;
height: 1428px; height: 1428px;
margin-left: 396px; margin-left: 396px;
margin-top: 32px; margin-top: 32px;
background-image: url("../../assets/img/leftoright/rbcontent.png"); background-image: url("../../assets/img/leftoright/rbcontent.png");
} }
} }
} }
</style> </style>

View File

@ -1,13 +1,13 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
// const defaultTheme = require('tailwindcss/defaultTheme') // const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = { module.exports = {
content: ['./index.html', './src/**/*.{vue,js,ts}'], content: ['./index.html', './src/**/*.{vue,js,ts}'],
theme: { theme: {
extend: { extend: {
// fontFamily: { // fontFamily: {
// sans: ['"Inter var"', ...defaultTheme.fontFamily.sans], // sans: ['"Inter var"', ...defaultTheme.fontFamily.sans],
// }, // },
}, },
}, },
plugins: [], plugins: [],
} }

View File

@ -1,13 +1,13 @@
{ {
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node" "moduleResolution": "node"
}, },
"exclude": ["node_modules","public"], "exclude": ["node_modules","public"],
"include": [ "include": [
"vite.config.ts", "vite.config.ts",
"config/**/*.ts", "config/**/*.ts",
"config/**/*.d.ts", "config/**/*.d.ts",
] ]
} }

View File

@ -1,46 +1,46 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "esnext", "target": "esnext",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"strict": true, "strict": true,
"jsx": "preserve", "jsx": "preserve",
"sourceMap": true, "sourceMap": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"esModuleInterop": true, "esModuleInterop": true,
"lib": [ "lib": [
"esnext", "esnext",
"dom" "dom"
], ],
"baseUrl": "./", "baseUrl": "./",
"types": [ "types": [
"node", "node",
"vite/client", "vite/client",
"element-plus/global" "element-plus/global"
], ],
"paths": { "paths": {
"@/*": [ "@/*": [
"src/*" "src/*"
], ],
"api/*": [ "api/*": [
"src/api/*" "src/api/*"
], ],
} }
}, },
"include": [ "include": [
"src/**/*.ts", "src/**/*.ts",
"src/**/*.d.ts", "src/**/*.d.ts",
"src/**/*.tsx", "src/**/*.tsx",
"src/**/*.vue", "src/**/*.vue",
"types/env.d.ts", "types/env.d.ts",
"types/*.d.ts" "types/*.d.ts"
], ],
"exclude": ["node_modules","public"], "exclude": ["node_modules","public"],
"references": [ "references": [
{ {
"path": "./tsconfig.config.json" "path": "./tsconfig.config.json"
} }
] ]
} }

12
types/env.d.ts vendored
View File

@ -1,6 +1,6 @@
declare module "*.vue" { declare module "*.vue" {
import { defineComponent } from "vue"; import { defineComponent } from "vue";
const Component: ReturnType<typeof defineComponent>; const Component: ReturnType<typeof defineComponent>;
export default Component; export default Component;
} }

8
types/global.d.ts vendored
View File

@ -1,5 +1,5 @@
interface Window { interface Window {
$message: any $message: any
} }
type TimeProp= NodeJS.Timeout type TimeProp= NodeJS.Timeout

View File

@ -1,86 +1,86 @@
/* /*
* @Author: 996555510 65213605+996555510@users.noreply.github.com * @Author: 996555510 65213605+996555510@users.noreply.github.com
* @Date: 2025-05-22 10:12:42 * @Date: 2025-05-22 10:12:42
* @LastEditors: 996555510 65213605+996555510@users.noreply.github.com * @LastEditors: 996555510 65213605+996555510@users.noreply.github.com
* @LastEditTime: 2025-05-23 15:55:37 * @LastEditTime: 2025-05-23 15:55:37
* @FilePath: \IofTV-Screen-Vue3-master\vite.config.ts * @FilePath: \IofTV-Screen-Vue3-master\vite.config.ts
* @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
/// <reference types="vite/client" /> /// <reference types="vite/client" />
import type { UserConfig, ConfigEnv } from 'vite'; import type { UserConfig, ConfigEnv } from 'vite';
import { defineConfig, loadEnv } from 'vite' import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import { resolve } from "path"; import { resolve } from "path";
import AutoImport from 'unplugin-auto-import/vite' import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
//https://github.com/element-plus/unplugin-element-plus/blob/HEAD/README.zh-CN.md //https://github.com/element-plus/unplugin-element-plus/blob/HEAD/README.zh-CN.md
import ElementPlus from 'unplugin-element-plus/vite' import ElementPlus from 'unplugin-element-plus/vite'
import {createHtmlPlugin} from 'vite-plugin-html'; import {createHtmlPlugin} from 'vite-plugin-html';
export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
const viteEnv = loadEnv(mode, process.cwd()); const viteEnv = loadEnv(mode, process.cwd());
return { return {
base: '/', base: '/',
plugins: [ plugins: [
vue(), vue(),
AutoImport({ resolvers: [ElementPlusResolver()] }), AutoImport({ resolvers: [ElementPlusResolver()] }),
Components({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }),
ElementPlus(), ElementPlus(),
createHtmlPlugin({ createHtmlPlugin({
inject: { inject: {
data: { data: {
injectMapScript: `<script src="./public/static/map_load.js"></script>`, injectMapScript: `<script src="./public/static/map_load.js"></script>`,
}, },
}, },
minify: true, minify: true,
}), }),
// Compression({ // Compression({
// algorithm: 'gzip', // algorithm: 'gzip',
// test: /\.(js|css|html|json|svg|jpe?g|png|gif)$/i, // test: /\.(js|css|html|json|svg|jpe?g|png|gif)$/i,
// threshold: 1024, // threshold: 1024,
// deleteOriginalAssets: false // deleteOriginalAssets: false
// }), // }),
// svgr() // svgr()
], ],
publicDir: "public", publicDir: "public",
build: { build: {
outDir: 'dist', outDir: 'dist',
assetsDir: 'static' assetsDir: 'static'
}, },
server: { server: {
host: '0.0.0.0', host: '0.0.0.0',
port: 8080, port: 8080,
open: true, open: true,
strictPort: false, strictPort: false,
proxy: { proxy: {
[viteEnv.VITE_APP_CONTROL_BASE_API]: { [viteEnv.VITE_APP_CONTROL_BASE_API]: {
target: 'http://172.16.1.128:8090/xjIotApi', target: 'http://172.16.1.133:8081/xjIotApi',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(new RegExp(`^${viteEnv.VITE_APP_CONTROL_BASE_API}`), ''), rewrite: (path) => path.replace(new RegExp(`^${viteEnv.VITE_APP_CONTROL_BASE_API}`), ''),
}, },
[viteEnv.VITE_APP_BASE_API]: { [viteEnv.VITE_APP_BASE_API]: {
target: 'http://172.16.1.128:8090/iotApi', target: 'http://172.16.1.133:8081/iotApi',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(new RegExp(`^${viteEnv.VITE_APP_BASE_API}`), ''), rewrite: (path) => path.replace(new RegExp(`^${viteEnv.VITE_APP_BASE_API}`), ''),
} }
}, },
}, },
resolve: { resolve: {
alias: { alias: {
"@": resolve(__dirname, "./src"), "@": resolve(__dirname, "./src"),
"components": resolve(__dirname, "./src/components"), "components": resolve(__dirname, "./src/components"),
"api": resolve(__dirname, "./src/api") "api": resolve(__dirname, "./src/api")
}, },
}, },
css: { css: {
preprocessorOptions: { preprocessorOptions: {
scss: { scss: {
additionalData: `@use "./src/assets/css/variable.scss" as *;`, additionalData: `@use "./src/assets/css/variable.scss" as *;`,
}, },
}, },
} }
}; };
}); });