| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- <template>
- <DashboardLayout>
- <!-- 天气 -->
- <template #header-left>
-
- </template>
- <!-- 日期 -->
- <template #header-right>
- <DateTimeWidget />
- </template>
- <!-- 地图 -->
- <template #map>
- <TongzhouTrafficMap
- ref="trafficMapRef"
- amapKey="db2da7e3e248c3b2077d53fc809be63f"
- securityJsCode="a7413c674852c5eaf01d90813c5b7ef6"
- />
- </template>
- <template #left>
- <div class="panel-list">
- <div class="panel-item">
- <PanelContainer title="在线状态">
- <OnlineStatusTabs :deviceData="onlineStatusData" />
- </PanelContainer>
- </div>
- <div class="panel-item">
- <PanelContainer title="控制模式">
- <TickDonutChart :chartData="controlInfoData" :centerTitle="controlTotal + '个'" centerSubTitle="控制信息" />
- </PanelContainer>
- </div>
- <div class="panel-item">
- <PanelContainer title="故障报警" class="alarm-panel">
- <SeamlessScroll :data="alarmData" :limit="3" measureSelector=".alarm-list-container">
- <template #default="{ list }">
- <AlarmMessageList :listData="list" @ignore="onAlarmIgnore" @view="onAlarmView" />
- </template>
- </SeamlessScroll>
- </PanelContainer>
- </div>
- </div>
- </template>
- <template #right>
- <div class="panel-list">
- <div class="panel-item">
- <PanelContainer title="设备状态">
- <DeviceStatusTabs :statusData="deviceFaultData" />
- </PanelContainer>
- </div>
- <div class="panel-item">
- <PanelContainer title="勤务执行" class="table-panel">
- <SeamlessScroll :data="tableData" :limit="4">
- <template #default="{ list }">
- <TechTable ref="dutyTable" :columns="tableColumns" :data="list" >
- <template #level="{ row }">
- <span :title="row.level" :style="{ color: row.level === '二级' ? '#FFDF0C' : '#F00' }">
- {{ row.level }}
- </span>
- </template>
- <template #status="{ row }">
- <span :title="row.status" :style="{ color: row.status === '进行中' ? '#FFDF0C' : '#F00' }">
- {{ row.status }}
- </span>
- </template>
- <template #action="{ row }">
- <span class="action-btn" @click="handleView(row)">
- 查看
- </span>
- </template>
- </TechTable>
- </template>
- </SeamlessScroll>
- </PanelContainer>
- </div>
- <div class="panel-item">
- <PanelContainer title="关键路口" class="table-panel">
- <SeamlessScroll :data="keyIntersectionData" :limit="4">
- <template #default="{ list }">
- <TechTable :columns="keyIntersectionColumns" :data="list"
- @row-click="onIntersectionRowClick" />
- </template>
- </SeamlessScroll>
- </PanelContainer>
- </div>
- </div>
- </template>
- <template #center>
- </template>
- </DashboardLayout>
- </template>
- <script>
- import DashboardLayout from '@/layouts/DashboardLayout.vue';
- import WeatherWidget from '@/components/ui/WeatherWidget.vue';
- import DateTimeWidget from '@/components/ui/DateTimeWidget.vue';
- import PanelContainer from '@/components/ui/PanelContainer.vue';
- import TickDonutChart from '@/components/ui/TickDonutChart.vue';
- import AlarmMessageList from '@/components/ui/AlarmMessageList.vue';
- import TechTable from '@/components/ui/TechTable.vue';
- import TongzhouTrafficMap from '@/components/TongzhouTrafficMap.vue';
- import OnlineStatusTabs from '@/components/ui/OnlineStatusTabs.vue';
- import DeviceStatusTabs from '@/components/ui/DeviceStatusTabs.vue';
- import SeamlessScroll from '@/components/ui/SeamlessScroll.vue';
- import { apiGetControlModeStats, apiGetLatestAlarms, apiGetTasks, apiGetKeyIntersections, apiGetDeviceStatus, apiGetDeviceFaultStatus } from '@/api';
- export default {
- name: "HomePage",
- components: {
- DashboardLayout,
- WeatherWidget,
- DateTimeWidget,
- PanelContainer,
- TickDonutChart,
- AlarmMessageList,
- TechTable,
- TongzhouTrafficMap,
- OnlineStatusTabs,
- DeviceStatusTabs,
- SeamlessScroll
- },
- data() {
- return {
- dutyScrollTimer: null,
- currentScrollTop: 0, // 记录当前的精确滚动像素
- isDataDoubled: false, // 记录数据是否已经克隆翻倍
- scrollContainer: null, // 存放表格内部滚动容器的 DOM
- controlInfoData: [],
- alarmData: [],
- onlineStatusData: null,
- deviceFaultData: null,
- // 1. 表头
- tableColumns: [
- { label: '序号', key: 'id', width: '14%' },
- { label: '名称', key: 'name', width: '20%' },
- { label: '执行人', key: 'executor', width: '18%' },
- { label: '等级', key: 'level', width: '14%' },
- { label: '状态', key: 'status', width: '20%' },
- { label: '操作', key: 'action', width: '14%' }
- ],
- tableData: [],
- // 1. 表头
- keyIntersectionColumns: [
- { label: '路口', key: 'intersection', align: 'left' }, // 路口名称较长,建议左对齐更好看
- { label: '运营模式', key: 'mode', width: '120px' },
- { label: '方案号', key: 'plan', width: '80px' }
- ],
- keyIntersectionData: [],
- // 搜索数据
- currentMapSearch: 'all',
- mapSearchOptions: [
- { label: '全部', value: 'all' },
- { label: '选项2', value: '1' },
- { label: '选项3', value: '2' },
- ]
- };
- },
- computed: {
- controlTotal() {
- return this.controlInfoData.reduce((s, m) => s + (m.value || 0), 0);
- }
- },
- async mounted() {
- await this.fetchPageData();
- },
- beforeDestroy() {
-
- },
- methods: {
- async fetchPageData() {
- const [controlData, alarmData, taskData, keyData, onlineData, faultData] = await Promise.all([
- apiGetControlModeStats(),
- apiGetLatestAlarms(),
- apiGetTasks({ pageSize: 10 }),
- apiGetKeyIntersections(),
- apiGetDeviceStatus(),
- apiGetDeviceFaultStatus(),
- ]);
- this.controlInfoData = controlData || [];
-
- // 优先使用地图生成的告警数据(与地图点位完全匹配)
- const mapAlarms = localStorage.getItem('alarmListFromMap');
- if (mapAlarms) {
- try {
- const parsed = JSON.parse(mapAlarms);
- this.alarmData = parsed.map((a, i) => ({
- ...a,
- time: new Date(Date.now() - i * 180000).toLocaleTimeString(),
- }));
- } catch (e) {
- this.alarmData = alarmData?.list || alarmData || [];
- }
- } else {
- this.alarmData = alarmData?.list || alarmData || [];
- }
-
- this.tableData = taskData?.list || taskData || [];
- this.keyIntersectionData = keyData || [];
- this.onlineStatusData = onlineData || null;
- this.deviceFaultData = faultData || null;
- },
- // 处理忽略逻辑
- onAlarmIgnore({ item, index }) {
- console.log('点击了忽略:', item.title);
- // 真实业务中可能会调接口,这里我们可以演示本地移除:
- this.alarmData.splice(index, 1);
- },
- // 处理查看逻辑
- onAlarmView({ item, index }) {
- console.log('点击了查看:', item);
- let position;
- // 优先使用 item 自身携带的坐标(最可靠,不受索引偏移影响)
- if (item.position && item.position.length === 2) {
- position = [Number(item.position[0]), Number(item.position[1])];
- } else {
- // 兜底:通过索引从 localStorage 查找
- const actualIndex = index % 12;
- const positionStr = localStorage.getItem(`pos${actualIndex + 1}`);
- if (positionStr) {
- const parts = positionStr.split(',');
- position = [Number(parts[0]), Number(parts[1])];
- } else {
- console.warn('未找到对应的位置信息,使用默认位置');
- position = [116.663, 39.905];
- }
- }
- // 地图联动
- console.log(position);
- this.$refs.trafficMapRef.focusByLocation(position);
- },
- onIntersectionRowClick({ row, index }) {
- console.log(`准备跳转查看关键路口详情,当前路口:`, row.id, row.intersection);
- // 使用 Vue Router 跳转,将信息通过 URL 参数 (query) 带过去
- // 注意:这里的 path 请替换为你项目中“状态监控”页面的真实路由路径
- this.$router.push({
- path: '/surve', // 替换为真实的路由
- query: {
- tab: 'crossing', // 告诉目标页面:把 Tab 切到“路口”页
- action: 'open-dialog', // 告诉目标页面:直接打开弹窗
- id: row.id, // 传递路口 ID
- }
- });
- },
- // 处理搜索
- handleSearch() {
- console.log('搜索', this.currentMapSearch);
- },
- // 跳转逻辑修改
- handleView(row) {
- console.log('准备跳转查看特勤线路,当前数据:', row);
- // 使用 Vue Router 跳转,将信息通过 URL 参数 (query) 带过去
- // 注意:这里的 path 请替换为你项目中“状态监控”页面的真实路由路径
- this.$router.push({
- path: '/surve', // 或者使用 name: 'StatusMonitoring'
- query: {
- tab: 'specialDuty', // 告诉目标页面:把 Tab 切到“特勤线路”
- action: 'open-dialog', // 告诉目标页面:直接弹窗
- id: row.id // 传递这条特勤线路的唯一 ID,用来请求弹窗详情接口
- }
- });
- },
- }
- }
- </script>
- <style scoped>
- .panel-list {
- display: flex;
- flex-direction: column;
- gap: 16px;
- height: 100%;
- }
- .panel-item {
- /* 可用高度 = 100vh - 80px(header) - 20px(上padding) - 60px(下padding)
- 3个面板 + 2个gap(16px) = 32px
- 每个面板 = (100vh - 192px) / 3 */
- height: calc((100vh - 192px) / 3);
- }
- .table-panel ::v-deep .panel-content {
- padding: 0;
- }
- .action-btn {
- color: #c4d7f0;
- cursor: pointer;
- transition: color 0.3s;
- user-select: none;
- }
- .action-btn:hover {
- color: #32F6F8;
- text-decoration: underline;
- }
- .map-legend-pos {
- position: absolute;
- bottom: 100px;
- right: 0;
- }
- .top-search-pos {
- position: absolute;
- top: 0;
- right: 0;
- display: flex;
- flex-direction: row;
- column-gap: 9px;
- }
- .table-panel ::v-deep .tech-table-wrapper {
- height: auto !important;
- max-height: none !important;
- overflow: visible !important;
- }
- .alarm-panel ::v-deep .alarm-list-container {
- height: auto !important;
- overflow: visible !important;
- padding: 0 15px !important;
- }
- .alarm-panel ::v-deep .panel-content {
- padding: 0;
- }
- </style>
|