|
|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <div class="page">
|
|
|
+ <div class="page" @mousemove="onMouseMove" @mouseleave="onMouseLeave">
|
|
|
<!-- Top Header -->
|
|
|
<header class="topbar">
|
|
|
<div class="top-left">
|
|
|
@@ -10,7 +10,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="top-center">
|
|
|
+ <div class="top-center" style="visibility: hidden;">
|
|
|
<div class="title-frame">
|
|
|
<div class="title">交通信号控制平台</div>
|
|
|
</div>
|
|
|
@@ -71,6 +71,7 @@
|
|
|
{{ onlineTabsMap[onlineTab] }} · {{ onlineView.online }}/{{ onlineView.total }}
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <!-- <div class="dock-arrow right" @click="dockNext" title="下一页"></div> -->
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -78,36 +79,24 @@
|
|
|
<div class="card card-b">
|
|
|
<div class="card-h">
|
|
|
<span class="h-dot"></span>
|
|
|
- <span>控制模式</span>
|
|
|
+ <span>在线状态</span>
|
|
|
</div>
|
|
|
|
|
|
- <div class="mode-grid">
|
|
|
- <div class="mode">
|
|
|
- <div class="mode-h">中心控制</div>
|
|
|
- <div class="mode-b">
|
|
|
- <span class="pill">中心控制</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <div class="tabs small">
|
|
|
+ <button class="tab" :class="{ active: true }">信号机</button>
|
|
|
+ <button class="tab">检测器</button>
|
|
|
+ <button class="tab">红绿灯</button>
|
|
|
+ </div>
|
|
|
|
|
|
- <div class="mode">
|
|
|
- <div class="mode-h">本地控制</div>
|
|
|
- <div class="mode-b">
|
|
|
- <span class="pill">定周期</span>
|
|
|
- <span class="pill">感应</span>
|
|
|
- <span class="pill">协调</span>
|
|
|
- <span class="pill">自适应</span>
|
|
|
- <span class="pill">手动</span>
|
|
|
+ <div class="row row-b">
|
|
|
+ <div class="legend legend-b">
|
|
|
+ <div class="lg" v-for="it in controlLegend" :key="it.k">
|
|
|
+ <span class="b" :class="it.cls"></span><span>{{ it.t }}</span>
|
|
|
+ <span class="num">{{ it.v }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="mode">
|
|
|
- <div class="mode-h">特殊控制</div>
|
|
|
- <div class="mode-b">
|
|
|
- <span class="pill warn">黄闪</span>
|
|
|
- <span class="pill warn">全红</span>
|
|
|
- <span class="pill mute">关灯</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <div ref="controlChart" class="chart donut small"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -127,7 +116,7 @@
|
|
|
</div>
|
|
|
<div class="a-sub">
|
|
|
<span class="loc">{{ a.loc }}</span>
|
|
|
- <span class="time">{{ a.time }}</span>
|
|
|
+ <!-- <span class="time">{{ a.time }}</span> -->
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="a-actions">
|
|
|
@@ -188,6 +177,25 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <!-- Bottom Dock (overlay on map) -->
|
|
|
+ <div class="dock-wrap" @mousemove="onDockMove" @mouseleave="onDockLeave">
|
|
|
+ <div class="dock-arrow left" @click="dockPrev" title="上一页"></div>
|
|
|
+ <div class="dockbar">
|
|
|
+ <div
|
|
|
+ v-for="(m, idx) in modules"
|
|
|
+ :key="m.key"
|
|
|
+ class="dock-item"
|
|
|
+ :class="{ active: activeModule === m.key }"
|
|
|
+ :style="navItemStyle(idx)"
|
|
|
+ @click="selectModule(m)"
|
|
|
+ >
|
|
|
+ <div class="dock-icon" :style="navIconStyle(m)"></div>
|
|
|
+ <div class="dock-label">{{ m.title }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="dock-arrow right" @click="dockNext" title="下一页"></div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
@@ -228,6 +236,7 @@
|
|
|
{{ deviceTabsMap[deviceTab] }} · {{ deviceView.fault === 0 ? "全绿" : "需处理" }}
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <!-- <div class="dock-arrow right" @click="dockNext" title="下一页"></div> -->
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -289,32 +298,6 @@
|
|
|
</div>
|
|
|
</section>
|
|
|
</main>
|
|
|
-
|
|
|
- <!-- Bottom Nav (模块一级菜单,>6可滑动) -->
|
|
|
- <footer class="bottombar">
|
|
|
- <button class="arrow" v-if="modules.length > 6" @click="scrollModules(-1)" aria-label="left">‹</button>
|
|
|
-
|
|
|
- <div class="nav-viewport">
|
|
|
- <div class="nav-track" :style="navTrackStyle">
|
|
|
- <button
|
|
|
- v-for="m in modules"
|
|
|
- :key="m.key"
|
|
|
- class="nav-item"
|
|
|
- :class="{ active: activeModule === m.key }"
|
|
|
- @click="selectModule(m)"
|
|
|
- @mouseenter="hoverModule = m.key"
|
|
|
- @mouseleave="hoverModule = ''"
|
|
|
- >
|
|
|
- <div class="nav-ic" :class="{ glow: hoverModule === m.key || activeModule === m.key }">
|
|
|
- <span class="ic" :class="m.icon"></span>
|
|
|
- </div>
|
|
|
- <div class="nav-t">{{ m.title }}</div>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <button class="arrow" v-if="modules.length > 6" @click="scrollModules(1)" aria-label="right">›</button>
|
|
|
- </footer>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -322,9 +305,16 @@
|
|
|
import * as echarts from "echarts";
|
|
|
|
|
|
export default {
|
|
|
+ // eslint-disable-next-line vue/multi-word-component-names
|
|
|
name: "Home",
|
|
|
data() {
|
|
|
return {
|
|
|
+ baseW: 1920,
|
|
|
+ baseH: 1080,
|
|
|
+ scale: 1,
|
|
|
+ mouseX: null,
|
|
|
+ mouseY: null,
|
|
|
+ activeDockIndex: 0,
|
|
|
header: {
|
|
|
weatherText: "晴",
|
|
|
tempText: "32/17°C",
|
|
|
@@ -364,7 +354,16 @@ export default {
|
|
|
camera: { online: 298, offline: 12, total: 310, rate: 96 }
|
|
|
},
|
|
|
|
|
|
- deviceTab: "signal",
|
|
|
+
|
|
|
+ // 左侧第二块:控制信息(示例图的环形图)
|
|
|
+ controlLegend: [
|
|
|
+ { k: "cycle", t: "定周期控制", cls: "c4", v: 400 },
|
|
|
+ { k: "sense", t: "感应控制", cls: "c5", v: 50 },
|
|
|
+ { k: "coord", t: "干线协调", cls: "c2", v: 200 },
|
|
|
+ { k: "spec", t: "黄闪控制", cls: "c8", v: 5 }
|
|
|
+ ],
|
|
|
+ controlTotal: 650,
|
|
|
+deviceTab: "signal",
|
|
|
deviceTabs: [
|
|
|
{ key: "signal", label: "信号机" },
|
|
|
{ key: "detector", label: "检测器" },
|
|
|
@@ -380,18 +379,13 @@ export default {
|
|
|
alarms: [
|
|
|
{ id: "a1", level: 1, levelText: "一级", title: "通讯中断", loc: "中关村大街-科学院南路口-设备离线", time: "16:28:28" },
|
|
|
{ id: "a2", level: 2, levelText: "二级", title: "降级黄闪", loc: "中关村大街-科学院南路口-设备离线", time: "16:28:28" },
|
|
|
- { id: "a3", level: 3, levelText: "三级", title: "降级黄闪", loc: "中关村大街-科学院南路口-设备离线", time: "16:28:28" },
|
|
|
- { id: "a4", level: 4, levelText: "四级", title: "检测器报警", loc: "海淀路-彩和坊路口-检测器异常", time: "16:22:10" },
|
|
|
- { id: "a5", level: 2, levelText: "二级", title: "信号灯报警", loc: "知春路-学院路口-信号灯异常", time: "16:18:02" }
|
|
|
- ],
|
|
|
-
|
|
|
- duty: [
|
|
|
+ { id: "a3", level: 3, levelText: "三级", title: "信号灯报警", loc: "知春路-学院路口-信号灯异常", time: "16:18:02" }
|
|
|
+ ], duty: [
|
|
|
{ id: "d1", name: "测试", executor: "测试", level: 1, levelText: "一级", statusKey: "wait", status: "未开始" },
|
|
|
{ id: "d2", name: "张飞", executor: "张飞", level: 1, levelText: "一级", statusKey: "wait", status: "未开始" },
|
|
|
- { id: "d3", name: "关将", executor: "关将", level: 2, levelText: "二级", statusKey: "run", status: "进行中" },
|
|
|
+ { id: "d3", name: "关将", executor: "关将", level: 2, levelText: "二级", statusKey: "doing", status: "进行中" },
|
|
|
{ id: "d4", name: "刘备", executor: "刘备", level: 1, levelText: "一级", statusKey: "wait", status: "未开始" },
|
|
|
- { id: "d5", name: "孙权", executor: "孙权", level: 1, levelText: "一级", statusKey: "wait", status: "未开始" },
|
|
|
- { id: "d6", name: "赵云", executor: "赵云", level: 3, levelText: "三级", statusKey: "wait", status: "未开始" }
|
|
|
+ { id: "d5", name: "孙权", executor: "孙权", level: 1, levelText: "一级", statusKey: "wait", status: "未开始" }
|
|
|
],
|
|
|
dutyScrollOffset: 0,
|
|
|
activeDutyId: "",
|
|
|
@@ -403,20 +397,17 @@ export default {
|
|
|
],
|
|
|
|
|
|
modules: [
|
|
|
- { key: "home", title: "首页", icon: "ic-home", route: "/home" },
|
|
|
- { key: "status", title: "状态监控", icon: "ic-eye", route: "/status" },
|
|
|
- { key: "vip", title: "特勤安保", icon: "ic-shield", route: "/vip" },
|
|
|
- { key: "corr", title: "干线协调", icon: "ic-road", route: "/corridor" },
|
|
|
- { key: "overview", title: "状态展示", icon: "ic-chart", route: "/overview" },
|
|
|
- { key: "settings", title: "系统设置", icon: "ic-gear", route: "/settings" },
|
|
|
- { key: "fault", title: "故障报警", icon: "ic-bell", route: "/fault" },
|
|
|
- { key: "device", title: "设备状态", icon: "ic-chip", route: "/device" }
|
|
|
+ { key: "home", title: "首页", img: "main-home.png", route: { path: "/home" } },
|
|
|
+ { key: "watch", title: "状态监控", img: "main-watch.png", route: { path: "/home", query: { panel: "watch" } } },
|
|
|
+ { key: "vip", title: "特勤安保", img: "main-security.png", route: { path: "/home", query: { panel: "security" } } },
|
|
|
+ { key: "corr", title: "干线协调", img: "main-coor.png", route: { path: "/home", query: { panel: "coor" } } },
|
|
|
+ { key: "overview", title: "状态展示", img: "main-surve.png", route: { path: "/home", query: { panel: "surve" } } },
|
|
|
+ { key: "settings", title: "系统设置", img: "main-setting.png", route: { path: "/home", query: { panel: "setting" } } }
|
|
|
],
|
|
|
activeModule: "home",
|
|
|
hoverModule: "",
|
|
|
- navIndex: 0,
|
|
|
-
|
|
|
- onlineChart: null,
|
|
|
+onlineChart: null,
|
|
|
+ controlChart: null,
|
|
|
deviceChart: null,
|
|
|
|
|
|
timerClock: null,
|
|
|
@@ -439,11 +430,7 @@ export default {
|
|
|
);
|
|
|
return arr.slice(0, 4);
|
|
|
},
|
|
|
- navTrackStyle() {
|
|
|
- const step = 124;
|
|
|
- const x = -this.navIndex * step;
|
|
|
- return { transform: `translateX(${x}px)` };
|
|
|
- },
|
|
|
+
|
|
|
mapBgStyle() {
|
|
|
if (!this.mapBgUrl) return {};
|
|
|
return { backgroundImage: `url(${this.mapBgUrl})` };
|
|
|
@@ -454,6 +441,8 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
+ this.updateScale();
|
|
|
+ window.addEventListener("resize", this.updateScale, { passive: true });
|
|
|
this.startClock();
|
|
|
this.initCharts();
|
|
|
this.renderCharts();
|
|
|
@@ -476,16 +465,61 @@ export default {
|
|
|
window.addEventListener("resize", this.onResize, { passive: true });
|
|
|
},
|
|
|
beforeDestroy() {
|
|
|
+ window.removeEventListener("resize", this.updateScale);
|
|
|
window.removeEventListener("resize", this.onResize);
|
|
|
clearInterval(this.timerClock);
|
|
|
clearInterval(this.timerCycle);
|
|
|
clearInterval(this.timerDutyScroll);
|
|
|
this.onlineChart && this.onlineChart.dispose();
|
|
|
+ this.controlChart && this.controlChart.dispose();
|
|
|
this.deviceChart && this.deviceChart.dispose();
|
|
|
},
|
|
|
methods: {
|
|
|
+ onMouseMove(e) {
|
|
|
+ const rect = this.$el.getBoundingClientRect();
|
|
|
+ this.mouseX = e.clientX - rect.left;
|
|
|
+ this.mouseY = e.clientY - rect.top;
|
|
|
+ },
|
|
|
+ onMouseLeave() {
|
|
|
+ this.mouseX = null;
|
|
|
+ this.mouseY = null;
|
|
|
+ },
|
|
|
+ updateScale() {
|
|
|
+ const w = window.innerWidth || this.baseW;
|
|
|
+ const h = window.innerHeight || this.baseH;
|
|
|
+ this.scale = Math.min(w / this.baseW, h / this.baseH);
|
|
|
+ this.$el && this.$el.style.setProperty("--s", this.scale.toFixed(6));
|
|
|
+ },
|
|
|
+ onDockMove(e) {
|
|
|
+ const rect = this.$el.getBoundingClientRect();
|
|
|
+ this.mouseX = e.clientX - rect.left;
|
|
|
+ this.mouseY = e.clientY - rect.top;
|
|
|
+ },
|
|
|
+ onDockLeave() {
|
|
|
+ this.mouseX = null;
|
|
|
+ this.mouseY = null;
|
|
|
+ },
|
|
|
+ navItemStyle(idx) {
|
|
|
+ if (this.mouseX == null) return { transform: "translateZ(0) scale(1)" };
|
|
|
+ const dock = this.$el.querySelector(".dockbar");
|
|
|
+ const el = dock && dock.children && dock.children[idx];
|
|
|
+ if (!el) return { transform: "translateZ(0) scale(1)" };
|
|
|
+ const r = el.getBoundingClientRect();
|
|
|
+ const root = this.$el.getBoundingClientRect();
|
|
|
+ const cx = (r.left - root.left) + r.width / 2;
|
|
|
+ const cy = (r.top - root.top) + r.height / 2;
|
|
|
+ const dx = this.mouseX - cx;
|
|
|
+ const dy = this.mouseY - cy;
|
|
|
+ const dist = Math.sqrt(dx * dx + dy * dy);
|
|
|
+ const R = 220 * this.scale;
|
|
|
+ const t = Math.max(0, 1 - dist / R);
|
|
|
+ const s = 1 + 0.35 * (t * t);
|
|
|
+ return { transform: `translateZ(0) scale(${s.toFixed(4)})` };
|
|
|
+ },
|
|
|
+
|
|
|
onResize() {
|
|
|
this.onlineChart && this.onlineChart.resize();
|
|
|
+ this.controlChart && this.controlChart.resize();
|
|
|
this.deviceChart && this.deviceChart.resize();
|
|
|
},
|
|
|
startClock() {
|
|
|
@@ -503,11 +537,13 @@ export default {
|
|
|
|
|
|
initCharts() {
|
|
|
this.onlineChart = echarts.init(this.$refs.onlineChart);
|
|
|
+ this.controlChart = echarts.init(this.$refs.controlChart);
|
|
|
this.deviceChart = echarts.init(this.$refs.deviceChart);
|
|
|
},
|
|
|
|
|
|
renderCharts() {
|
|
|
this.renderOnlineChart();
|
|
|
+ this.renderControlChart();
|
|
|
this.renderDeviceChart();
|
|
|
},
|
|
|
|
|
|
@@ -556,6 +592,49 @@ export default {
|
|
|
});
|
|
|
},
|
|
|
|
|
|
+ renderControlChart() {
|
|
|
+ const items = this.controlLegend || [];
|
|
|
+ const total = this.controlTotal || items.reduce((s, x) => s + (x.v || 0), 0) || 0;
|
|
|
+ this.controlChart.setOption({
|
|
|
+ animation: true,
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: "pie",
|
|
|
+ radius: ["72%", "90%"],
|
|
|
+ center: ["50%", "50%"],
|
|
|
+ silent: true,
|
|
|
+ label: { show: false },
|
|
|
+ labelLine: { show: false },
|
|
|
+ data: items.map((x) => ({ value: x.v, name: x.t }))
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ graphic: [
|
|
|
+ {
|
|
|
+ type: "text",
|
|
|
+ left: "center",
|
|
|
+ top: "40%",
|
|
|
+ style: {
|
|
|
+ text: `${total}个`,
|
|
|
+ fill: "rgba(235,255,255,0.92)",
|
|
|
+ fontSize: 18,
|
|
|
+ fontWeight: 800
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "text",
|
|
|
+ left: "center",
|
|
|
+ top: "58%",
|
|
|
+ style: {
|
|
|
+ text: "控制信息",
|
|
|
+ fill: "rgba(190,225,255,0.70)",
|
|
|
+ fontSize: 12,
|
|
|
+ fontWeight: 600
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
renderDeviceChart() {
|
|
|
const v = this.deviceView;
|
|
|
this.deviceChart.setOption({
|
|
|
@@ -628,20 +707,37 @@ export default {
|
|
|
};
|
|
|
setTimeout(() => (this.mapPopup = null), 2200);
|
|
|
},
|
|
|
-
|
|
|
- scrollModules(dir) {
|
|
|
- const max = Math.max(0, this.modules.length - 6);
|
|
|
- this.navIndex = Math.max(0, Math.min(max, this.navIndex + dir));
|
|
|
+ assetUrl(file) {
|
|
|
+ try {
|
|
|
+ return require("@/assets/main/" + file);
|
|
|
+ } catch (e) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ },
|
|
|
+ navIconStyle(m) {
|
|
|
+ return { backgroundImage: `url(${this.assetUrl(m.img)})` };
|
|
|
},
|
|
|
selectModule(m) {
|
|
|
this.activeModule = m.key;
|
|
|
- // if (m.route) this.$router.push(m.route);
|
|
|
- }
|
|
|
+ if (m.route && this.$router) this.$router.push(m.route);
|
|
|
+ },
|
|
|
+ dockPrev() {
|
|
|
+ if (typeof this.activeDockIndex !== "number") this.activeDockIndex = 0;
|
|
|
+ const n = (this.dockItems && this.dockItems.length) ? this.dockItems.length : 6;
|
|
|
+ this.activeDockIndex = (this.activeDockIndex - 1 + n) % n;
|
|
|
+ },
|
|
|
+ dockNext() {
|
|
|
+ if (typeof this.activeDockIndex !== "number") this.activeDockIndex = 0;
|
|
|
+ const n = (this.dockItems && this.dockItems.length) ? this.dockItems.length : 6;
|
|
|
+ this.activeDockIndex = (this.activeDockIndex + 1) % n;
|
|
|
+ },
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
+
|
|
|
+
|
|
|
/* ====== Root ====== */
|
|
|
.page{
|
|
|
width:100vw;
|
|
|
@@ -649,69 +745,43 @@ export default {
|
|
|
overflow:hidden;
|
|
|
background: radial-gradient(1200px 700px at 50% 25%, rgba(40,120,255,0.22), rgba(5,12,30,1) 58%);
|
|
|
position:relative;
|
|
|
+ --s: 1;
|
|
|
font-family: "Microsoft YaHei", system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
|
|
|
+ display:flex;
|
|
|
+ flex-direction:column;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/* ====== Top ====== */
|
|
|
.topbar{
|
|
|
- height: clamp(72px, 9vh, 98px);
|
|
|
+ height: calc(var(--s) * 100px);
|
|
|
display:grid;
|
|
|
grid-template-columns: 1fr 1.6fr 1fr;
|
|
|
align-items:center;
|
|
|
- padding: 0 clamp(12px, 2vw, 26px);
|
|
|
+ padding: 0 calc(var(--s) * 26px);
|
|
|
position:relative;
|
|
|
+ background: center/100% 100% no-repeat;
|
|
|
+ background-image: url("~@/assets/main/main-header.png");
|
|
|
+ z-index: 10;
|
|
|
}
|
|
|
-.topbar::before{
|
|
|
- content:"";
|
|
|
- position:absolute;
|
|
|
- left:0; right:0; top:0; bottom:0;
|
|
|
- background:
|
|
|
- linear-gradient(180deg, rgba(10,30,80,0.65), rgba(10,30,80,0.15)),
|
|
|
- linear-gradient(90deg, rgba(0,220,255,0.12), rgba(0,0,0,0) 18%, rgba(0,0,0,0) 82%, rgba(0,220,255,0.12));
|
|
|
- border-bottom: 1px solid rgba(80,200,255,0.18);
|
|
|
- pointer-events:none;
|
|
|
-}
|
|
|
-.top-left, .top-right{ position:relative; z-index:1; }
|
|
|
-.weather{ display:flex; align-items:center; gap:10px; color: rgba(230,250,255,0.92); }
|
|
|
-.dot-sun{
|
|
|
- width:10px; height:10px; border-radius:999px;
|
|
|
- background: radial-gradient(circle at 30% 30%, rgba(255,255,180,1), rgba(255,200,60,0.8));
|
|
|
- box-shadow: 0 0 10px rgba(255,220,120,0.55);
|
|
|
- display:inline-block;
|
|
|
-}
|
|
|
-.w1{ font-weight:800; letter-spacing:0.5px; }
|
|
|
-.w2{ color: rgba(200,235,255,0.85); }
|
|
|
-
|
|
|
-.top-center{ position:relative; z-index:1; display:flex; justify-content:center; }
|
|
|
+.topbar::before{ display:none; }
|
|
|
.title-frame{
|
|
|
- position:relative;
|
|
|
- width: min(680px, 56vw);
|
|
|
- height: clamp(46px, 5.6vh, 64px);
|
|
|
+ width: min(62vw, calc(var(--s) * 980px));
|
|
|
+ height: calc(var(--s) * 64px);
|
|
|
+ border-radius: calc(var(--s) * 14px);
|
|
|
+ background: rgba(10,35,70,0.35);
|
|
|
+ box-shadow: inset 0 0 calc(var(--s) * 26px) rgba(0,160,255,0.18);
|
|
|
display:flex;
|
|
|
align-items:center;
|
|
|
justify-content:center;
|
|
|
- border-radius: 12px;
|
|
|
- background: linear-gradient(180deg, rgba(6,18,55,0.55), rgba(6,18,55,0.22));
|
|
|
- border: 1px solid rgba(80,200,255,0.22);
|
|
|
- box-shadow: 0 10px 34px rgba(0,0,0,0.28);
|
|
|
- overflow:hidden;
|
|
|
-}
|
|
|
-.title-frame::before{
|
|
|
- content:"";
|
|
|
- position:absolute;
|
|
|
- inset:-1px;
|
|
|
- background: conic-gradient(from 180deg, rgba(0,240,255,0.0), rgba(0,240,255,0.22), rgba(0,240,255,0.0));
|
|
|
- opacity:0.65;
|
|
|
- filter: blur(12px);
|
|
|
}
|
|
|
.title{
|
|
|
- position:relative;
|
|
|
- font-size: clamp(22px, 2.2vw, 36px);
|
|
|
- font-weight: 900;
|
|
|
- letter-spacing: 2px;
|
|
|
- color: rgba(235,255,255,0.96);
|
|
|
- text-shadow: 0 2px 14px rgba(0,220,255,0.25);
|
|
|
+ font-size: calc(var(--s) * 34px);
|
|
|
+ letter-spacing: calc(var(--s) * 2px);
|
|
|
+ text-shadow: 0 0 calc(var(--s) * 10px) rgba(90, 200, 255, 0.35);
|
|
|
+ font-weight: 600;
|
|
|
}
|
|
|
+
|
|
|
.clock{ text-align:right; color: rgba(235,255,255,0.92); }
|
|
|
.time{ font-weight: 900; font-size: clamp(18px, 1.6vw, 28px); letter-spacing: 1px; }
|
|
|
.date{ margin-top:4px; font-size: 12px; color: rgba(190,225,255,0.78); }
|
|
|
@@ -719,17 +789,23 @@ export default {
|
|
|
|
|
|
/* ====== Grid ====== */
|
|
|
.grid{
|
|
|
- height: calc(100vh - clamp(72px, 9vh, 98px) - clamp(88px, 11vh, 122px));
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
padding: clamp(10px, 1.6vw, 18px);
|
|
|
display:grid;
|
|
|
grid-template-columns: 1fr 2.1fr 1fr;
|
|
|
gap: clamp(10px, 1.2vw, 16px);
|
|
|
}
|
|
|
-.col{ min-width:0; display:flex; flex-direction:column; gap: clamp(10px, 1.2vw, 16px); }
|
|
|
+.col{ min-width:0; min-height:0; }
|
|
|
+.col.left, .col.right{ display:grid; grid-template-rows: 1fr 1fr 1fr; gap: clamp(10px, 1.2vw, 16px); }
|
|
|
+.col.mid{ display:flex; flex-direction:column; gap: clamp(10px, 1.2vw, 16px); }
|
|
|
.grow{ flex:1; min-height:0; overflow:hidden; }
|
|
|
|
|
|
/* ====== Cards ====== */
|
|
|
.card{
|
|
|
+ display:flex;
|
|
|
+ flex-direction:column;
|
|
|
+ min-height:0;
|
|
|
border-radius: 14px;
|
|
|
background: linear-gradient(180deg, rgba(8,22,60,0.58), rgba(8,22,60,0.28));
|
|
|
border: 1px solid rgba(80,200,255,0.18);
|
|
|
@@ -782,6 +858,8 @@ export default {
|
|
|
font-weight:700;
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
+.tabs.small{ margin-bottom: 8px; }
|
|
|
+.tabs.small .tab{ height: 26px; font-size: 12px; }
|
|
|
.tab.active{
|
|
|
background: rgba(0,180,255,0.18);
|
|
|
color: rgba(235,255,255,0.92);
|
|
|
@@ -791,6 +869,10 @@ export default {
|
|
|
/* Charts */
|
|
|
.row{ position:relative; z-index:1; display:grid; grid-template-columns: 140px 1fr; gap: 12px; align-items:center; }
|
|
|
.chart.donut{ width: 140px; height: 120px; }
|
|
|
+.chart.donut.small{ width: 120px; height: 108px; }
|
|
|
+.row.row-b{ grid-template-columns: 1fr 120px; }
|
|
|
+.legend.legend-b{ gap: 8px; }
|
|
|
+.legend.legend-b .lg{ grid-template-columns: 14px 1fr auto; }
|
|
|
.legend{ display:flex; flex-direction:column; gap: 8px; }
|
|
|
.lg{ display:flex; align-items:center; gap: 8px; color: rgba(200,235,255,0.78); font-size: 12px; }
|
|
|
.lg .num{ margin-left:auto; color: rgba(235,255,255,0.92); font-weight: 800; }
|
|
|
@@ -826,7 +908,11 @@ export default {
|
|
|
.pill.mute{ border-color: rgba(160,180,220,0.16); background: rgba(10,30,80,0.12); color: rgba(170,200,240,0.75); }
|
|
|
|
|
|
/* Alarms */
|
|
|
-.alarm-list{ position:relative; z-index:1; height: 100%; overflow:auto; padding-right: 6px; }
|
|
|
+.alarm-list{
|
|
|
+ flex:1;
|
|
|
+ min-height:0;
|
|
|
+ overflow:auto;
|
|
|
+ position:relative; z-index:1; height: 100%; overflow:hidden; padding-right: 6px; }
|
|
|
.alarm-item{
|
|
|
display:flex;
|
|
|
justify-content:space-between;
|
|
|
@@ -876,6 +962,7 @@ export default {
|
|
|
/* Map */
|
|
|
.map-wrap{ flex:1; min-height:0; }
|
|
|
.map-frame{
|
|
|
+ position: relative;
|
|
|
height:100%;
|
|
|
border-radius: 16px;
|
|
|
border: 1px solid rgba(80,200,255,0.18);
|
|
|
@@ -924,6 +1011,7 @@ export default {
|
|
|
.caret{ opacity:0.8; }
|
|
|
|
|
|
.map-canvas{
|
|
|
+ padding-bottom: calc(var(--s) * 120px);
|
|
|
position:absolute;
|
|
|
inset:0;
|
|
|
background-image:
|
|
|
@@ -969,7 +1057,7 @@ export default {
|
|
|
.legend-box{
|
|
|
position:absolute;
|
|
|
right: 14px;
|
|
|
- bottom: 16px;
|
|
|
+ bottom: calc(var(--s) * 150px);
|
|
|
width: 140px;
|
|
|
border-radius: 14px;
|
|
|
border: 1px solid rgba(80,200,255,0.16);
|
|
|
@@ -994,7 +1082,11 @@ export default {
|
|
|
.c11{ background: rgba(255,90,90,0.90); }
|
|
|
|
|
|
/* Tables */
|
|
|
-.tbl-wrap{ height:100%; overflow:hidden; }
|
|
|
+.tbl-wrap{
|
|
|
+ flex:1;
|
|
|
+ min-height:0;
|
|
|
+ overflow:auto;
|
|
|
+ height:100%; overflow:hidden; }
|
|
|
.tbl{
|
|
|
width:100%;
|
|
|
border-collapse: collapse;
|
|
|
@@ -1048,14 +1140,18 @@ export default {
|
|
|
|
|
|
/* ====== Bottom nav ====== */
|
|
|
.bottombar{
|
|
|
- position:absolute;
|
|
|
- left:0; right:0; bottom:0;
|
|
|
+ flex: 0 0 auto;
|
|
|
height: clamp(88px, 11vh, 122px);
|
|
|
display:flex;
|
|
|
align-items:center;
|
|
|
justify-content:center;
|
|
|
- gap: 10px;
|
|
|
- padding: 0 14px;
|
|
|
+ padding: 0 14px 10px;
|
|
|
+}
|
|
|
+.nav-row{
|
|
|
+ display:flex;
|
|
|
+ align-items:center;
|
|
|
+ justify-content:center;
|
|
|
+ gap: clamp(10px, 1vw, 16px);
|
|
|
}
|
|
|
.bottombar::before{
|
|
|
content:"";
|
|
|
@@ -1078,8 +1174,8 @@ export default {
|
|
|
will-change: transform;
|
|
|
}
|
|
|
.nav-item{
|
|
|
- width: 112px;
|
|
|
- height: 88px;
|
|
|
+ width: clamp(92px, 6.8vw, 124px);
|
|
|
+ height: clamp(72px, 9vh, 96px);
|
|
|
border-radius: 16px;
|
|
|
border: 1px solid rgba(80,200,255,0.16);
|
|
|
background: rgba(0,0,0,0.12);
|
|
|
@@ -1091,14 +1187,21 @@ export default {
|
|
|
gap: 8px;
|
|
|
color: rgba(200,235,255,0.76);
|
|
|
position:relative;
|
|
|
+ transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease;
|
|
|
}
|
|
|
.nav-item.active{
|
|
|
background: rgba(0,200,255,0.10);
|
|
|
color: rgba(235,255,255,0.92);
|
|
|
box-shadow: 0 0 20px rgba(0,200,255,0.14);
|
|
|
}
|
|
|
+.nav-item:hover{
|
|
|
+ transform: translateZ(0) scale(1.03);
|
|
|
+ border-color: rgba(0,220,255,0.26);
|
|
|
+ box-shadow: 0 0 20px rgba(0,220,255,0.10);
|
|
|
+}
|
|
|
.nav-ic{
|
|
|
- width: 44px; height: 44px;
|
|
|
+ width: clamp(58px, 4.8vw, 86px);
|
|
|
+ height: clamp(52px, 5.8vh, 78px);
|
|
|
border-radius: 14px;
|
|
|
display:flex;
|
|
|
align-items:center;
|
|
|
@@ -1106,6 +1209,11 @@ export default {
|
|
|
border: 1px solid rgba(80,200,255,0.18);
|
|
|
background: rgba(10,30,80,0.22);
|
|
|
}
|
|
|
+.nav-img{
|
|
|
+ width: 100%;
|
|
|
+ background: center/contain no-repeat;
|
|
|
+ filter: drop-shadow(0 0 10px rgba(80,200,255,0.35));
|
|
|
+}
|
|
|
.nav-ic.glow{
|
|
|
box-shadow: 0 0 18px rgba(0,220,255,0.22);
|
|
|
background: rgba(0,200,255,0.10);
|
|
|
@@ -1149,4 +1257,142 @@ export default {
|
|
|
.nav-viewport{ width: 94vw; }
|
|
|
.map-frame{ min-height: 42vh; }
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+/* ====== Chart layout align with UI sample ====== */
|
|
|
+.card-a .row{ flex-direction: row-reverse; gap: calc(var(--s) * 14px); }
|
|
|
+.card-a .donut{ width: calc(var(--s) * 160px); height: calc(var(--s) * 160px); }
|
|
|
+.card-a .legend{ flex: 1; padding-right: calc(var(--s) * 6px); }
|
|
|
+
|
|
|
+.card-b .row-b{ flex-direction: row; gap: calc(var(--s) * 14px); }
|
|
|
+.card-b .donut.small{ width: calc(var(--s) * 150px); height: calc(var(--s) * 150px); }
|
|
|
+
|
|
|
+.card-r1 .row{ flex-direction: row-reverse; gap: calc(var(--s) * 14px); }
|
|
|
+.card-r1 .donut{ width: calc(var(--s) * 160px); height: calc(var(--s) * 160px); }
|
|
|
+
|
|
|
+
|
|
|
+/* ====== Bottom Dock (overlay on map) ====== */
|
|
|
+.dock-wrap{
|
|
|
+ position:absolute;
|
|
|
+ left:50%;
|
|
|
+ bottom: calc(var(--s) * 14px);
|
|
|
+ transform: translateX(-50%);
|
|
|
+ width: min(86%, calc(var(--s) * 980px)); /* 基本与地图主体宽度一致 */
|
|
|
+ display:flex;
|
|
|
+ justify-content:center;
|
|
|
+ pointer-events:auto;
|
|
|
+ z-index: 9;
|
|
|
+}
|
|
|
+.dockbar{
|
|
|
+ width: 100%;
|
|
|
+ display:flex;
|
|
|
+ align-items:flex-end;
|
|
|
+ justify-content:space-between;
|
|
|
+ gap: calc(var(--s) * 22px);
|
|
|
+ padding: calc(var(--s) * 6px) calc(var(--s) * 10px);
|
|
|
+}
|
|
|
+.dock-item{
|
|
|
+ width: calc(var(--s) * 98px);
|
|
|
+ height: calc(var(--s) * 124px);
|
|
|
+ display:flex;
|
|
|
+ flex-direction:column;
|
|
|
+ align-items:center;
|
|
|
+ justify-content:flex-start;
|
|
|
+ cursor:pointer;
|
|
|
+ transform-origin: 50% 80%;
|
|
|
+ transition: transform .08s linear;
|
|
|
+}
|
|
|
+.dock-icon{
|
|
|
+ width: calc(var(--s) * 98px);
|
|
|
+ height: calc(var(--s) * 86px);
|
|
|
+ background: center/contain no-repeat;
|
|
|
+ filter: drop-shadow(0 0 calc(var(--s) * 12px) rgba(70, 190, 255, .35));
|
|
|
+}
|
|
|
+.dock-label{
|
|
|
+ margin-top: calc(var(--s) * 10px);
|
|
|
+ font-size: calc(var(--s) * 14px);
|
|
|
+ line-height: 1;
|
|
|
+ opacity: .92;
|
|
|
+ text-shadow: 0 0 calc(var(--s) * 8px) rgba(0, 160, 255, .25);
|
|
|
+
|
|
|
+ color: rgba(235,255,255,0.92);
|
|
|
+ text-shadow: 0 0 calc(var(--s) * 10px) rgba(0,160,255,.35);
|
|
|
+}
|
|
|
+.dock-item:hover .dock-icon{
|
|
|
+ filter: drop-shadow(0 0 calc(var(--s) * 18px) rgba(120, 230, 255, .65));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* ====== Patch: header corner info (match 示例图) ====== */
|
|
|
+.top-left, .top-right{
|
|
|
+ align-self: start;
|
|
|
+ padding-top: calc(var(--s) * 60px);
|
|
|
+}
|
|
|
+.weather{
|
|
|
+ display:flex;
|
|
|
+ align-items:center;
|
|
|
+ gap: calc(var(--s) * 10px);
|
|
|
+ color: rgba(235,255,255,0.92);
|
|
|
+}
|
|
|
+.dot-sun{
|
|
|
+ width: calc(var(--s) * 8px);
|
|
|
+ height: calc(var(--s) * 8px);
|
|
|
+ border-radius: 50%;
|
|
|
+ background: rgba(255,220,120,0.95);
|
|
|
+ box-shadow: 0 0 calc(var(--s) * 10px) rgba(255,220,120,0.55);
|
|
|
+ display:inline-block;
|
|
|
+}
|
|
|
+.w1{ font-size: calc(var(--s) * 18px); font-weight: 700; letter-spacing: 1px; }
|
|
|
+.w2{ font-size: calc(var(--s) * 16px); opacity: .9; }
|
|
|
+
|
|
|
+.clock{ text-align:right; color: rgba(235,255,255,0.92); }
|
|
|
+.time{
|
|
|
+ font-weight: 900;
|
|
|
+ font-size: calc(var(--s) * 28px);
|
|
|
+ line-height: 1;
|
|
|
+ letter-spacing: 1px;
|
|
|
+ text-shadow: 0 0 calc(var(--s) * 10px) rgba(90, 200, 255, 0.28);
|
|
|
+}
|
|
|
+.date{
|
|
|
+ margin-top: calc(var(--s) * 6px);
|
|
|
+ font-size: calc(var(--s) * 12px);
|
|
|
+ color: rgba(190,225,255,0.78);
|
|
|
+}
|
|
|
+
|
|
|
+/* ====== Patch: side cards sizing (avoid squashed/hidden) ====== */
|
|
|
+.left .card-a, .right .card-a{ flex: 0 0 calc(var(--s) * 220px); }
|
|
|
+.left .card-b{ flex: 0 0 calc(var(--s) * 240px); }
|
|
|
+.right .card-b{ flex: 0 0 calc(var(--s) * 230px); }
|
|
|
+.left .grow, .right .grow{ flex: 1 1 auto; min-height: calc(var(--s) * 230px); }
|
|
|
+
|
|
|
+/* ====== Patch: dock arrows + label color (match 示例图) ====== */
|
|
|
+.dock-wrap{
|
|
|
+ display:flex;
|
|
|
+ align-items:center;
|
|
|
+ justify-content:center;
|
|
|
+ gap: calc(var(--s) * 22px);
|
|
|
+}
|
|
|
+.dock-arrow{
|
|
|
+ width: calc(var(--s) * 54px);
|
|
|
+ height: calc(var(--s) * 54px);
|
|
|
+ background: center/contain no-repeat;
|
|
|
+ opacity: .92;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: transform .18s ease, opacity .18s ease, filter .18s ease;
|
|
|
+ filter: drop-shadow(0 0 calc(var(--s) * 10px) rgba(60, 180, 255, .35));
|
|
|
+}
|
|
|
+.dock-arrow:hover{
|
|
|
+ opacity: 1;
|
|
|
+ transform: translateZ(0) scale(1.08);
|
|
|
+ filter: drop-shadow(0 0 calc(var(--s) * 14px) rgba(90, 210, 255, .6));
|
|
|
+}
|
|
|
+.dock-arrow.left{ background-image: url("~@/assets/main/main-left.png"); }
|
|
|
+.dock-arrow.right{ background-image: url("~@/assets/main/main-right.png"); }
|
|
|
+
|
|
|
+.dock-label{
|
|
|
+ color: rgba(235,255,255,0.92) !important;
|
|
|
+ text-shadow: 0 0 calc(var(--s) * 8px) rgba(0, 160, 255, 0.28);
|
|
|
+}
|
|
|
</style>
|