Home.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <template>
  2. <DashboardLayout>
  3. <!-- 天气 -->
  4. <template #header-left>
  5. <WeatherWidget />
  6. </template>
  7. <!-- 日期 -->
  8. <template #header-right>
  9. <DateTimeWidget />
  10. </template>
  11. <!-- 地图 -->
  12. <template #map>
  13. <TongzhouTrafficMap
  14. amapKey="db2da7e3e248c3b2077d53fc809be63f"
  15. securityJsCode="a7413c674852c5eaf01d90813c5b7ef6"
  16. />
  17. </template>
  18. <template #left>
  19. <div class="panel-list">
  20. <div class="panel-item">
  21. <PanelContainer title="在线状态">
  22. <TechTabs v-model="onlineStatusActiveTab" type="segmented" autoPlay @tab-click="handleTabClick">
  23. <TechTabPane label="信号机" name="signalMachine" style="height: 100%;">
  24. <DynamicDonutChart v-if="onlineStatusDisplayData" :chartData="onlineStatusDisplayData.chartData"
  25. :centerTitle="onlineStatusDisplayData.centerTitle" :centerSubTitle="onlineStatusDisplayData.centerSubTitle" customHeight="163px" />
  26. </TechTabPane>
  27. <TechTabPane label="检测器" name="detector">
  28. <DynamicDonutChart v-if="onlineStatusDisplayData" :chartData="onlineStatusDisplayData.chartData"
  29. :centerTitle="onlineStatusDisplayData.centerTitle" :centerSubTitle="onlineStatusDisplayData.centerSubTitle" customHeight="163px"/>
  30. </TechTabPane>
  31. <TechTabPane label="红绿灯" name="trafficLight">
  32. <DynamicDonutChart v-if="onlineStatusDisplayData" :chartData="onlineStatusDisplayData.chartData"
  33. :centerTitle="onlineStatusDisplayData.centerTitle" :centerSubTitle="onlineStatusDisplayData.centerSubTitle" customHeight="163px"/>
  34. </TechTabPane>
  35. </TechTabs>
  36. </PanelContainer>
  37. </div>
  38. <div class="panel-item">
  39. <PanelContainer title="控制模式">
  40. <TickDonutChart
  41. :chartData="controlInfoData"
  42. centerTitle="650个"
  43. centerSubTitle="控制信息"
  44. />
  45. </PanelContainer>
  46. </div>
  47. <div class="panel-item">
  48. <PanelContainer title="故障报警">
  49. <AlarmMessageList
  50. :listData="alarmData"
  51. @ignore="onAlarmIgnore"
  52. @view="onAlarmView"
  53. />
  54. </PanelContainer>
  55. </div>
  56. </div>
  57. </template>
  58. <template #right>
  59. <div class="panel-list">
  60. <div class="panel-item">
  61. <PanelContainer title="设备状态">
  62. <TechTabs v-model="onlineStatusActiveTab" type="segmented" autoPlay @tab-click="handleTabClick">
  63. <TechTabPane label="信号机" name="signalMachine" style="height: 100%;">
  64. <DynamicDonutChart v-if="onlineStatusDisplayData" :chartData="onlineStatusDisplayData.chartData"
  65. :centerTitle="onlineStatusDisplayData.centerTitle" :centerSubTitle="onlineStatusDisplayData.centerSubTitle"/>
  66. </TechTabPane>
  67. <TechTabPane label="检测器" name="detector">
  68. <DynamicDonutChart v-if="onlineStatusDisplayData" :chartData="onlineStatusDisplayData.chartData"
  69. :centerTitle="onlineStatusDisplayData.centerTitle" :centerSubTitle="onlineStatusDisplayData.centerSubTitle"/>
  70. </TechTabPane>
  71. <TechTabPane label="红绿灯" name="trafficLight">
  72. <DynamicDonutChart v-if="onlineStatusDisplayData" :chartData="onlineStatusDisplayData.chartData"
  73. :centerTitle="onlineStatusDisplayData.centerTitle" :centerSubTitle="onlineStatusDisplayData.centerSubTitle"/>
  74. </TechTabPane>
  75. </TechTabs>
  76. </PanelContainer>
  77. </div>
  78. <div class="panel-item">
  79. <PanelContainer title="勤务执行" class="table-panel">
  80. <TechTable :columns="tableColumns" :data="tableData" height="263px">
  81. <template #level="{ row }">
  82. <span :style="{ color: row.level === '二级' ? '#FFDF0C' : 'inherit' }">
  83. {{ row.level }}
  84. </span>
  85. </template>
  86. <template #status="{ row }">
  87. <span :style="{ color: row.status === '进行中' ? '#FFDF0C' : 'inherit' }">
  88. {{ row.status }}
  89. </span>
  90. </template>
  91. <template #action="{ row }">
  92. <span
  93. class="action-btn"
  94. @click="handleView(row)"
  95. >
  96. 查看
  97. </span>
  98. </template>
  99. </TechTable>
  100. </PanelContainer>
  101. </div>
  102. <div class="panel-item">
  103. <PanelContainer title="关键路口" class="table-panel">
  104. <TechTable
  105. :columns="keyIntersectionColumns"
  106. :data="keyIntersectionData"
  107. height="263px"
  108. @row-click="onIntersectionRowClick"
  109. />
  110. </PanelContainer>
  111. </div>
  112. </div>
  113. </template>
  114. <template #center>
  115. </template>
  116. </DashboardLayout>
  117. </template>
  118. <script>
  119. import DashboardLayout from '@/layouts/DashboardLayout.vue';
  120. import WeatherWidget from '@/components/ui/WeatherWidget.vue';
  121. import DateTimeWidget from '@/components/ui/DateTimeWidget.vue';
  122. import PanelContainer from '@/components/ui/PanelContainer.vue';
  123. import DynamicDonutChart from '@/components/ui/DynamicDonutChart.vue';
  124. import TechTabs from '@/components/ui/TechTabs.vue';
  125. import TechTabPane from '@/components/ui/TechTabPane.vue';
  126. import TickDonutChart from '@/components/ui/TickDonutChart.vue';
  127. import AlarmMessageList from '@/components/ui/AlarmMessageList.vue';
  128. import TechTable from '@/components/ui/TechTable.vue';
  129. import TongzhouTrafficMap from '@/components/TongzhouTrafficMap.vue';
  130. const mockDeviceData = {
  131. 'signalMachine': {
  132. centerTitle: '98%',
  133. centerSubTitle: '980/1000',
  134. chartData: [
  135. { name: '在线', value: 980, color: '#32F6F8' },
  136. { name: '离线', value: 20, color: '#E4D552' }
  137. ]
  138. },
  139. 'detector': {
  140. centerTitle: '85%',
  141. centerSubTitle: '425/500',
  142. chartData: [
  143. { name: '正常', value: 425, color: '#32F6F8' },
  144. { name: '故障', value: 50, color: '#faad14' },
  145. { name: '掉线', value: 25, color: '#ff4d4f' }
  146. ]
  147. },
  148. 'trafficLight': {
  149. centerTitle: '99%',
  150. centerSubTitle: '1188/1200',
  151. chartData: [
  152. { name: '正常发光', value: 1188, color: '#32F6F8' },
  153. { name: '灯组损坏', value: 12, color: '#ff4d4f' }
  154. ]
  155. }
  156. };
  157. export default {
  158. name: "HomePage",
  159. components: {
  160. DashboardLayout,
  161. WeatherWidget,
  162. DateTimeWidget,
  163. PanelContainer,
  164. DynamicDonutChart,
  165. TechTabs,
  166. TechTabPane,
  167. TickDonutChart,
  168. AlarmMessageList,
  169. TechTable,
  170. TongzhouTrafficMap
  171. },
  172. data() {
  173. return {
  174. // 在线状态面板
  175. onlineStatusActiveTab: 'detector',
  176. onlineStatusDisplayData: mockDeviceData['detector'],
  177. controlInfoData: [
  178. { name: '定周期控制', value: 400, color: '#33a3ff' }, // 蓝色
  179. { name: '感应控制', value: 50, color: '#e6734d' }, // 橙色
  180. { name: '干线协调', value: 200, color: '#10b981' }, // 绿色
  181. { name: '黄闪控制', value: 6, color: '#eab308' }, // 黄色
  182. { name: '关灯控制', value: null,color: '#64748b' }, // 灰色 (没有值传入null即可隐藏数字)
  183. { name: '自适应控制', value: 10, color: '#2dd4bf' }, // 青色
  184. { name: '中心控制', value: null,color: '#8b5cf6' }, // 紫色
  185. { name: '全红控制', value: null,color: '#f43f5e' } // 红色
  186. ],
  187. // 消息模拟数据
  188. alarmData: [
  189. {
  190. id: '1',
  191. title: '通讯中断',
  192. type: 'error', // 渲染为红色
  193. time: '16:28:28',
  194. description: '中关村大街-科学院南路口-设备离线'
  195. },
  196. {
  197. id: '2',
  198. title: '2.降级黄闪',
  199. type: 'warning', // 渲染为黄色
  200. time: '', //
  201. description: '中关村大街-科学院南路口-设备离线'
  202. },
  203. {
  204. id: '3',
  205. title: '3.降级黄闪',
  206. type: 'warning',
  207. time: '16:28:28',
  208. description: '中关村大街-科学院南路口-设备离线'
  209. }
  210. ],
  211. // 1. 表头
  212. tableColumns: [
  213. { label: '序号', key: 'id', width: '60px' },
  214. { label: '名称', key: 'name' },
  215. { label: '执行人', key: 'executor' },
  216. { label: '等级', key: 'level' },
  217. { label: '状态', key: 'status' },
  218. { label: '操作', key: 'action', width: '80px' }
  219. ],
  220. // 2. 模拟数据源
  221. tableData: [
  222. { id: 1, name: '测试', executor: '测试', level: '一级', status: '未开始' },
  223. { id: 2, name: '张飞', executor: '张飞', level: '一级', status: '未开始' },
  224. { id: 3, name: '关将', executor: '关将', level: '二级', status: '进行中' },
  225. { id: 4, name: '刘备', executor: '刘备', level: '一级', status: '未开始' },
  226. { id: 5, name: '孙权', executor: '孙权', level: '一级', status: '未开始' },
  227. ],
  228. // 1. 表头
  229. keyIntersectionColumns: [
  230. { label: '路口', key: 'intersection', align: 'left' }, // 路口名称较长,建议左对齐更好看
  231. { label: '运营模式', key: 'mode', width: '120px' },
  232. { label: '方案号', key: 'plan', width: '80px' }
  233. ],
  234. // 2. 模拟数据源 (完美还原截图内容)
  235. keyIntersectionData: [
  236. { intersection: '实行东街双园路交叉路口', mode: '定周期控制', plan: '4' },
  237. { intersection: '实行东街双园路交叉路口', mode: '自适应控制', plan: '1' },
  238. { intersection: '实行东街双园路交叉路口', mode: '感应控制', plan: '5' }
  239. ],
  240. // 搜索数据
  241. currentMapSearch: 'all',
  242. mapSearchOptions: [
  243. {label: '全部', value: 'all' },
  244. {label: '选项2', value: '1' },
  245. {label: '选项3', value: '2' },
  246. ]
  247. };
  248. },
  249. mounted() {
  250. },
  251. methods: {
  252. // 监听 Tab 切换事件
  253. handleTabClick(selectedTabName) {
  254. console.log('用户切换了设备类型:', selectedTabName);
  255. // 从 mock 字典中取出对应的数据并赋值,图表会自动响应式更新!
  256. if (mockDeviceData[selectedTabName]) {
  257. this.onlineStatusDisplayData = mockDeviceData[selectedTabName];
  258. }
  259. },
  260. // 处理忽略逻辑
  261. onAlarmIgnore({ item, index }) {
  262. console.log('点击了忽略:', item.title);
  263. // 真实业务中可能会调接口,这里我们可以演示本地移除:
  264. // this.alarmData.splice(index, 1);
  265. },
  266. // 处理查看逻辑
  267. onAlarmView({ item, index }) {
  268. console.log('点击了查看:', item.title);
  269. // 这里可以触发打开一个弹窗 (调用你之前的 SmartDialog 或者路由跳转)
  270. },
  271. handleView(row) {
  272. console.log('点击了查看,当前行数据:', row);
  273. // 这里可以触发弹窗或路由跳转
  274. },
  275. onIntersectionRowClick({ row, index }) {
  276. console.log(`用户点击了第 ${index + 1} 行,路口名称是:`, row.intersection);
  277. },
  278. // 处理搜索
  279. handleSearch() {
  280. console.log('搜索', this.currentMapSearch);
  281. }
  282. }
  283. }
  284. </script>
  285. <style scoped>
  286. .panel-list {
  287. display: flex;
  288. flex-direction: column;
  289. gap: 16px;
  290. }
  291. .panel-item {
  292. height: 254px;
  293. }
  294. .table-panel ::v-deep .panel-content {
  295. padding: 0;
  296. }
  297. .action-btn {
  298. color: #c4d7f0;
  299. cursor: pointer;
  300. transition: color 0.3s;
  301. user-select: none;
  302. }
  303. .action-btn:hover {
  304. color: #32F6F8;
  305. text-decoration: underline;
  306. }
  307. .map-legend-pos {
  308. position: absolute;
  309. bottom: 100px;
  310. right: 0;
  311. }
  312. .top-search-pos {
  313. position: absolute;
  314. top: 0;
  315. right: 0;
  316. display: flex;
  317. flex-direction: row;
  318. column-gap: 9px;
  319. }
  320. </style>