# yh-dashboard **Repository Path**: ehl_yt/yh-dashboard ## Basic Information - **Project Name**: yh-dashboard - **Description**: 烟台大屏框架 - **Primary Language**: Unknown - **License**: GPL-2.0 - **Default Branch**: projects/yantai - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 2 - **Created**: 2024-01-12 - **Last Updated**: 2026-03-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 云洄脚手架 本框架大致有以下三种使用者 1. 框架维护者 2. 组件开发者 3. 看板搭建者 三个角色互相依赖,并且缺一不可 框架维护者需要关注全局,大部分工作在 `/src/components/core`和`/src/module/core`文件夹下进行 组件开发者需要关注`/src/components/`,在其中除了`core`文件夹中开发组件 看板搭建者主要关注`/src/dashborads`,在其中使用组件开发者的组件搭建看板内容 ## 目录结构 ``` │ App.vue │ main.js ├─api // 全局axios封装 │ │ index.js │ └─modules │ HomeApi.js ├─assets // 组件内 import引入的静态资源文件,目前决定静态资源图片统一放置到 | | // `public/assets/projects/projectYT`目录下 │ ├─picImgs │ ├─projects │ │ └─projectYT ├─components // 系统内组件 │ │ index.js // 统一注册 │ ├─business // 业务组件 │ ├─core // 核心组件 │ │ container.vue │ │ containerLayout.vue │ │ containerMixin.js │ │ wrapper.vue │ │ │ ├─gisMap // 地图组件 │ └─pool // 可复用的组件池 │ └─children // 组件池中可复用的子组件 │ ├─dashboards // 看板配置目录 │ │ capabilityBase.js // 某个看板 │ │ componentDemo.js // 另一个看板 │ │ demo.js │ │ demo1.js │ ├─dashboard1 // 看板配置较多用文件夹包裹 │ └─dashboard2 // 同理 │ ├─module // 组件类 │ │ Action.js // 事件类 │ │ Echart.js // 图表类 │ │ Filter.js // 过滤类 │ │ Pic.js // 图片类 │ │ ReadMe.md │ │ TextField.js // 文本类 │ │ │ └─core // 核心类 │ BaseComponent.js // 所有组件的基类 │ Container.js // 看板容器类 ├─router // 配置路由,用于显示views中的页面 ├─store // 全局存储 ├─style // 通用样式 │ │ index.scss │ │ variables.scss // 变量 │ └─element-ui │ │ element-variables.scss // 覆写element-ui的变量 │ └─components // 覆写element-ui的组件class | button.scss // 同名的组件scss ├─utils // 工具函数 └─views // 页面入口 ``` ## business业务组件目录介绍 ## 技术栈 vite-vue2 > ### Vue2 + Vite + Vuex + Vue-router + Axios > #### USE > ``` npm install ``` > #### RUN ``` npm run serve ``` > #### BUILD ``` npm run build:prod ``` ## 数据大屏 数据大屏的开发有以下几个问题需要明确并解决 - 缩放问题 - 大屏缩放 - 组件缩放 - popover缩放 - 组件问题 - 组件数量多,需要阶段性沉淀 - 组件注册繁琐 - 样式问题 - 看板的主题色 - 组件样式重复&冲突 - 组件字体 - 数据问题(此处不讨论统一查询接口) - 接口数据,数据格式转换 - 多个后端环境的切换 - 交互联动问题 - 明确数据流与查询方式 - 性能问题 - 响应式数据的取舍 - 大数据量优化 本项目作为样例工程,旨在解决数据大屏开发过程中的通用性问题 最终通过 开发通用组件 + 编写组件配置的形式形成数据大屏 市面上的可视化工具非常多,他们也许能解决70%的问题,但是剩余的30%会使使用者非常困惑,所以选择手动开发,早日完成定制化项目~ ## 使用说明 本项目基于目录结构约定开发的行为 1. 编写可视化组件 2. 编写组件类 3. 编写看板配置 4. 编写控件(数据过滤查询) ### 编写可视化组件 vue组件中的`name`属性必填,须和文件名一致 推荐在`/src/components/pool/`文件夹下以 `.vue` 单文件形式(SFC)编写`通用组件` 推荐在`/src/components/business/`文件夹下以 `.vue` 单文件形式(SFC)编写`业务组件` `pool`和`business` 目录下的`vue`组件会被自动注册为全局组件 ### 编写组件类 组件类为非必须,只有在`BaseComponent`无法满足的情况下需要编写 推荐在`/src/module/`文件夹下以大驼峰命名组件的`module`,class的形式可以避免很多不必要的错误 ### 编写看板配置 看板配置是整个看板的结构化描述,在云洄中以可视化编辑的形式存在,但在此开发框架中需要自己手动填写 ```js // 最简单的组件例子 new BaseComponent(b('graph', 300, 400, 900, 300, null, {method: 'get', url: 'http://172.38.110.228:30032/api/v1/pieGraph'})) ``` `b`函数做了两件事 1. 位置参数转换为对象,方便class解构 2. 合并多余的对象参数,方便组件类解构,支持设置组件的上下层级,详见`demo.js` #### 组件层级 组件的层级关系在声明组件列表时会`隐式地从下到上层叠`,只需要声明时注意顺序就可以避免层叠的问题,`{zIndex: 2}`或者`{'z-index': 2}`只是另一个显式的解决办法 推荐在`/src/dashboards/`文件夹下参照 `demo.js` 编写 ### 编写控件 控件的存在用于过滤数据,在此类看板项目中,数据的流转不做复杂设置 大概有以下三种方式 1. 以看板为整体,控件设置看板状态,组件监听状态做自身的数据刷新 2. 组件之间以订阅发布(pubsub)做相互的消息传递 3. 使用发布订阅模式,使组件之间具有订阅关系 为了项目的简易上手,选择前两种作为示例,第三种可以自行发挥 注意点 1. 控件必须以 class `Filter`作为实例化来源,在组件列表声明后会通过`Container`类初始化联动关系 2. 联动关系会自动在 `mixin.js` 中初始化联动逻辑 3. 控件的编写需要使用类似以下代码对联动的组件进行消息传递 ```js this.pubsub.publish(id, { component: {[filterKey]: e} }); ``` ### 事件关联 本章节中需要了解系统内的交互核心`pubsub`,基于此实现了组件之间的互相订阅和发布消息。 所有组件基于配置在mixin.js中订阅消息,控件基于关联组件配置发送消息 一般由发起方配置关联组件即可,接收方会自动添加订阅关系 ```js // mixin.js中订阅 this.pubsub.subscribe(id, (msg, data) => {}) // 组件发布消息 this.pubsub.publish(id, data); ``` 一对多关联配置 #### 参数过滤 必须使用Filter类包装,且必须的一个参数 - relatedWidgets 其中的filterKey是自身定义的,可以是其他的 ```js // 参数过滤 // 配置文件 config new Filter(b('SelectFilterCommon', 200, 80, 300, 100, {filterKey: 'name',relatedWidgets: ['echart-1','echart-2']}, null)), // 控件 SelectFilterCommon change(e) { let {id, option: { filterKey }} = this.instance; // 发送消息 this.pubsub.publish(id, { [filterKey]: e }); } // 订阅在组件mixin中统一处理了 ``` #### 组件显隐 必须的三个参数 - relatedWidgets - showWidgets - actionType: Action.SHOW ```js new Action(b('OrangeBlueTabs', 210, 27, 130, 75, { relatedWidgets: ['cardata-StatisticCards-5', 'cardata-StatisticCards-6'], showWidgets: [['cardata-StatisticCards-5'], ['cardata-StatisticCards-6']], actionType: Action.SHOW, isShowArrow: false, tabData: [{ label: '进烟车辆', key: 0, }, { label: '出烟车辆', key: 1, }, ] })) // tab组件发送消息 this.pubsub.publish(id, { component: this.activeComponents }); ``` #### 组件移动 必须的三个参数 - relatedWidgets - moveTo - actionType ```js // 边栏收放按键,7 new Action(b('retractLay', 650, 70, -64, 1010, { relatedWidgets: ['DynamicComponentContainer-8', 'DynamicComponentContainer-9', 'pic-10', 'popDialog-11'], moveTo: [[-500, 114], [1925, 114], [1925, 114], [1925, 123]], actionType: Action.MOVE })), // 控件 retractLay zhankaiFn() { this.zhankai = !this.zhankai; let {id,option: { relatedWidgets, moveTo }} = this.instance; // 发送消息 this.pubsub.publish(id, mergeArraysToObject(relatedWidgets, moveTo)); } ``` #### 初始化事件 允许此组件等待其他组件渲染完毕以消息形式调用此组件的初始化事件 ```js // 所有组件mixin // 当目标组件配置 waitMsg 为true时,此组件不会进行初始化,而是等待初始化的消息才进行 mounted() { // ... // let { rest } = this.instance; if (rest) { let { waitMsg } = rest; if (waitMsg) { console.log('waiting'); } else { this.mountedInit() } } else { this.mountedInit() } // ... // case Filter.INIT: console.log("接收到init事件调用,开始组件INIT"); this.queryParams = Object.assign({}, this.queryParams, data); this.mountedInit() // ... // } // 事件发起的组件 init() { let { id } = this.instance; console.log(this.queryResult); //初始化默认选中综合交通要素 this.value = [this.queryResult[0].value, "comp"]; // 此处暂时使用延时等待目标组件渲染完毕 setTimeout(() => { this.pubsub.publish(genPublishId(id, Filter.INIT), { xzqh: this.queryResult[0].value, fxpgdjLx: "comp", date: "2023-07-14", type: "drv", }); this.addRoadLineLayer(); }, 1700); }, ``` #### 其他 自定义事件,执行目标组件中的customEvent事件 ```js // 以地图选择器举例 new Action(b('riskLegendMap', 186, 128, 1218, 932, { layerArray: riskLayerList.driverCar, actionType: Action.CUSTOM, customKey: 'list', relatedWidgets: ['mineMap-7'] }, null, { 'z-index': 1 })), // mineMap customEvent(data) { this.dataList = data.layers.list; for (let model of data.layers.list) { if (model.check) { if (model.to) { mineMapGis.flyTo(model.to) } } else { } } } ``` #### 多事件发生器 某些场景,一个组件要发起多类消息 必须使用`MultActions`类包裹,并且必须有 - multActions: 其中的内容配置照搬上面所有的配置项即可 ```js // 多事件发生器 配置参考 new MultActions(b('titleWithMutilSelection', 476, 58, 20, 114, { title: '区域和交通要素选择', multActions: [{ relatedWidgets: ['DynamicComponentContainer-1', 'DynamicComponentContainer-2', 'DynamicComponentContainer-3', 'DynamicComponentContainer-4', 'DynamicComponentContainer-5', 'DynamicComponentContainer-6', 'mineMap-7'], showWidgets: [ ['DynamicComponentContainer-1', 'riskLegendMap-8', 'DynamicComponentContainer-14', 'mineMap-7', 'DynamicComponentContainer-6'], ['DynamicComponentContainer-2', 'riskLegendMap-9', 'DynamicComponentContainer-15', 'mineMap-7'] ], actionType: Action.SHOW, }, { relatedWidgets: ['transport-echart-2', 'transport-echart-3'], actionType: Filter.INIT }, { relatedWidgets: ['transport-echart-2', 'transport-echart-3'], actionType: Filter.FILTER }, ] })) // 因为有了这么多的订阅关系,所以在发起消息的组件中可以在合适的环节去发起消息 // 需要注意的是,此时的id应该由genPublishId(id, {actionType})替代 this.pubsub.publish(genPublishId(id, Action.SHOW), { component: this.activeComponents, layers: { list: [], }, selectionChanged: true, }); this.pubsub.publish(genPublishId(id, Filter.FILTER), { xzqh: xzqh, fxpgdjLx: fxpgdjLx, date: "2023-07-14", type: fxpgdjLx, }); ``` #### 主题色 本项目使用element-ui作为ui组件库 主题色文件在`src/style/element-ui/element-variables.scss` 开发说明 1. 在`src/style/variables.scss`中声明全局变量 2. 用于重写elementui的变量以`$style-el`作为前缀,变量名参考根目录的`element-variables.scss`文件 3. 组件复杂样式在`src/style/element-ui/components/`下声明组件样式文件 组件样式覆写参考代码 ##### 覆盖部分变量 ```scss /*改变button*/ $--button-font-size:64px; @import "element-ui/packages/theme-chalk/src/card"; ``` ##### 覆盖部分class 组件样式开发 `src/style/element-ui/components/el-card.scss` ```scss .el-card{ &__header { padding: 5px 10px; } } ``` 样式引入 `src/style/element-ui/element-variables.scss` ```scss @import "element-ui/packages/theme-chalk/src/card"; @import "./components/el-card.scss"; ``` ### 看板展示 看板本身在 `/src/views/`文件夹下,依据`Home.vue` 编写 ### 图片资源放置及引用 项目大屏,图片资源统一放置在`public/assets/projects/projectYT`目录下,各个大屏建立各自的文件夹存放资源, 可复用组件引用的图片直接放在projectYT文件夹下 资源引用方式:css通过url('文件路径'),js通过@import