From 6ff794533630661c271545d79e7d6962ce20e967 Mon Sep 17 00:00:00 2001 From: zhanghao <774378400@qq.com> Date: Tue, 21 Jul 2026 14:00:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=E9=A2=9C=E8=89=B2?= =?UTF-8?q?=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/IPlayer/config.js | 8 +- src/components/IPlayer/index.vue | 1 + src/main.js | 2 +- src/style.css | 9 --- src/style.scss | 39 ++++++++++ src/styles/vars.scss | 128 ++++++++++++++++++++++--------- src/utils/themeColors.js | 2 + src/views/Address.vue | 18 ++--- src/views/Home.vue | 39 ++++++---- src/views/arm/MechanicalArm.vue | 77 +++++++++---------- src/views/arm/UrdfView.vue | 7 +- src/views/camera/index.vue | 3 +- src/views/chassis/Chassis.vue | 128 ++++++++++++++++--------------- src/views/chassis/MapCanvas.vue | 20 ++--- src/views/chassis/canvasUtils.js | 53 ++++++------- 15 files changed, 319 insertions(+), 215 deletions(-) delete mode 100644 src/style.css create mode 100644 src/style.scss create mode 100644 src/utils/themeColors.js diff --git a/src/components/IPlayer/config.js b/src/components/IPlayer/config.js index 3048428..a02d3d9 100644 --- a/src/components/IPlayer/config.js +++ b/src/components/IPlayer/config.js @@ -24,7 +24,7 @@ export const conf = { text: '进度条信息提示...', // hover的时候展示文案,可以为空 duration: 1, // 展示时间跨度,单位为s style: { // 指定样式 - backgroundColor: 'white' + backgroundColor: 'var(--color-player-marker)' } }, ], @@ -41,9 +41,9 @@ export const conf = { i18n: [], // 使用默认的中文 // 自定义一些颜色 commonStyle: { - progressColor: 'green', // 整个进度条颜色 - playedColor: 'chocolate', // 已播放的进度条颜色 - volumeColor: 'pink', // 音量大小竖向滑块颜色 + progressColor: 'var(--color-player-progress)', // 整个进度条颜色 + playedColor: 'var(--color-player-played-progress)', // 已播放的进度条颜色 + volumeColor: 'var(--color-player-volume)', // 音量大小竖向滑块颜色 }, controls: true, // 是否使用底部控制栏,默认使用 miniprogress: true, // 是否使用mini进度条(当底部控制栏隐藏时生效) diff --git a/src/components/IPlayer/index.vue b/src/components/IPlayer/index.vue index 8b14a05..6237a2a 100644 --- a/src/components/IPlayer/index.vue +++ b/src/components/IPlayer/index.vue @@ -53,6 +53,7 @@ const init = () => { .player_container { width: 100%; height: 100%; + background: $color-background-base; .xgplayer { height: 100% !important; diff --git a/src/main.js b/src/main.js index 1c845fc..d2cce40 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,5 @@ import { createApp } from 'vue' -import './style.css' +import './style.scss' import App from './App.vue' import router from '@/router' import { createPinia } from 'pinia' diff --git a/src/style.css b/src/style.css deleted file mode 100644 index 84a4633..0000000 --- a/src/style.css +++ /dev/null @@ -1,9 +0,0 @@ -* { - padding: 0; - margin: 0; -} - -html,body, #app { - width: 100%; - height: 100%; -} \ No newline at end of file diff --git a/src/style.scss b/src/style.scss new file mode 100644 index 0000000..f6812c6 --- /dev/null +++ b/src/style.scss @@ -0,0 +1,39 @@ +:root { + --color-background-base: #{$color-background-base}; + --color-connection-card: #{$color-connection-card}; + --color-canvas-grid-line: #{$color-canvas-grid-line}; + --color-canvas-area-fill: #{$color-canvas-area-fill}; + --color-canvas-area-border: #{$color-canvas-area-border}; + --color-canvas-line-default: #{$color-canvas-line-default}; + --color-canvas-line-forbidden: #{$color-canvas-line-forbidden}; + --color-canvas-line-virtual: #{$color-canvas-line-virtual}; + --color-canvas-route: #{$color-canvas-route}; + --color-canvas-route-arrow: #{$color-canvas-route-arrow}; + --color-canvas-curve: #{$color-canvas-curve}; + --color-canvas-curve-arrow: #{$color-canvas-curve-arrow}; + --color-canvas-curve-marker: #{$color-canvas-curve-marker}; + --color-canvas-landmark: #{$color-canvas-landmark}; + --color-canvas-charging-point: #{$color-canvas-charging-point}; + --color-canvas-loading-point: #{$color-canvas-loading-point}; + --color-canvas-workstation: #{$color-canvas-workstation}; + --color-canvas-tooltip-title: #{$color-canvas-tooltip-title}; + --color-canvas-boundary: #{$color-canvas-boundary}; + --color-canvas-robot: #{$color-canvas-robot}; + --color-canvas-robot-label: #{$color-canvas-robot-label}; + --color-player-marker: #{$color-player-marker}; + --color-player-progress: #{$color-player-progress}; + --color-player-played-progress: #{$color-player-played-progress}; + --color-player-volume: #{$color-player-volume}; +} + +* { + padding: 0; + margin: 0; +} + +html, +body, +#app { + width: 100%; + height: 100%; +} diff --git a/src/styles/vars.scss b/src/styles/vars.scss index f38d2a7..d91f4c7 100644 --- a/src/styles/vars.scss +++ b/src/styles/vars.scss @@ -1,42 +1,96 @@ -// 全局自定义变量 -$primary: #409eff; +// Dark application theme +$color-background-base: #070c11; +$color-background-panel: #0b1218; +$color-background-card: #101a21; +$color-background-card-hover: #16252d; +$color-background-header-translucent: rgba(11, 18, 24, 0.92); +$color-background-tooltip-translucent: rgba(11, 18, 24, 0.96); -$text: #c0cfdf; -$text-dim: #475a72; -$text-bright:#e8f0f8; -$text-gray: #666; +$color-text-primary: #c8d9e2; +$color-text-secondary: #86a7b5; +$color-text-bright: #edf9fc; +$color-text-accent-soft: #b4eaf4; +$color-text-accent-bright: #d2f4fa; +$color-text-navigation: #a8cbd5; +$color-text-navigation-active: #effcff; +$color-text-tooltip: #9db9c3; -$bg-deep: #060a12; -$bg-body: #0a0f1a; -$bg-card: #0f1628; -$bg-card-hi: #141d32; -$border: rgba(0,195,255,0.08); -$border-hi: rgba(0,195,255,0.22); -$accent: #00c3ff; -$accent-dim: rgba(0,195,255,0.14); -$danger: #ff2d55; -$success: #00e676; +$color-accent-primary: #66d9f7; +$color-accent-secondary: #8ee7f9; +$color-accent-border: rgba(102, 217, 247, 0.14); +$color-accent-border-strong: rgba(102, 217, 247, 0.34); +$color-accent-glow: rgba(102, 217, 247, 0.18); +$color-accent-subtle: rgba(102, 217, 247, 0.07); +$color-accent-soft: rgba(102, 217, 247, 0.10); +$color-accent-medium: rgba(102, 217, 247, 0.12); +$color-accent-selected: rgba(102, 217, 247, 0.16); +$color-accent-border-muted: rgba(102, 217, 247, 0.20); +$color-accent-border-header: rgba(102, 217, 247, 0.30); +$color-accent-border-hover: rgba(102, 217, 247, 0.42); +$color-accent-border-active: rgba(142, 231, 249, 0.72); +$color-accent-text-muted: rgba(142, 231, 249, 0.42); +$color-accent-radial-glow: rgba(102, 217, 247, 0.09); +$color-accent-inset-glow: rgba(102, 217, 247, 0.06); +$color-accent-tag-background: rgba(102, 217, 247, 0.08); +$color-accent-core-background: rgba(102, 217, 247, 0.03); +$color-accent-slider-glow: rgba(102, 217, 247, 0.35); -$font-h: 'Rajdhani', sans-serif; -$font-b: 'Noto Sans SC', 'PingFang SC', sans-serif; -$font-m: 'JetBrains Mono', monospace; -$radius: 10px; -$topbar-bg: #1e2a3a; -$topbar-text: #a8b2c7; -$transition: 0.25s ease; -$shadow: 0 2px 12px rgba(0, 0, 0, 0.06); -$text-secondary: #606266; -$border-light: #f0f2f5; -$text-primary: #2c3e50; -$text-muted: #909399; -$radius-sm: 6px; -$primary-dark: #337ecc; -$border-color: #e4e7ed; +$color-danger: #ff2d55; +$color-danger-background: rgba(255, 45, 85, 0.06); +$color-danger-background-hover: rgba(255, 45, 85, 0.14); +$color-success: #00e676; +$color-axis-x: #f87171; +$color-axis-x-background: rgba(248, 113, 113, 0.15); +$color-axis-x-border: rgba(248, 113, 113, 0.30); +$color-axis-y: #34d399; +$color-axis-y-background: rgba(52, 211, 153, 0.15); +$color-axis-y-border: rgba(52, 211, 153, 0.30); +$color-axis-z: #38bdf8; +$color-axis-z-background: rgba(56, 189, 248, 0.15); +$color-axis-z-border: rgba(56, 189, 248, 0.30); +$color-white-subtle: rgba(255, 255, 255, 0.02); +$color-white-muted: rgba(255, 255, 255, 0.05); +$color-shadow-soft: rgba(0, 0, 0, 0.06); +$color-shadow-header: rgba(0, 0, 0, 0.22); +$color-shadow-tooltip: rgba(0, 0, 0, 0.50); -// 自定义混合 -@mixin flex-center { - display: flex; - align-items: center; - justify-content: center; -} \ No newline at end of file +// Connection page +$color-connection-card: #ffffff; +$color-connection-title: #000000; +$color-connection-text-muted: #8f8f8f; +$color-connection-primary: #003af7; +$color-connection-tip-background: #b7c5f3; + +// Canvas and 3D visualization +$color-canvas-grid-line: rgba(30, 50, 80, 0.25); +$color-canvas-area-fill: rgba(224, 64, 251, 0.12); +$color-canvas-area-border: rgba(224, 64, 251, 0.50); +$color-canvas-line-default: #ff6e40; +$color-canvas-line-forbidden: #ff1744; +$color-canvas-line-virtual: #ffab40; +$color-canvas-route: #00e5ff; +$color-canvas-route-arrow: rgba(0, 229, 255, 0.60); +$color-canvas-curve: #76ff03; +$color-canvas-curve-arrow: rgba(118, 255, 3, 0.70); +$color-canvas-curve-marker: rgba(118, 255, 3, 0.25); +$color-canvas-landmark: #ffd600; +$color-canvas-charging-point: #22c55e; +$color-canvas-loading-point: #3b82f6; +$color-canvas-workstation: #a855f7; +$color-canvas-tooltip-title: #00e5a0; +$color-canvas-boundary: rgba(0, 229, 160, 0.10); +$color-canvas-robot: #00d4ff; +$color-canvas-robot-label: #a0c0f0; + +// Video player controls +$color-player-marker: #ffffff; +$color-player-progress: #008000; +$color-player-played-progress: #d2691e; +$color-player-volume: #ffc0cb; + +$font-family-heading: 'Rajdhani', sans-serif; +$font-family-body: 'Noto Sans SC', 'PingFang SC', sans-serif; +$font-family-monospace: 'JetBrains Mono', monospace; + +$shadow-input: 0 2px 12px $color-shadow-soft; diff --git a/src/utils/themeColors.js b/src/utils/themeColors.js new file mode 100644 index 0000000..d614af7 --- /dev/null +++ b/src/utils/themeColors.js @@ -0,0 +1,2 @@ +export const getThemeColor = (propertyName) => + getComputedStyle(document.documentElement).getPropertyValue(propertyName).trim() diff --git a/src/views/Address.vue b/src/views/Address.vue index d7bd978..fb62453 100644 --- a/src/views/Address.vue +++ b/src/views/Address.vue @@ -99,20 +99,20 @@ const testConnect = async () => { .card { padding: 40px; box-sizing: border-box; - background: #fff; + background: $color-connection-card; width: 600px; border-radius: 20px; .title { font-size: 40px; font-weight: bold; - color: #000; + color: $color-connection-title; margin-bottom: 12px; } .secondaryTitle { font-size: 20px; - color: #8f8f8f; + color: $color-connection-text-muted; margin-bottom: 8px; } @@ -120,8 +120,8 @@ const testConnect = async () => { display: inline-block; font-size: 14px; padding: 4px 16px; - color: #003af7; - background: #b7c5f3; + color: $color-connection-primary; + background: $color-connection-tip-background; border-radius: 12px; margin-bottom: 12px; } @@ -135,8 +135,8 @@ const testConnect = async () => { height: 50px; display: inline-block; padding: 4px 20px; - color: #fff; - background: #003af7; + color: $color-connection-card; + background: $color-connection-primary; border-radius: 12px; margin-top: 20px; } @@ -144,9 +144,9 @@ const testConnect = async () => { .error-tips { text-align: center; font-size: 14px; - color: #8f8f8f; + color: $color-connection-text-muted; margin-top: 10px; } } } - \ No newline at end of file + diff --git a/src/views/Home.vue b/src/views/Home.vue index d4e3d98..01c2fc0 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -96,14 +96,18 @@ onUnmounted(() => { .common-layout { width: 100%; height: 100%; + background: $color-background-base; + color: $color-text-primary; .el-container { width: 100%; height: 100%; - background-color: #000d2e; + background: radial-gradient(circle at 50% -20%, $color-accent-radial-glow, transparent 38%), $color-background-base; .el-header { - border-bottom: 1px solid #00c2ff4d; + background: $color-background-header-translucent; + border-bottom: 1px solid $color-accent-border-header; + box-shadow: 0 6px 24px $color-shadow-header; } .header-container { @@ -116,7 +120,7 @@ onUnmounted(() => { display: flex; align-items: center; justify-content: center; - color: #00c2ff; + color: $color-accent-secondary; cursor: pointer; } @@ -130,7 +134,7 @@ onUnmounted(() => { align-items: center; justify-content: center; gap: 20px; - color: #00c2ff; + color: $color-text-accent-soft; font-size: 16px; font-weight: 600; letter-spacing: 2px; @@ -149,7 +153,8 @@ onUnmounted(() => { font-size: 26px; font-weight: 700; text-align: center; - color: #00c2ff; + color: $color-text-accent-bright; + text-shadow: 0 0 18px $color-accent-border-strong; letter-spacing: 4px; white-space: nowrap; } @@ -166,22 +171,28 @@ onUnmounted(() => { gap: 12px ; .nav-btn { - background: #003c8c80; - border: 1px solid rgba(0, 194, 255, 0.35); - color: #a0d8f0; + background: $color-accent-subtle; + border: 1px solid $color-accent-border-muted; + color: $color-text-navigation; padding: 6px 20px; border-radius: 3px; font-size: 14px; cursor: pointer; transition: all 0.2s; white-space: nowrap; + + &:hover { + border-color: $color-accent-border-hover; + color: $color-text-accent-bright; + background: $color-accent-medium; + } } .active { - background: rgba(0, 120, 220, 0.7); - border-color: #00c2ff; - color: #fff; - box-shadow: 0 0 10px rgba(0, 194, 255, 0.5); + background: $color-accent-selected; + border-color: $color-accent-border-active; + color: $color-text-navigation-active; + box-shadow: 0 0 14px $color-accent-glow, inset 0 0 12px $color-accent-inset-glow; } } } @@ -192,7 +203,7 @@ onUnmounted(() => { justify-content: center; font-family: 'Orbitron', monospace; font-size: 14px; - color: #00c2ff4d; + color: $color-accent-text-muted; letter-spacing: 2px; white-space: nowrap; overflow: hidden; @@ -205,4 +216,4 @@ onUnmounted(() => { } } - \ No newline at end of file + diff --git a/src/views/arm/MechanicalArm.vue b/src/views/arm/MechanicalArm.vue index 09580b9..e0cd33d 100644 --- a/src/views/arm/MechanicalArm.vue +++ b/src/views/arm/MechanicalArm.vue @@ -437,9 +437,9 @@ onUnmounted(() => { \ No newline at end of file + diff --git a/src/views/arm/UrdfView.vue b/src/views/arm/UrdfView.vue index 8513698..ddfca72 100644 --- a/src/views/arm/UrdfView.vue +++ b/src/views/arm/UrdfView.vue @@ -14,6 +14,7 @@ import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js'; import { XacroAdapter } from '@/adapters/XacroAdapter.js' import { fileNameMap } from './config' +import { getThemeColor } from '@/utils/themeColors.js' const props = defineProps({ filePath: { @@ -40,7 +41,7 @@ const initScene = () => { const height = container.clientHeight || 600; scene = new THREE.Scene(); - scene.background = new THREE.Color('#060a12'); + scene.background = new THREE.Color(getThemeColor('--color-background-base')); camera = new THREE.PerspectiveCamera(35, width / height, 0.1, 1000); camera.position.set(0, 0, 4); @@ -210,7 +211,7 @@ defineExpose({ updateJoint, resetJoints, jointControls }); height: 100%; position: relative; overflow: hidden; - background: #0d0d1a; + background: $color-background-base; .viewer-container { width: 100%; @@ -227,4 +228,4 @@ defineExpose({ updateJoint, resetJoints, jointControls }); - \ No newline at end of file + diff --git a/src/views/camera/index.vue b/src/views/camera/index.vue index f8388d5..8dffc60 100644 --- a/src/views/camera/index.vue +++ b/src/views/camera/index.vue @@ -10,5 +10,6 @@ import IPlayer from '@/components/IPlayer/index.vue' .page-container { width: 100%; height: 100%; + background: $color-background-base; } - \ No newline at end of file + diff --git a/src/views/chassis/Chassis.vue b/src/views/chassis/Chassis.vue index 7f1a331..74bbc96 100644 --- a/src/views/chassis/Chassis.vue +++ b/src/views/chassis/Chassis.vue @@ -13,14 +13,14 @@
- +
- +
- +
@@ -207,9 +207,9 @@ function onRelease() { display: flex; flex-direction: column; height: 100%; - background: #030729; - color: $text; - font-family: $font-b; + background: $color-background-base; + color: $color-text-primary; + font-family: $font-family-body; overflow: hidden; user-select: none; @@ -224,7 +224,7 @@ function onRelease() { display: flex; flex-direction: column; min-width: 0; - border-right: 1px solid $border; + border-right: 1px solid $color-accent-border; .area-bar { display: flex; @@ -232,19 +232,19 @@ function onRelease() { justify-content: space-between; height: 40px; padding: 0 16px; - background: #030729; - border-bottom: 1px solid $border; + background: $color-background-panel; + border-bottom: 1px solid $color-accent-border; flex-shrink: 0; &__title { display: flex; align-items: center; gap: 8px; - font-family: $font-h; + font-family: $font-family-heading; font-size: 13px; font-weight: 700; letter-spacing: 1.5px; - color: $text-dim; + color: $color-text-secondary; text-transform: uppercase; } @@ -267,14 +267,14 @@ function onRelease() { width: 32px; height: 32px; border-radius: 8px; - background: #1E3A5F; + background: $color-accent-soft; display: flex; align-items: center; justify-content: center; border: 1px solid transparent; &:hover { - border-color: #00D4FF; + border-color: $color-accent-primary; cursor: pointer; } } @@ -282,13 +282,13 @@ function onRelease() { :deep(.el-button) { font-size: 12px; - color: $text-dim; - border-color: $border; + color: $color-text-secondary; + border-color: $color-accent-border; background: transparent; &:hover { - color: $accent; - border-color: $border-hi; + color: $color-accent-primary; + border-color: $color-accent-border-strong; } } } @@ -297,7 +297,7 @@ function onRelease() { flex: 1; position: relative; overflow: hidden; - background: $bg-deep; + background: $color-background-base; // canvas { // position: absolute; @@ -313,11 +313,11 @@ function onRelease() { flex-direction: column; gap: 0; overflow-y: auto; - background: $bg-body; + background: $color-background-panel; .card { padding: 18px 20px; - border-bottom: 1px solid $border; + border-bottom: 1px solid $color-accent-border; &__head { display: flex; @@ -326,11 +326,11 @@ function onRelease() { margin-bottom: 16px; h3 { - font-family: $font-h; + font-family: $font-family-heading; font-size: 14px; font-weight: 700; letter-spacing: 1.2px; - color: $text-dim; + color: $color-text-secondary; margin: 0; text-transform: uppercase; } @@ -339,9 +339,9 @@ function onRelease() { :deep(.el-tag) { font-size: 10px; height: 20px; - background: rgba(0,195,255,0.08); - border-color: $border; - color: $accent; + background: $color-accent-tag-background; + border-color: $color-accent-border; + color: $color-accent-primary; } .dpad { @@ -358,10 +358,10 @@ function onRelease() { align-items: center; justify-content: center; gap: 4px; - border: 1px solid $border; + border: 1px solid $color-accent-border; border-radius: 8px; - background: $bg-card; - color: $text; + background: $color-background-card; + color: $color-text-primary; cursor: pointer; transition: all .15s ease; @@ -373,24 +373,24 @@ function onRelease() { small { font-size: 10px; letter-spacing: .5px; - color: $text-dim; + color: $color-text-secondary; } &:hover { - border-color: $border-hi; - background: $bg-card-hi; - color: $accent; + border-color: $color-accent-border-strong; + background: $color-background-card-hover; + color: $color-accent-primary; } &:active { - background: $accent; - color: $bg-deep; - border-color: $accent; - box-shadow: 0 0 24px $accent-dim; + background: $color-accent-primary; + color: $color-background-base; + border-color: $color-accent-primary; + box-shadow: 0 0 24px $color-accent-glow; transform: scale(.94); small { - color: $bg-deep; + color: $color-background-base; } } @@ -421,15 +421,15 @@ function onRelease() { display: flex; align-items: center; justify-content: center; - border: 1px solid $border; + border: 1px solid $color-accent-border; border-radius: 8px; - background: rgba(0,195,255,0.03); + background: $color-accent-core-background; span { width: 10px; height: 10px; border-radius: 50%; - background: $accent; + background: $color-accent-primary; opacity: .3; } } @@ -441,12 +441,12 @@ function onRelease() { font-size: 14px; font-weight: 600; letter-spacing: 1px; - border-color: $danger !important; - color: $danger !important; - background: rgba(255,45,85,0.06) !important; + border-color: $color-danger !important; + color: $color-danger !important; + background: $color-danger-background !important; &:hover { - background: rgba(255,45,85,0.14) !important; + background: $color-danger-background-hover !important; } } @@ -463,25 +463,25 @@ function onRelease() { align-items: baseline; margin-bottom: 8px; font-size: 13px; - color: $text-dim; + color: $color-text-secondary; } &__val { - font-family: $font-m; + font-family: $font-family-monospace; font-size: 13px; font-weight: 500; - color: $accent; + color: $color-accent-primary; } :deep(.el-slider) { .el-slider__runway { height: 4px; - background: rgba(255,255,255,0.05); + background: $color-white-muted; border-radius: 2px; } .el-slider__bar { height: 4px; - background: $accent; + background: $color-accent-primary; border-radius: 2px; } .el-slider__button-wrapper { @@ -490,13 +490,13 @@ function onRelease() { .el-slider__button { width: 14px; height: 14px; - border: 2px solid $accent; - background: $bg-deep; - box-shadow: 0 0 8px $accent-dim; + border: 2px solid $color-accent-primary; + background: $color-background-base; + box-shadow: 0 0 8px $color-accent-glow; transition: box-shadow .2s; &:hover { - box-shadow: 0 0 14px rgba(0,195,255,0.35); + box-shadow: 0 0 14px $color-accent-slider-glow; } } } @@ -511,8 +511,8 @@ function onRelease() { gap: 16px; height: 38px; padding: 0 24px; - background: $bg-card; - border-top: 1px solid $border; + background: $color-background-card; + border-top: 1px solid $color-accent-border; flex-shrink: 0; .chip { @@ -521,27 +521,31 @@ function onRelease() { gap: 8px; padding: 3px 12px; border-radius: 4px; - background: rgba(255,255,255,0.02); - border: 1px solid $border; + background: $color-white-subtle; + border: 1px solid $color-accent-border; &__label { font-size: 10px; - color: $text-dim; + color: $color-text-secondary; letter-spacing: .5px; } &__val { - font-family: $font-m; + font-family: $font-family-monospace; font-size: 11px; - color: $text-bright; + color: $color-text-bright; font-weight: 500; &--ok { - color: $success; + color: $color-success; } } } } } - \ No newline at end of file +.map-action-icon { + color: $color-canvas-robot; +} + + diff --git a/src/views/chassis/MapCanvas.vue b/src/views/chassis/MapCanvas.vue index b65977b..1c97983 100644 --- a/src/views/chassis/MapCanvas.vue +++ b/src/views/chassis/MapCanvas.vue @@ -28,6 +28,7 @@ import { import { getCurrentMap, getRobotState } from '@/api/agv.js' import { ElMessage } from 'element-plus'; import { useRobotStore } from '@/stores/robot' +import { getThemeColor } from '@/utils/themeColors.js' const props = defineProps({ mapName: { @@ -223,7 +224,7 @@ function renderFrame(timestamp) { drawAdvancedAreaList(canvasCtx, map, applicationState) } - // 图层4: normalLineList(基础直线,青色 #00e5ff) + // 图层4: normalLineList(基础青色直线) if (layers.normalLines) { drawNormalLines(canvasCtx, map, applicationState); } @@ -233,7 +234,7 @@ function renderFrame(timestamp) { drawAdvancedLineList(canvasCtx, map, applicationState); } - // 图层6: advancedCurveList(贝塞尔曲线路径,酸绿色 #76ff03) + // 图层6: advancedCurveList(酸绿色贝塞尔曲线路径) if (layers.curves) { drawAdvancedCurveList(canvasCtx, map, applicationState); } @@ -345,7 +346,7 @@ const updateTooltipDisplay = (hitResult) => {
朝向: ${(robot.angle * 180 / Math.PI).toFixed(1)}°
ID: ${robot.id}
`; } - tooltipEl.innerHTML = `
${title}
${details}`; + tooltipEl.innerHTML = `
${title}
${details}`; tooltipEl.style.display = 'block'; tooltipEl.style.left = Math.min(applicationState.mouseState.screenX + 14, applicationState.canvasWidthPx - 220) + 'px'; tooltipEl.style.top = Math.max(4, applicationState.mouseState.screenY - 8) + 'px'; @@ -424,7 +425,7 @@ watch(() => props.mapName, async (newVal) => { if (newVal) { await loadSourceMap(newVal) - addRobot(robotStore.ip, robotStore.ip, robotStore.position.x, robotStore.position.y, robotStore.position.theta, '#00D4FF') + addRobot(robotStore.ip, robotStore.ip, robotStore.position.x, robotStore.position.y, robotStore.position.theta, getThemeColor('--color-canvas-robot')) requestAnimationFrame(renderFrame); window.addEventListener('resize', resizeCanvasToContainer); // 监听窗口大小变化 resizeCanvasToContainer(); // 初始调用一次 @@ -454,8 +455,8 @@ onUnmounted(() => { #tooltip { position: absolute; display: none; - background: #0b1120; - border: 1px solid #2a3a5c; + background: $color-background-tooltip-translucent; + border: 1px solid $color-accent-border-header; padding: 8px 12px; border-radius: 3px; pointer-events: none; @@ -463,20 +464,21 @@ onUnmounted(() => { font-size: 10px; min-width: 180px; max-width: 280px; - box-shadow: 0 4px 16px rgba(0, 0, 0, .5); + box-shadow: 0 4px 16px $color-shadow-tooltip; .tooltip-title { font-weight: 700; font-size: 11px; margin-bottom: 5px; + color: $color-canvas-tooltip-title; } :deep(.tooltip-detail) { - color: #94a3b8; + color: $color-text-tooltip; padding: 2px 0; font-size: 9px; word-break: break-all; } } } - \ No newline at end of file + diff --git a/src/views/chassis/canvasUtils.js b/src/views/chassis/canvasUtils.js index b85e588..bb38f23 100644 --- a/src/views/chassis/canvasUtils.js +++ b/src/views/chassis/canvasUtils.js @@ -1,6 +1,8 @@ +import { getThemeColor } from '@/utils/themeColors.js'; + /** * 6位十六进制颜色字符串转为 CSS rgba 字符串 - * @param {string} hex - 如 "#ff6e40" + * @param {string} hexString - 六位十六进制颜色字符串 * @param {number} alpha - 透明度 0~1 * @returns {string} CSS rgba 字符串 */ @@ -61,7 +63,7 @@ export const decodeArgbInteger = (argbInt) => { * 将 decodeArgbInteger 的结果转为 CSS rgba() 字符串 * @param {{r,g,b,a}} colorObj - RGBA 分量对象 * @param {number} [alphaOverride] - 可选,覆盖 alpha 值(0~1) - * @returns {string|null} CSS rgba 字符串,如 "rgba(255,0,64,0.3)" + * @returns {string|null} CSS rgba 颜色字符串 */ export const toCssRgba = (colorObj, alphaOverride) => { if (!colorObj) return null; // 空对象返回 null @@ -108,7 +110,7 @@ export const drawGridlines = (canvasCtx, dataStore) => { const viewMaxY = Math.ceil(Math.max(viewTopLeft.y, viewBottomRight.y)); // 可视Y最大整数 // 绘制1米间隔的浅色网格线 - canvasCtx.strokeStyle = 'rgba(30,50,80,0.25)'; // 淡蓝灰色 + canvasCtx.strokeStyle = getThemeColor('--color-canvas-grid-line'); canvasCtx.lineWidth = 0.5; // 细线 canvasCtx.beginPath(); for (let worldX = viewMinX; worldX <= viewMaxX; worldX++) { // 逐列绘制垂直线 @@ -138,8 +140,8 @@ export const drawAdvancedAreaList = (canvasCtx, map, dataStore) => { if (vertices.length < 3) continue; // 少于3个顶点无法构成多边形,跳过 // ── 颜色:优先使用 attribute 中的 ARGB 整数,否则用默认品红色 ── - let fillColorCss = 'rgba(224,64,251,0.12)'; // 默认填充(品红半透明) - let strokeColorCss = 'rgba(224,64,251,0.5)'; // 默认描边(品红较不透明) + let fillColorCss = getThemeColor('--color-canvas-area-fill'); + let strokeColorCss = getThemeColor('--color-canvas-area-border'); if (area.attribute) { const brushColor = decodeArgbInteger(area.attribute.colorBrush); // 解码填充色 const penColor = decodeArgbInteger(area.attribute.colorPen); // 解码描边色 @@ -196,14 +198,14 @@ export const drawAdvancedLineList = (canvasCtx, map, dataStore) => { const endScreen = worldToScreen(lineData.endPos.x, lineData.endPos.y, store); // ── 根据 className 决定颜色和线型 ── - let lineColorHex = '#ff6e40'; // 默认橙色 + let lineColorHex = getThemeColor('--color-canvas-line-default'); let lineDashPattern = []; // 默认实线(无虚线) const classNameLower = (advLine.className || '').toLowerCase(); if (classNameLower.includes('forbidden')) { // 禁行线 → 红色虚线 - lineColorHex = '#ff1744'; + lineColorHex = getThemeColor('--color-canvas-line-forbidden'); lineDashPattern = [8, 4]; // 虚线模式:8px实 4px空 } else if (classNameLower.includes('virtual')) { // 虚拟线 → 橙色虚线 - lineColorHex = '#ffab40'; + lineColorHex = getThemeColor('--color-canvas-line-virtual'); lineDashPattern = [4, 4]; } @@ -247,9 +249,9 @@ export const drawNormalLines = (canvasCtx, map, dataStore) => { // 绘制发光直线 canvasCtx.save(); - canvasCtx.shadowColor = '#00e5ff'; // 阴影颜色(产生辉光) + canvasCtx.shadowColor = getThemeColor('--color-canvas-route'); canvasCtx.shadowBlur = 5; // 辉光扩散半径 - canvasCtx.strokeStyle = '#00e5ff'; // 线条颜色:青色 + canvasCtx.strokeStyle = getThemeColor('--color-canvas-route'); canvasCtx.lineWidth = 2; // 线宽 canvasCtx.globalAlpha = 0.8; // 80%不透明 canvasCtx.beginPath(); @@ -263,7 +265,7 @@ export const drawNormalLines = (canvasCtx, map, dataStore) => { const midpointY = (startScreen.y + endScreen.y) / 2; // 中点Y const arrowAngle = Math.atan2(endScreen.y - startScreen.y, endScreen.x - startScreen.x); // 线段方向角 const arrowLength = 5; // 箭头长度 - canvasCtx.fillStyle = 'rgba(0,229,255,0.6)'; // 箭头填充色 + canvasCtx.fillStyle = getThemeColor('--color-canvas-route-arrow'); canvasCtx.beginPath(); canvasCtx.moveTo(midpointX + Math.cos(arrowAngle) * arrowLength, midpointY + Math.sin(arrowAngle) * arrowLength); // 箭头尖端 canvasCtx.lineTo(midpointX + Math.cos(arrowAngle + 2.5) * 4, midpointY + Math.sin(arrowAngle + 2.5) * 4); // 左翼 @@ -299,9 +301,9 @@ export const drawAdvancedCurveList = (canvasCtx, map, dataStore) => { // ── 绘制贝塞尔曲线(虚线)── canvasCtx.save(); - canvasCtx.shadowColor = '#76ff03'; // 酸绿色辉光 + canvasCtx.shadowColor = getThemeColor('--color-canvas-curve'); canvasCtx.shadowBlur = 5; - canvasCtx.strokeStyle = '#76ff03'; // 酸绿色线条 + canvasCtx.strokeStyle = getThemeColor('--color-canvas-curve'); canvasCtx.lineWidth = 2; canvasCtx.setLineDash([8, 5]); // 虚线模式 canvasCtx.globalAlpha = 0.85; @@ -334,7 +336,7 @@ export const drawAdvancedCurveList = (canvasCtx, map, dataStore) => { const tangentAngle = Math.atan2(tangentY, tangentX); // 切线角度 // 绘制箭头 - canvasCtx.fillStyle = 'rgba(118,255,3,0.7)'; + canvasCtx.fillStyle = getThemeColor('--color-canvas-curve-arrow'); canvasCtx.beginPath(); canvasCtx.moveTo(arrowX + Math.cos(tangentAngle) * 5, arrowY + Math.sin(tangentAngle) * 5); canvasCtx.lineTo(arrowX + Math.cos(tangentAngle + 2.5) * 4, arrowY + Math.sin(tangentAngle + 2.5) * 4); @@ -343,7 +345,7 @@ export const drawAdvancedCurveList = (canvasCtx, map, dataStore) => { canvasCtx.fill(); // 绘制控制点小圆(辅助调试用) - canvasCtx.fillStyle = 'rgba(118,255,3,0.25)'; // 浅绿色半透明 + canvasCtx.fillStyle = getThemeColor('--color-canvas-curve-marker'); for (const controlScreen of [cp1Screen, cp2Screen]) { canvasCtx.beginPath(); canvasCtx.arc(controlScreen.x, controlScreen.y, 2.5, 0, Math.PI * 2); // 半径2.5px的小圆 @@ -353,7 +355,6 @@ export const drawAdvancedCurveList = (canvasCtx, map, dataStore) => { // ── 曲线名称标签 ── // canvasCtx.font = '500 7px "JetBrains Mono"'; - // canvasCtx.fillStyle = 'rgba(118,255,3,0.5)'; // canvasCtx.textAlign = 'center'; // const labelScreenY = Math.min(startScreen.y, endScreen.y) - 8; // 标签放在最高点上方 // canvasCtx.fillText(curve.instanceName || '', (startScreen.x + endScreen.x) / 2, labelScreenY); @@ -378,18 +379,18 @@ export const drawAdvancedPointList = (canvasCtx, map, dataStore) => { store.hoveredElement.index === pointIndex; // ── 根据 className 决定颜色 ── - let pointColorHex = '#ffd600'; // 默认琥珀色 + let pointColorHex = getThemeColor('--color-canvas-landmark'); const classNameLower = (advPoint.className || '').toLowerCase(); if (classNameLower.includes('landmark') || classNameLower.includes('land')) { - pointColorHex = '#ffd600'; // LandMark → 琥珀色 + pointColorHex = getThemeColor('--color-canvas-landmark'); } else if (classNameLower.includes('charge')) { - pointColorHex = '#22c55e'; // 充电点 → 绿色 + pointColorHex = getThemeColor('--color-canvas-charging-point'); } else if (classNameLower.includes('load')) { - pointColorHex = '#3b82f6'; // 装载点 → 蓝色 + pointColorHex = getThemeColor('--color-canvas-loading-point'); } else if (classNameLower.includes('work') || classNameLower.includes('process')) { - pointColorHex = '#a855f7'; // 工位 → 紫色 + pointColorHex = getThemeColor('--color-canvas-workstation'); } else if (classNameLower.includes('station')) { - pointColorHex = '#00e5ff'; // 站点 → 青色 + pointColorHex = getThemeColor('--color-canvas-route'); } // ── 绘制菱形标记 ── @@ -413,7 +414,7 @@ export const drawAdvancedPointList = (canvasCtx, map, dataStore) => { // 中心白色小圆点(指示精确位置) canvasCtx.shadowBlur = 0; - canvasCtx.fillStyle = '#fff'; + canvasCtx.fillStyle = getThemeColor('--color-connection-card'); canvasCtx.globalAlpha = 0.9; canvasCtx.beginPath(); canvasCtx.arc(pointScreen.x, pointScreen.y, 1.8, 0, Math.PI * 2); // 半径1.8px @@ -457,7 +458,7 @@ export const drawMapBoundary = (canvasCtx, map, dataStore) => { const store = dataStore ; const boundaryTopLeft = worldToScreen(map.originX, map.maxY, store); // 边界左上角屏幕位置 const boundaryBottomRight = worldToScreen(map.maxX, map.originY, store); // 边界右下角屏幕位置 - canvasCtx.strokeStyle = 'rgba(0,229,160,0.1)'; // 极淡的青绿色 + canvasCtx.strokeStyle = getThemeColor('--color-canvas-boundary'); canvasCtx.lineWidth = 1; canvasCtx.setLineDash([5, 4]); // 虚线 canvasCtx.strokeRect( @@ -555,7 +556,7 @@ export const buildOccupancyGridImage = (parsedMap, showEdgeGlow) => { } export class MobileRobot { - constructor(id, name, x, y, angleRad = 0, color= "#00D4FF", imageUrl = null) { + constructor(id, name, x, y, angleRad = 0, color = getThemeColor('--color-canvas-robot'), imageUrl = null) { this.id = id; this.name = name; this.x = x; // 世界坐标X (米) @@ -602,7 +603,7 @@ export class MobileRobot { } // 名字标签 ctx.font = '12px "JetBrains Mono"'; - ctx.fillStyle = '#a0c0f0'; + ctx.fillStyle = getThemeColor('--color-canvas-robot-label'); ctx.shadowBlur = 0; ctx.fillText(this.name, screenX, screenY - 20); }