Commit b59f2fb2a4d619d7897e06c15b4c3e0e06e1db91
1 parent
67f451d6
init
Showing
10 changed files
with
398 additions
and
154 deletions
package-lock.json
| @@ -289,9 +289,6 @@ | @@ -289,9 +289,6 @@ | ||
| 289 | "arm64" | 289 | "arm64" |
| 290 | ], | 290 | ], |
| 291 | "dev": true, | 291 | "dev": true, |
| 292 | - "libc": [ | ||
| 293 | - "glibc" | ||
| 294 | - ], | ||
| 295 | "license": "MIT", | 292 | "license": "MIT", |
| 296 | "optional": true, | 293 | "optional": true, |
| 297 | "os": [ | 294 | "os": [ |
| @@ -309,9 +306,6 @@ | @@ -309,9 +306,6 @@ | ||
| 309 | "arm64" | 306 | "arm64" |
| 310 | ], | 307 | ], |
| 311 | "dev": true, | 308 | "dev": true, |
| 312 | - "libc": [ | ||
| 313 | - "musl" | ||
| 314 | - ], | ||
| 315 | "license": "MIT", | 309 | "license": "MIT", |
| 316 | "optional": true, | 310 | "optional": true, |
| 317 | "os": [ | 311 | "os": [ |
| @@ -329,9 +323,6 @@ | @@ -329,9 +323,6 @@ | ||
| 329 | "ppc64" | 323 | "ppc64" |
| 330 | ], | 324 | ], |
| 331 | "dev": true, | 325 | "dev": true, |
| 332 | - "libc": [ | ||
| 333 | - "glibc" | ||
| 334 | - ], | ||
| 335 | "license": "MIT", | 326 | "license": "MIT", |
| 336 | "optional": true, | 327 | "optional": true, |
| 337 | "os": [ | 328 | "os": [ |
| @@ -349,9 +340,6 @@ | @@ -349,9 +340,6 @@ | ||
| 349 | "s390x" | 340 | "s390x" |
| 350 | ], | 341 | ], |
| 351 | "dev": true, | 342 | "dev": true, |
| 352 | - "libc": [ | ||
| 353 | - "glibc" | ||
| 354 | - ], | ||
| 355 | "license": "MIT", | 343 | "license": "MIT", |
| 356 | "optional": true, | 344 | "optional": true, |
| 357 | "os": [ | 345 | "os": [ |
| @@ -369,9 +357,6 @@ | @@ -369,9 +357,6 @@ | ||
| 369 | "x64" | 357 | "x64" |
| 370 | ], | 358 | ], |
| 371 | "dev": true, | 359 | "dev": true, |
| 372 | - "libc": [ | ||
| 373 | - "glibc" | ||
| 374 | - ], | ||
| 375 | "license": "MIT", | 360 | "license": "MIT", |
| 376 | "optional": true, | 361 | "optional": true, |
| 377 | "os": [ | 362 | "os": [ |
| @@ -389,9 +374,6 @@ | @@ -389,9 +374,6 @@ | ||
| 389 | "x64" | 374 | "x64" |
| 390 | ], | 375 | ], |
| 391 | "dev": true, | 376 | "dev": true, |
| 392 | - "libc": [ | ||
| 393 | - "musl" | ||
| 394 | - ], | ||
| 395 | "license": "MIT", | 377 | "license": "MIT", |
| 396 | "optional": true, | 378 | "optional": true, |
| 397 | "os": [ | 379 | "os": [ |
| @@ -1325,9 +1307,6 @@ | @@ -1325,9 +1307,6 @@ | ||
| 1325 | "arm64" | 1307 | "arm64" |
| 1326 | ], | 1308 | ], |
| 1327 | "dev": true, | 1309 | "dev": true, |
| 1328 | - "libc": [ | ||
| 1329 | - "glibc" | ||
| 1330 | - ], | ||
| 1331 | "license": "MPL-2.0", | 1310 | "license": "MPL-2.0", |
| 1332 | "optional": true, | 1311 | "optional": true, |
| 1333 | "os": [ | 1312 | "os": [ |
| @@ -1349,9 +1328,6 @@ | @@ -1349,9 +1328,6 @@ | ||
| 1349 | "arm64" | 1328 | "arm64" |
| 1350 | ], | 1329 | ], |
| 1351 | "dev": true, | 1330 | "dev": true, |
| 1352 | - "libc": [ | ||
| 1353 | - "musl" | ||
| 1354 | - ], | ||
| 1355 | "license": "MPL-2.0", | 1331 | "license": "MPL-2.0", |
| 1356 | "optional": true, | 1332 | "optional": true, |
| 1357 | "os": [ | 1333 | "os": [ |
| @@ -1373,9 +1349,6 @@ | @@ -1373,9 +1349,6 @@ | ||
| 1373 | "x64" | 1349 | "x64" |
| 1374 | ], | 1350 | ], |
| 1375 | "dev": true, | 1351 | "dev": true, |
| 1376 | - "libc": [ | ||
| 1377 | - "glibc" | ||
| 1378 | - ], | ||
| 1379 | "license": "MPL-2.0", | 1352 | "license": "MPL-2.0", |
| 1380 | "optional": true, | 1353 | "optional": true, |
| 1381 | "os": [ | 1354 | "os": [ |
| @@ -1397,9 +1370,6 @@ | @@ -1397,9 +1370,6 @@ | ||
| 1397 | "x64" | 1370 | "x64" |
| 1398 | ], | 1371 | ], |
| 1399 | "dev": true, | 1372 | "dev": true, |
| 1400 | - "libc": [ | ||
| 1401 | - "musl" | ||
| 1402 | - ], | ||
| 1403 | "license": "MPL-2.0", | 1373 | "license": "MPL-2.0", |
| 1404 | "optional": true, | 1374 | "optional": true, |
| 1405 | "os": [ | 1375 | "os": [ |
src/App.vue
| 1 | <template> | 1 | <template> |
| 2 | - <router-view /> | 2 | + <a-config-provider |
| 3 | + :theme="{ | ||
| 4 | + algorithm: app.isDarkMode ? theme.darkAlgorithm : theme.defaultAlgorithm, | ||
| 5 | + token: { | ||
| 6 | + colorPrimary: '#8c7cf0', | ||
| 7 | + borderRadius: 12, | ||
| 8 | + fontFamily: 'Outfit, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif' | ||
| 9 | + } | ||
| 10 | + }" | ||
| 11 | + > | ||
| 12 | + <router-view /> | ||
| 13 | + </a-config-provider> | ||
| 3 | </template> | 14 | </template> |
| 15 | + | ||
| 16 | +<script setup lang="ts"> | ||
| 17 | +import { theme } from 'ant-design-vue' | ||
| 18 | +import { useAppStore } from '@/stores/app' | ||
| 19 | + | ||
| 20 | +const app = useAppStore() | ||
| 21 | +</script> | ||
| 22 | + | ||
| 23 | +<style> | ||
| 24 | +/* Loading bar simulation */ | ||
| 25 | +body::before { | ||
| 26 | + content: ""; | ||
| 27 | + position: fixed; | ||
| 28 | + top: 0; | ||
| 29 | + left: 0; | ||
| 30 | + height: 3px; | ||
| 31 | + background: linear-gradient(90deg, #8c7cf0, #f1bfd8); | ||
| 32 | + z-index: 9999; | ||
| 33 | + width: 0; | ||
| 34 | + transition: width 0.3s ease; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +body.loading::before { | ||
| 38 | + width: 80%; | ||
| 39 | + animation: loading-pulse 2s infinite linear; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +@keyframes loading-pulse { | ||
| 43 | + 0% { width: 0%; opacity: 1; } | ||
| 44 | + 50% { width: 70%; opacity: 0.8; } | ||
| 45 | + 100% { width: 90%; opacity: 0.6; } | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +body:not(.loading)::before { | ||
| 49 | + width: 100%; | ||
| 50 | + opacity: 0; | ||
| 51 | + transition: width 0.3s ease, opacity 0.3s 0.2s ease; | ||
| 52 | +} | ||
| 53 | +</style> |
src/layouts/MainLayout.vue
| 1 | <template> | 1 | <template> |
| 2 | <div class="layout-shell" :class="{ collapsed }"> | 2 | <div class="layout-shell" :class="{ collapsed }"> |
| 3 | - <aside class="soft-sider" :class="{ collapsed }"> | 3 | + <aside v-if="!isMobile" class="soft-sider" :class="{ collapsed }"> |
| 4 | <button class="sider-toggle" type="button" @click="collapsed = !collapsed"> | 4 | <button class="sider-toggle" type="button" @click="collapsed = !collapsed"> |
| 5 | <menu-fold-outlined v-if="!collapsed" /> | 5 | <menu-fold-outlined v-if="!collapsed" /> |
| 6 | <menu-unfold-outlined v-else /> | 6 | <menu-unfold-outlined v-else /> |
| @@ -77,12 +77,49 @@ | @@ -77,12 +77,49 @@ | ||
| 77 | 77 | ||
| 78 | <main class="content-column"> | 78 | <main class="content-column"> |
| 79 | <header class="soft-topbar"> | 79 | <header class="soft-topbar"> |
| 80 | - <div> | ||
| 81 | - <p class="eyebrow">Soft-Neo Admin</p> | ||
| 82 | - <h1>{{ currentTitle }}</h1> | 80 | + <div class="topbar-left"> |
| 81 | + <button v-if="isMobile" class="mobile-menu-trigger" type="button" @click="collapsed = !collapsed"> | ||
| 82 | + <menu-unfold-outlined /> | ||
| 83 | + </button> | ||
| 84 | + <div class="topbar-header-info"> | ||
| 85 | + <a-breadcrumb separator="/"> | ||
| 86 | + <a-breadcrumb-item key="home"> | ||
| 87 | + <router-link to="/"> | ||
| 88 | + <home-outlined /> | ||
| 89 | + </router-link> | ||
| 90 | + </a-breadcrumb-item> | ||
| 91 | + <a-breadcrumb-item v-for="bc in breadcrumbs" :key="bc.path"> | ||
| 92 | + {{ bc.title }} | ||
| 93 | + </a-breadcrumb-item> | ||
| 94 | + </a-breadcrumb> | ||
| 95 | + <h1>{{ currentTitle }}</h1> | ||
| 96 | + </div> | ||
| 83 | </div> | 97 | </div> |
| 84 | <div class="topbar-actions"> | 98 | <div class="topbar-actions"> |
| 85 | - <div class="date-pill"> | 99 | + <div class="header-action-group"> |
| 100 | + <a-tooltip :title="app.isDarkMode ? 'ๅๆขไบฎ่ฒๆจกๅผ' : 'ๅๆขๆ่ฒๆจกๅผ'"> | ||
| 101 | + <a-button type="text" shape="circle" @click="app.toggleDarkMode()"> | ||
| 102 | + <template #icon> | ||
| 103 | + <bulb-outlined v-if="!app.isDarkMode" /> | ||
| 104 | + <bulb-filled v-else style="color: #fadb14" /> | ||
| 105 | + </template> | ||
| 106 | + </a-button> | ||
| 107 | + </a-tooltip> | ||
| 108 | + <a-tooltip title="้็ฅ"> | ||
| 109 | + <a-button type="text" shape="circle"> | ||
| 110 | + <template #icon><bell-outlined /></template> | ||
| 111 | + </a-button> | ||
| 112 | + </a-tooltip> | ||
| 113 | + <a-tooltip :title="isFullscreen ? '้ๅบๅ จๅฑ' : 'ๅ จๅฑ'"> | ||
| 114 | + <a-button type="text" shape="circle" @click="toggleFullscreen"> | ||
| 115 | + <template #icon> | ||
| 116 | + <fullscreen-outlined v-if="!isFullscreen" /> | ||
| 117 | + <fullscreen-exit-outlined v-else /> | ||
| 118 | + </template> | ||
| 119 | + </a-button> | ||
| 120 | + </a-tooltip> | ||
| 121 | + </div> | ||
| 122 | + <div class="date-pill hide-sm"> | ||
| 86 | <calendar-outlined /> | 123 | <calendar-outlined /> |
| 87 | <span>{{ todayLabel }}</span> | 124 | <span>{{ todayLabel }}</span> |
| 88 | </div> | 125 | </div> |
| @@ -108,45 +145,134 @@ | @@ -108,45 +145,134 @@ | ||
| 108 | <router-view /> | 145 | <router-view /> |
| 109 | </div> | 146 | </div> |
| 110 | </main> | 147 | </main> |
| 148 | + | ||
| 149 | + <a-drawer | ||
| 150 | + v-if="isMobile" | ||
| 151 | + :open="!collapsed" | ||
| 152 | + placement="left" | ||
| 153 | + :closable="false" | ||
| 154 | + @update:open="collapsed = true" | ||
| 155 | + > | ||
| 156 | + <div class="drawer-brand"> | ||
| 157 | + <div class="brand-mark">DL</div> | ||
| 158 | + <strong>ๅฐๅฉ้ชๆ</strong> | ||
| 159 | + </div> | ||
| 160 | + <a-menu | ||
| 161 | + v-model:selectedKeys="selectedKeys" | ||
| 162 | + mode="inline" | ||
| 163 | + @click="onMenuClick(); collapsed = true" | ||
| 164 | + > | ||
| 165 | + <a-menu-item key="/dashboard"> | ||
| 166 | + <template #icon><home-outlined /></template> | ||
| 167 | + ๅทฅไฝๅฐ | ||
| 168 | + </a-menu-item> | ||
| 169 | + <a-menu-item v-if="isAdmin" key="/city"> | ||
| 170 | + <template #icon><global-outlined /></template> | ||
| 171 | + ็งๆท็ฎก็ | ||
| 172 | + </a-menu-item> | ||
| 173 | + <a-menu-item v-if="isAdmin" key="/substation"> | ||
| 174 | + <template #icon><apartment-outlined /></template> | ||
| 175 | + ๅ็ซ็ฎก็ | ||
| 176 | + </a-menu-item> | ||
| 177 | + <a-sub-menu key="merchant"> | ||
| 178 | + <template #icon><shop-outlined /></template> | ||
| 179 | + <template #title>ๅๅฎถ็ฎก็</template> | ||
| 180 | + <a-menu-item key="/merchant/enter">ๅ ฅ้ฉป็ณ่ฏท</a-menu-item> | ||
| 181 | + <a-menu-item key="/merchant/store">ๅบ้บ็ฎก็</a-menu-item> | ||
| 182 | + </a-sub-menu> | ||
| 183 | + <a-menu-item key="/rider"> | ||
| 184 | + <template #icon><user-outlined /></template> | ||
| 185 | + ้ชๆ็ฎก็ | ||
| 186 | + </a-menu-item> | ||
| 187 | + <a-menu-item key="/rider/evaluate"> | ||
| 188 | + <template #icon><star-outlined /></template> | ||
| 189 | + ้ชๆ่ฏไปท | ||
| 190 | + </a-menu-item> | ||
| 191 | + <a-sub-menu key="orders"> | ||
| 192 | + <template #icon><unordered-list-outlined /></template> | ||
| 193 | + <template #title>่ฎขๅ็ฎก็</template> | ||
| 194 | + <a-menu-item key="/order">่ฎขๅๅ่กจ</a-menu-item> | ||
| 195 | + <a-menu-item key="/refund">้ๆฌพ็ฎก็</a-menu-item> | ||
| 196 | + <a-menu-item key="/delivery/order">้ ้่ฎขๅ</a-menu-item> | ||
| 197 | + </a-sub-menu> | ||
| 198 | + <a-sub-menu key="config"> | ||
| 199 | + <template #icon><control-outlined /></template> | ||
| 200 | + <template #title>้ ็ฝฎไธญๅฟ</template> | ||
| 201 | + <a-menu-item key="/config/fee-plan">้ ้่ดน้ ็ฝฎ</a-menu-item> | ||
| 202 | + <a-menu-item key="/dispatch/rule">่ฐๅบฆ้ ็ฝฎ</a-menu-item> | ||
| 203 | + </a-sub-menu> | ||
| 204 | + <a-sub-menu key="open"> | ||
| 205 | + <template #icon><api-outlined /></template> | ||
| 206 | + <template #title>ๅผๆพๅนณๅฐ</template> | ||
| 207 | + <a-menu-item key="/open">ๅบ็จ็ฎก็</a-menu-item> | ||
| 208 | + <a-menu-item key="/open/mock-delivery">ๆจกๆๆจๅ</a-menu-item> | ||
| 209 | + </a-sub-menu> | ||
| 210 | + </a-menu> | ||
| 211 | + </a-drawer> | ||
| 111 | </div> | 212 | </div> |
| 112 | </template> | 213 | </template> |
| 113 | 214 | ||
| 114 | <script setup lang="ts"> | 215 | <script setup lang="ts"> |
| 115 | -import { computed, ref, watch } from 'vue' | 216 | +import { theme } from 'ant-design-vue' |
| 217 | +import { computed, ref, watch, onMounted, onUnmounted } from 'vue' | ||
| 116 | import { useRouter, useRoute } from 'vue-router' | 218 | import { useRouter, useRoute } from 'vue-router' |
| 117 | import { useAuthStore } from '@/stores/auth' | 219 | import { useAuthStore } from '@/stores/auth' |
| 220 | +import { useAppStore } from '@/stores/app' | ||
| 118 | import { | 221 | import { |
| 119 | GlobalOutlined, ApartmentOutlined, ShopOutlined, | 222 | GlobalOutlined, ApartmentOutlined, ShopOutlined, |
| 120 | UserOutlined, UnorderedListOutlined, ApiOutlined, DownOutlined, StarOutlined, | 223 | UserOutlined, UnorderedListOutlined, ApiOutlined, DownOutlined, StarOutlined, |
| 121 | - CalendarOutlined, MenuFoldOutlined, MenuUnfoldOutlined, HomeOutlined, ControlOutlined | 224 | + CalendarOutlined, MenuFoldOutlined, MenuUnfoldOutlined, HomeOutlined, ControlOutlined, |
| 225 | + FullscreenOutlined, FullscreenExitOutlined, BellOutlined, | ||
| 226 | + BulbOutlined, BulbFilled | ||
| 122 | } from '@ant-design/icons-vue' | 227 | } from '@ant-design/icons-vue' |
| 123 | 228 | ||
| 124 | const router = useRouter() | 229 | const router = useRouter() |
| 125 | const route = useRoute() | 230 | const route = useRoute() |
| 126 | const auth = useAuthStore() | 231 | const auth = useAuthStore() |
| 232 | +const app = useAppStore() | ||
| 127 | const collapsed = ref(false) | 233 | const collapsed = ref(false) |
| 128 | const selectedKeys = ref([route.path]) | 234 | const selectedKeys = ref([route.path]) |
| 129 | -const titleMap: Record<string, string> = { | ||
| 130 | - '/dashboard': 'ๅทฅไฝๅฐ', | ||
| 131 | - '/city': '็งๆท็ฎก็', | ||
| 132 | - '/substation': 'ๅ็ซ็ฎก็', | ||
| 133 | - '/merchant/enter': 'ๅๅฎถๅ ฅ้ฉป', | ||
| 134 | - '/merchant/store': 'ๅบ้บ็ฎก็', | ||
| 135 | - '/rider': '้ชๆ็ฎก็', | ||
| 136 | - '/rider/evaluate': '้ชๆ่ฏไปท', | ||
| 137 | - '/order': '่ฎขๅๅ่กจ', | ||
| 138 | - '/refund': '้ๆฌพ็ฎก็', | ||
| 139 | - '/delivery/order': '้ ้่ฎขๅ', | ||
| 140 | - '/config/fee-plan': '้ ้่ดน้ ็ฝฎ', | ||
| 141 | - '/dispatch/rule': '่ฐๅบฆ้ ็ฝฎ', | ||
| 142 | - '/open': 'ๅผๆพๅนณๅฐ', | ||
| 143 | - '/open/mock-delivery': 'ๆจกๆๆจๅ', | 235 | + |
| 236 | +const isMobile = ref(window.innerWidth <= 960) | ||
| 237 | +function handleResize() { | ||
| 238 | + isMobile.value = window.innerWidth <= 960 | ||
| 144 | } | 239 | } |
| 145 | 240 | ||
| 241 | +onMounted(() => { | ||
| 242 | + window.addEventListener('resize', handleResize) | ||
| 243 | +}) | ||
| 244 | +onUnmounted(() => { | ||
| 245 | + window.removeEventListener('resize', handleResize) | ||
| 246 | +}) | ||
| 247 | + | ||
| 146 | watch(() => route.path, (p) => { selectedKeys.value = [p] }) | 248 | watch(() => route.path, (p) => { selectedKeys.value = [p] }) |
| 147 | 249 | ||
| 148 | const isAdmin = computed(() => auth.userInfo?.role === 'admin') | 250 | const isAdmin = computed(() => auth.userInfo?.role === 'admin') |
| 149 | -const currentTitle = computed(() => titleMap[route.path] || 'ๅคๅ็ฎก็') | 251 | +const currentTitle = computed(() => (route.meta.title as string) || 'ๅคๅ็ฎก็') |
| 252 | + | ||
| 253 | +// Breadcrumbs logic | ||
| 254 | +const breadcrumbs = computed(() => { | ||
| 255 | + return route.matched | ||
| 256 | + .filter(r => r.meta && r.meta.title) | ||
| 257 | + .map(r => ({ | ||
| 258 | + title: r.meta.title, | ||
| 259 | + path: r.path || '/', | ||
| 260 | + })) | ||
| 261 | +}) | ||
| 262 | + | ||
| 263 | +const isFullscreen = ref(false) | ||
| 264 | +function toggleFullscreen() { | ||
| 265 | + if (!document.fullscreenElement) { | ||
| 266 | + document.documentElement.requestFullscreen() | ||
| 267 | + isFullscreen.value = true | ||
| 268 | + } else { | ||
| 269 | + if (document.exitFullscreen) { | ||
| 270 | + document.exitFullscreen() | ||
| 271 | + isFullscreen.value = false | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | +} | ||
| 275 | + | ||
| 150 | const avatarText = computed(() => (auth.userInfo?.userNickname || '็ฎก็ๅ').slice(0, 1)) | 276 | const avatarText = computed(() => (auth.userInfo?.userNickname || '็ฎก็ๅ').slice(0, 1)) |
| 151 | const todayLabel = computed(() => new Intl.DateTimeFormat('zh-CN', { | 277 | const todayLabel = computed(() => new Intl.DateTimeFormat('zh-CN', { |
| 152 | month: 'long', | 278 | month: 'long', |
| @@ -179,10 +305,10 @@ function handleLogout() { | @@ -179,10 +305,10 @@ function handleLogout() { | ||
| 179 | 305 | ||
| 180 | .soft-sider, | 306 | .soft-sider, |
| 181 | .soft-topbar { | 307 | .soft-topbar { |
| 182 | - border: 1px solid rgba(255, 255, 255, 0.58); | ||
| 183 | - background: rgba(255, 255, 255, 0.74); | 308 | + border: 1px solid var(--line); |
| 309 | + background: var(--panel); | ||
| 184 | backdrop-filter: blur(22px); | 310 | backdrop-filter: blur(22px); |
| 185 | - box-shadow: 0 16px 40px rgba(130, 110, 218, 0.12); | 311 | + box-shadow: var(--shadow-xl); |
| 186 | } | 312 | } |
| 187 | 313 | ||
| 188 | .soft-sider { | 314 | .soft-sider { |
| @@ -228,8 +354,8 @@ function handleLogout() { | @@ -228,8 +354,8 @@ function handleLogout() { | ||
| 228 | height: 38px; | 354 | height: 38px; |
| 229 | border-radius: 12px; | 355 | border-radius: 12px; |
| 230 | border: none; | 356 | border: none; |
| 231 | - background: rgba(246, 242, 255, 0.9); | ||
| 232 | - color: #7f6de5; | 357 | + background: var(--line-strong); |
| 358 | + color: var(--soft-primary); | ||
| 233 | cursor: pointer; | 359 | cursor: pointer; |
| 234 | margin-bottom: 12px; | 360 | margin-bottom: 12px; |
| 235 | } | 361 | } |
| @@ -285,7 +411,7 @@ function handleLogout() { | @@ -285,7 +411,7 @@ function handleLogout() { | ||
| 285 | .insight-card strong, | 411 | .insight-card strong, |
| 286 | h1 { | 412 | h1 { |
| 287 | font-family: 'Outfit', sans-serif; | 413 | font-family: 'Outfit', sans-serif; |
| 288 | - color: #2f2946; | 414 | + color: var(--text-dark); |
| 289 | } | 415 | } |
| 290 | 416 | ||
| 291 | .brand-copy span, | 417 | .brand-copy span, |
| @@ -294,7 +420,7 @@ h1 { | @@ -294,7 +420,7 @@ h1 { | ||
| 294 | .hero-copy p, | 420 | .hero-copy p, |
| 295 | .insight-card p, | 421 | .insight-card p, |
| 296 | .note-list { | 422 | .note-list { |
| 297 | - color: #8d88a4; | 423 | + color: var(--text-soft); |
| 298 | } | 424 | } |
| 299 | 425 | ||
| 300 | :deep(.ant-menu) { | 426 | :deep(.ant-menu) { |
| @@ -310,7 +436,7 @@ h1 { | @@ -310,7 +436,7 @@ h1 { | ||
| 310 | margin-top: 10px; | 436 | margin-top: 10px; |
| 311 | flex-shrink: 0; | 437 | flex-shrink: 0; |
| 312 | border-radius: 18px; | 438 | border-radius: 18px; |
| 313 | - background: linear-gradient(180deg, rgba(245, 241, 255, 0.85), rgba(255, 247, 250, 0.92)); | 439 | + background: var(--panel-tint); |
| 314 | padding: 12px; | 440 | padding: 12px; |
| 315 | } | 441 | } |
| 316 | 442 | ||
| @@ -367,10 +493,11 @@ h1 { | @@ -367,10 +493,11 @@ h1 { | ||
| 367 | align-items: center; | 493 | align-items: center; |
| 368 | gap: 10px; | 494 | gap: 10px; |
| 369 | border-radius: 999px; | 495 | border-radius: 999px; |
| 370 | - background: rgba(255, 255, 255, 0.82); | ||
| 371 | - border: 1px solid rgba(194, 184, 237, 0.38); | 496 | + background: var(--panel-strong); |
| 497 | + border: 1px solid var(--line); | ||
| 372 | padding: 7px 10px; | 498 | padding: 7px 10px; |
| 373 | font-size: 12px; | 499 | font-size: 12px; |
| 500 | + color: var(--text-main); | ||
| 374 | } | 501 | } |
| 375 | 502 | ||
| 376 | .profile-button { | 503 | .profile-button { |
| @@ -443,25 +570,68 @@ h1 { | @@ -443,25 +570,68 @@ h1 { | ||
| 443 | display: none !important; | 570 | display: none !important; |
| 444 | } | 571 | } |
| 445 | 572 | ||
| 573 | +.topbar-left { | ||
| 574 | + display: flex; | ||
| 575 | + align-items: center; | ||
| 576 | + gap: 16px; | ||
| 577 | +} | ||
| 578 | + | ||
| 579 | +.topbar-header-info h1 { | ||
| 580 | + margin: 2px 0 0; | ||
| 581 | + font-size: 20px; | ||
| 582 | +} | ||
| 583 | + | ||
| 584 | +.header-action-group { | ||
| 585 | + display: flex; | ||
| 586 | + align-items: center; | ||
| 587 | + gap: 4px; | ||
| 588 | + margin-right: 8px; | ||
| 589 | +} | ||
| 590 | + | ||
| 591 | +.mobile-menu-trigger { | ||
| 592 | + width: 40px; | ||
| 593 | + height: 40px; | ||
| 594 | + border-radius: 12px; | ||
| 595 | + border: none; | ||
| 596 | + background: rgba(140, 124, 240, 0.1); | ||
| 597 | + color: #8c7cf0; | ||
| 598 | + cursor: pointer; | ||
| 599 | + display: flex; | ||
| 600 | + align-items: center; | ||
| 601 | + justify-content: center; | ||
| 602 | + font-size: 18px; | ||
| 603 | +} | ||
| 604 | + | ||
| 605 | +.drawer-brand { | ||
| 606 | + display: flex; | ||
| 607 | + align-items: center; | ||
| 608 | + gap: 12px; | ||
| 609 | + padding: 12px 16px 24px; | ||
| 610 | +} | ||
| 611 | + | ||
| 612 | +:deep(.ant-breadcrumb) { | ||
| 613 | + font-size: 11px; | ||
| 614 | +} | ||
| 615 | + | ||
| 616 | +:deep(.ant-breadcrumb-link), :deep(.ant-breadcrumb-separator) { | ||
| 617 | + color: var(--soft-subtext) !important; | ||
| 618 | +} | ||
| 619 | + | ||
| 446 | @media (max-width: 960px) { | 620 | @media (max-width: 960px) { |
| 447 | .layout-shell { | 621 | .layout-shell { |
| 448 | grid-template-columns: 1fr; | 622 | grid-template-columns: 1fr; |
| 449 | - padding: 14px; | 623 | + padding: 12px; |
| 450 | } | 624 | } |
| 451 | - | ||
| 452 | - .soft-sider { | ||
| 453 | - position: relative; | ||
| 454 | - top: 0; | ||
| 455 | - height: auto; | 625 | + |
| 626 | + .hide-sm { | ||
| 627 | + display: none !important; | ||
| 456 | } | 628 | } |
| 457 | 629 | ||
| 458 | - .menu-scroll { | ||
| 459 | - overflow: visible; | ||
| 460 | - padding-right: 0; | ||
| 461 | - } | ||
| 462 | .soft-topbar { | 630 | .soft-topbar { |
| 463 | - flex-direction: column; | ||
| 464 | - align-items: flex-start; | 631 | + padding: 10px 14px; |
| 632 | + flex-direction: row; | ||
| 633 | + align-items: center; | ||
| 634 | + border-radius: 18px; | ||
| 465 | } | 635 | } |
| 466 | } | 636 | } |
| 467 | 637 |
src/router/index.ts
| @@ -110,6 +110,16 @@ router.beforeEach((to) => { | @@ -110,6 +110,16 @@ router.beforeEach((to) => { | ||
| 110 | if (!to.meta.public && !auth.token) { | 110 | if (!to.meta.public && !auth.token) { |
| 111 | return { path: '/login' } | 111 | return { path: '/login' } |
| 112 | } | 112 | } |
| 113 | + | ||
| 114 | + // Start loading progress | ||
| 115 | + document.body.classList.add('loading') | ||
| 116 | +}) | ||
| 117 | + | ||
| 118 | +router.afterEach(() => { | ||
| 119 | + // Stop loading progress | ||
| 120 | + setTimeout(() => { | ||
| 121 | + document.body.classList.remove('loading') | ||
| 122 | + }, 300) | ||
| 113 | }) | 123 | }) |
| 114 | 124 | ||
| 115 | export default router | 125 | export default router |
src/stores/app.ts
0 โ 100644
| 1 | +import { defineStore } from 'pinia' | ||
| 2 | +import { ref } from 'vue' | ||
| 3 | + | ||
| 4 | +export const useAppStore = defineStore('app', () => { | ||
| 5 | + const isDarkMode = ref(localStorage.getItem('dark-mode') === 'true') | ||
| 6 | + | ||
| 7 | + function toggleDarkMode() { | ||
| 8 | + isDarkMode.value = !isDarkMode.value | ||
| 9 | + localStorage.setItem('dark-mode', isDarkMode.value.toString()) | ||
| 10 | + if (isDarkMode.value) { | ||
| 11 | + document.documentElement.classList.add('dark') | ||
| 12 | + } else { | ||
| 13 | + document.documentElement.classList.remove('dark') | ||
| 14 | + } | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + // Initialize | ||
| 18 | + if (isDarkMode.value) { | ||
| 19 | + document.documentElement.classList.add('dark') | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + return { | ||
| 23 | + isDarkMode, | ||
| 24 | + toggleDarkMode, | ||
| 25 | + } | ||
| 26 | +}) |
src/style.css
| @@ -34,6 +34,7 @@ | @@ -34,6 +34,7 @@ | ||
| 34 | --font-size-sm: 12px; | 34 | --font-size-sm: 12px; |
| 35 | --font-size-md: 13px; | 35 | --font-size-md: 13px; |
| 36 | --font-size-lg: 15px; | 36 | --font-size-lg: 15px; |
| 37 | + --bg-color: #f8f8ff; | ||
| 37 | color: var(--text-main); | 38 | color: var(--text-main); |
| 38 | font-family: var(--font-body); | 39 | font-family: var(--font-body); |
| 39 | line-height: 1.5; | 40 | line-height: 1.5; |
| @@ -42,7 +43,23 @@ | @@ -42,7 +43,23 @@ | ||
| 42 | text-rendering: optimizeLegibility; | 43 | text-rendering: optimizeLegibility; |
| 43 | -webkit-font-smoothing: antialiased; | 44 | -webkit-font-smoothing: antialiased; |
| 44 | -moz-osx-font-smoothing: grayscale; | 45 | -moz-osx-font-smoothing: grayscale; |
| 45 | - background: #f8f8ff; | 46 | + background: var(--bg-color); |
| 47 | +} | ||
| 48 | + | ||
| 49 | +.dark { | ||
| 50 | + --app-bg: #14111d; | ||
| 51 | + --bg-color: #14111d; | ||
| 52 | + --panel: rgba(30, 26, 46, 0.76); | ||
| 53 | + --panel-strong: rgba(40, 36, 60, 0.9); | ||
| 54 | + --panel-tint: linear-gradient(135deg, rgba(74, 58, 145, 0.45), rgba(125, 48, 88, 0.4)); | ||
| 55 | + --line: rgba(255, 255, 255, 0.1); | ||
| 56 | + --line-strong: rgba(255, 255, 255, 0.15); | ||
| 57 | + --text-main: #eeecf1; | ||
| 58 | + --text-soft: #a4a0b8; | ||
| 59 | + --text-dark: #ffffff; | ||
| 60 | + --shadow-xl: 0 22px 60px rgba(0, 0, 0, 0.5); | ||
| 61 | + --shadow-lg: 0 16px 35px rgba(0, 0, 0, 0.4); | ||
| 62 | + --shadow-sm: 0 10px 20px rgba(0, 0, 0, 0.3); | ||
| 46 | } | 63 | } |
| 47 | 64 | ||
| 48 | * { | 65 | * { |
| @@ -92,11 +109,11 @@ a { | @@ -92,11 +109,11 @@ a { | ||
| 92 | 109 | ||
| 93 | .ant-btn-default { | 110 | .ant-btn-default { |
| 94 | border-color: var(--line); | 111 | border-color: var(--line); |
| 95 | - background: rgba(255, 255, 255, 0.82); | 112 | + background: var(--panel-strong); |
| 96 | } | 113 | } |
| 97 | 114 | ||
| 98 | .ant-card { | 115 | .ant-card { |
| 99 | - border: 1px solid rgba(255, 255, 255, 0.55); | 116 | + border: 1px solid var(--line); |
| 100 | background: var(--panel); | 117 | background: var(--panel); |
| 101 | backdrop-filter: blur(18px); | 118 | backdrop-filter: blur(18px); |
| 102 | border-radius: var(--radius-lg); | 119 | border-radius: var(--radius-lg); |
| @@ -104,7 +121,7 @@ a { | @@ -104,7 +121,7 @@ a { | ||
| 104 | } | 121 | } |
| 105 | 122 | ||
| 106 | .ant-card .ant-card-head { | 123 | .ant-card .ant-card-head { |
| 107 | - border-bottom: 1px solid rgba(188, 180, 230, 0.18); | 124 | + border-bottom: 1px solid var(--line); |
| 108 | min-height: 56px; | 125 | min-height: 56px; |
| 109 | padding-inline: 20px; | 126 | padding-inline: 20px; |
| 110 | } | 127 | } |
| @@ -131,7 +148,7 @@ a { | @@ -131,7 +148,7 @@ a { | ||
| 131 | } | 148 | } |
| 132 | 149 | ||
| 133 | .ant-table-wrapper .ant-table-thead > tr > th { | 150 | .ant-table-wrapper .ant-table-thead > tr > th { |
| 134 | - background: rgba(244, 240, 255, 0.88); | 151 | + background: var(--panel-strong); |
| 135 | color: var(--text-dark); | 152 | color: var(--text-dark); |
| 136 | border-bottom: none; | 153 | border-bottom: none; |
| 137 | font-weight: 700; | 154 | font-weight: 700; |
| @@ -141,15 +158,16 @@ a { | @@ -141,15 +158,16 @@ a { | ||
| 141 | } | 158 | } |
| 142 | 159 | ||
| 143 | .ant-table-wrapper .ant-table-tbody > tr > td { | 160 | .ant-table-wrapper .ant-table-tbody > tr > td { |
| 144 | - border-bottom: 1px solid rgba(226, 220, 247, 0.72); | ||
| 145 | - background: rgba(255, 255, 255, 0.48); | 161 | + border-bottom: 1px solid var(--line); |
| 162 | + background: transparent; | ||
| 163 | + color: var(--text-main); | ||
| 146 | font-size: var(--font-size-md); | 164 | font-size: var(--font-size-md); |
| 147 | padding-top: 11px; | 165 | padding-top: 11px; |
| 148 | padding-bottom: 11px; | 166 | padding-bottom: 11px; |
| 149 | } | 167 | } |
| 150 | 168 | ||
| 151 | .ant-table-wrapper .ant-table-tbody > tr:hover > td { | 169 | .ant-table-wrapper .ant-table-tbody > tr:hover > td { |
| 152 | - background: rgba(247, 242, 255, 0.95) !important; | 170 | + background: var(--line-strong) !important; |
| 153 | } | 171 | } |
| 154 | 172 | ||
| 155 | .ant-input, | 173 | .ant-input, |
| @@ -160,8 +178,9 @@ a { | @@ -160,8 +178,9 @@ a { | ||
| 160 | .ant-input-number-input-wrap, | 178 | .ant-input-number-input-wrap, |
| 161 | .ant-picker { | 179 | .ant-picker { |
| 162 | border-radius: 16px !important; | 180 | border-radius: 16px !important; |
| 163 | - border-color: rgba(189, 180, 234, 0.4) !important; | ||
| 164 | - background: rgba(255, 255, 255, 0.78) !important; | 181 | + border-color: var(--line-strong) !important; |
| 182 | + background: var(--panel-strong) !important; | ||
| 183 | + color: var(--text-main) !important; | ||
| 165 | box-shadow: none !important; | 184 | box-shadow: none !important; |
| 166 | font-size: var(--font-size-md); | 185 | font-size: var(--font-size-md); |
| 167 | } | 186 | } |
| @@ -201,15 +220,15 @@ a { | @@ -201,15 +220,15 @@ a { | ||
| 201 | .ant-modal .ant-modal-content, | 220 | .ant-modal .ant-modal-content, |
| 202 | .ant-dropdown .ant-dropdown-menu { | 221 | .ant-dropdown .ant-dropdown-menu { |
| 203 | border-radius: 22px; | 222 | border-radius: 22px; |
| 204 | - border: 1px solid rgba(228, 223, 247, 0.7); | ||
| 205 | - background: rgba(255, 255, 255, 0.92); | 223 | + border: 1px solid var(--line); |
| 224 | + background: var(--panel-strong); | ||
| 206 | backdrop-filter: blur(24px); | 225 | backdrop-filter: blur(24px); |
| 207 | box-shadow: var(--shadow-xl); | 226 | box-shadow: var(--shadow-xl); |
| 208 | } | 227 | } |
| 209 | 228 | ||
| 210 | .ant-modal .ant-modal-header { | 229 | .ant-modal .ant-modal-header { |
| 211 | background: transparent; | 230 | background: transparent; |
| 212 | - border-bottom: 1px solid rgba(189, 180, 234, 0.18); | 231 | + border-bottom: 1px solid var(--line); |
| 213 | padding: 18px 20px 12px; | 232 | padding: 18px 20px 12px; |
| 214 | } | 233 | } |
| 215 | 234 | ||
| @@ -226,7 +245,7 @@ a { | @@ -226,7 +245,7 @@ a { | ||
| 226 | 245 | ||
| 227 | .ant-modal .ant-modal-footer { | 246 | .ant-modal .ant-modal-footer { |
| 228 | padding: 12px 20px 18px; | 247 | padding: 12px 20px 18px; |
| 229 | - border-top: 1px solid rgba(189, 180, 234, 0.14); | 248 | + border-top: 1px solid var(--line); |
| 230 | } | 249 | } |
| 231 | 250 | ||
| 232 | .ant-modal .ant-modal-footer .ant-btn + .ant-btn { | 251 | .ant-modal .ant-modal-footer .ant-btn + .ant-btn { |
| @@ -276,14 +295,15 @@ a { | @@ -276,14 +295,15 @@ a { | ||
| 276 | 295 | ||
| 277 | .ant-descriptions .ant-descriptions-item-label { | 296 | .ant-descriptions .ant-descriptions-item-label { |
| 278 | width: 128px; | 297 | width: 128px; |
| 279 | - background: rgba(244, 240, 255, 0.72) !important; | 298 | + background: var(--panel-strong) !important; |
| 280 | color: var(--text-dark) !important; | 299 | color: var(--text-dark) !important; |
| 281 | font-size: var(--font-size-md); | 300 | font-size: var(--font-size-md); |
| 282 | font-weight: 600; | 301 | font-weight: 600; |
| 283 | } | 302 | } |
| 284 | 303 | ||
| 285 | .ant-descriptions .ant-descriptions-item-content { | 304 | .ant-descriptions .ant-descriptions-item-content { |
| 286 | - background: rgba(255, 255, 255, 0.72) !important; | 305 | + background: var(--panel) !important; |
| 306 | + color: var(--text-main); | ||
| 287 | font-size: var(--font-size-md); | 307 | font-size: var(--font-size-md); |
| 288 | } | 308 | } |
| 289 | 309 | ||
| @@ -341,10 +361,10 @@ a { | @@ -341,10 +361,10 @@ a { | ||
| 341 | .ant-menu-submenu-popup .ant-menu { | 361 | .ant-menu-submenu-popup .ant-menu { |
| 342 | padding: 8px !important; | 362 | padding: 8px !important; |
| 343 | border-radius: 16px !important; | 363 | border-radius: 16px !important; |
| 344 | - border: 1px solid rgba(228, 223, 247, 0.72) !important; | ||
| 345 | - background: rgba(255, 255, 255, 0.96) !important; | 364 | + border: 1px solid var(--line) !important; |
| 365 | + background: var(--panel-strong) !important; | ||
| 346 | backdrop-filter: blur(20px); | 366 | backdrop-filter: blur(20px); |
| 347 | - box-shadow: 0 18px 40px rgba(121, 104, 213, 0.14) !important; | 367 | + box-shadow: var(--shadow-xl) !important; |
| 348 | } | 368 | } |
| 349 | 369 | ||
| 350 | .ant-menu-submenu-popup .ant-menu-item, | 370 | .ant-menu-submenu-popup .ant-menu-item, |
| @@ -359,18 +379,18 @@ a { | @@ -359,18 +379,18 @@ a { | ||
| 359 | 379 | ||
| 360 | .ant-menu-submenu-popup .ant-menu-item:hover, | 380 | .ant-menu-submenu-popup .ant-menu-item:hover, |
| 361 | .ant-menu-submenu-popup .ant-menu-submenu-title:hover { | 381 | .ant-menu-submenu-popup .ant-menu-submenu-title:hover { |
| 362 | - background: rgba(244, 240, 255, 0.92) !important; | 382 | + background: var(--line) !important; |
| 363 | } | 383 | } |
| 364 | 384 | ||
| 365 | .ant-menu-item:hover, | 385 | .ant-menu-item:hover, |
| 366 | .ant-menu-submenu-title:hover { | 386 | .ant-menu-submenu-title:hover { |
| 367 | color: var(--brand-deep) !important; | 387 | color: var(--brand-deep) !important; |
| 368 | - background: rgba(255, 255, 255, 0.5) !important; | 388 | + background: var(--line) !important; |
| 369 | } | 389 | } |
| 370 | 390 | ||
| 371 | .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled) { | 391 | .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled) { |
| 372 | color: var(--brand-deep); | 392 | color: var(--brand-deep); |
| 373 | - background: rgba(255, 255, 255, 0.92); | 393 | + background: var(--panel-strong); |
| 374 | border-color: rgba(140, 124, 240, 0.45); | 394 | border-color: rgba(140, 124, 240, 0.45); |
| 375 | box-shadow: 0 8px 18px rgba(140, 124, 240, 0.12); | 395 | box-shadow: 0 8px 18px rgba(140, 124, 240, 0.12); |
| 376 | } | 396 | } |
| @@ -450,8 +470,8 @@ a { | @@ -450,8 +470,8 @@ a { | ||
| 450 | .soft-note-card { | 470 | .soft-note-card { |
| 451 | border-radius: 16px; | 471 | border-radius: 16px; |
| 452 | padding: 14px 16px; | 472 | padding: 14px 16px; |
| 453 | - background: linear-gradient(135deg, rgba(244, 240, 255, 0.9), rgba(255, 250, 253, 0.9)); | ||
| 454 | - border: 1px solid rgba(206, 196, 244, 0.36); | 473 | + background: var(--panel-strong); |
| 474 | + border: 1px solid var(--line); | ||
| 455 | } | 475 | } |
| 456 | 476 | ||
| 457 | .soft-note-card strong { | 477 | .soft-note-card strong { |
| @@ -478,7 +498,7 @@ a { | @@ -478,7 +498,7 @@ a { | ||
| 478 | .soft-dashed-block { | 498 | .soft-dashed-block { |
| 479 | border-radius: 18px; | 499 | border-radius: 18px; |
| 480 | border: 1px dashed rgba(180, 170, 231, 0.5); | 500 | border: 1px dashed rgba(180, 170, 231, 0.5); |
| 481 | - background: rgba(255, 255, 255, 0.48); | 501 | + background: var(--panel); |
| 482 | padding: 14px; | 502 | padding: 14px; |
| 483 | } | 503 | } |
| 484 | 504 |
src/views/Login.vue
| @@ -103,10 +103,10 @@ async function onSubmit() { | @@ -103,10 +103,10 @@ async function onSubmit() { | ||
| 103 | 103 | ||
| 104 | .login-visual, | 104 | .login-visual, |
| 105 | .login-card { | 105 | .login-card { |
| 106 | - border: 1px solid rgba(255, 255, 255, 0.62); | ||
| 107 | - background: rgba(255, 255, 255, 0.76); | 106 | + border: 1px solid var(--line); |
| 107 | + background: var(--panel); | ||
| 108 | backdrop-filter: blur(24px); | 108 | backdrop-filter: blur(24px); |
| 109 | - box-shadow: 0 20px 50px rgba(126, 110, 211, 0.15); | 109 | + box-shadow: var(--shadow-xl); |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | .login-visual { | 112 | .login-visual { |
| @@ -133,7 +133,7 @@ async function onSubmit() { | @@ -133,7 +133,7 @@ async function onSubmit() { | ||
| 133 | .login-visual h1, | 133 | .login-visual h1, |
| 134 | .login-card h2 { | 134 | .login-card h2 { |
| 135 | font-family: 'Outfit', sans-serif; | 135 | font-family: 'Outfit', sans-serif; |
| 136 | - color: #2f2946; | 136 | + color: var(--text-dark); |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | .login-visual h1 { | 139 | .login-visual h1 { |
| @@ -144,7 +144,7 @@ async function onSubmit() { | @@ -144,7 +144,7 @@ async function onSubmit() { | ||
| 144 | 144 | ||
| 145 | .login-visual p, | 145 | .login-visual p, |
| 146 | .login-card-head p { | 146 | .login-card-head p { |
| 147 | - color: #8d88a4; | 147 | + color: var(--text-soft); |
| 148 | max-width: 520px; | 148 | max-width: 520px; |
| 149 | } | 149 | } |
| 150 | 150 | ||
| @@ -158,13 +158,13 @@ async function onSubmit() { | @@ -158,13 +158,13 @@ async function onSubmit() { | ||
| 158 | .visual-stats > div { | 158 | .visual-stats > div { |
| 159 | min-width: 210px; | 159 | min-width: 210px; |
| 160 | border-radius: 22px; | 160 | border-radius: 22px; |
| 161 | - background: rgba(255, 255, 255, 0.7); | 161 | + background: var(--panel-strong); |
| 162 | padding: 14px 16px; | 162 | padding: 14px 16px; |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | .visual-stats span { | 165 | .visual-stats span { |
| 166 | display: block; | 166 | display: block; |
| 167 | - color: #9893ad; | 167 | + color: var(--text-soft); |
| 168 | font-size: 12px; | 168 | font-size: 12px; |
| 169 | text-transform: uppercase; | 169 | text-transform: uppercase; |
| 170 | letter-spacing: 0.08em; | 170 | letter-spacing: 0.08em; |
| @@ -172,7 +172,7 @@ async function onSubmit() { | @@ -172,7 +172,7 @@ async function onSubmit() { | ||
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | .visual-stats strong { | 174 | .visual-stats strong { |
| 175 | - color: #342d4f; | 175 | + color: var(--text-dark); |
| 176 | font-family: 'Outfit', sans-serif; | 176 | font-family: 'Outfit', sans-serif; |
| 177 | } | 177 | } |
| 178 | 178 |
src/views/config/FeePlanList.vue
| @@ -847,8 +847,8 @@ onMounted(loadCities) | @@ -847,8 +847,8 @@ onMounted(loadCities) | ||
| 847 | .plan-sidebar, | 847 | .plan-sidebar, |
| 848 | .plan-content { | 848 | .plan-content { |
| 849 | border-radius: 24px; | 849 | border-radius: 24px; |
| 850 | - border: 1px solid rgba(194, 185, 239, 0.22); | ||
| 851 | - background: rgba(255, 255, 255, 0.58); | 850 | + border: 1px solid var(--line); |
| 851 | + background: var(--panel); | ||
| 852 | padding: 18px; | 852 | padding: 18px; |
| 853 | } | 853 | } |
| 854 | 854 | ||
| @@ -867,7 +867,7 @@ onMounted(loadCities) | @@ -867,7 +867,7 @@ onMounted(loadCities) | ||
| 867 | flex-direction: column; | 867 | flex-direction: column; |
| 868 | gap: 12px; | 868 | gap: 12px; |
| 869 | padding-bottom: 6px; | 869 | padding-bottom: 6px; |
| 870 | - background: linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(255, 255, 255, 0.76) 78%, rgba(255, 255, 255, 0)); | 870 | + background: var(--bg-color); |
| 871 | } | 871 | } |
| 872 | 872 | ||
| 873 | .plan-content-body { | 873 | .plan-content-body { |
| @@ -925,8 +925,8 @@ onMounted(loadCities) | @@ -925,8 +925,8 @@ onMounted(loadCities) | ||
| 925 | } | 925 | } |
| 926 | 926 | ||
| 927 | .plan-item { | 927 | .plan-item { |
| 928 | - border: 1px solid rgba(194, 185, 239, 0.22); | ||
| 929 | - background: rgba(255, 255, 255, 0.7); | 928 | + border: 1px solid var(--line); |
| 929 | + background: var(--panel-strong); | ||
| 930 | border-radius: 18px; | 930 | border-radius: 18px; |
| 931 | padding: 14px; | 931 | padding: 14px; |
| 932 | text-align: left; | 932 | text-align: left; |
| @@ -934,9 +934,9 @@ onMounted(loadCities) | @@ -934,9 +934,9 @@ onMounted(loadCities) | ||
| 934 | } | 934 | } |
| 935 | 935 | ||
| 936 | .plan-item.active { | 936 | .plan-item.active { |
| 937 | - border-color: rgba(140, 124, 240, 0.44); | ||
| 938 | - background: rgba(246, 242, 255, 0.95); | ||
| 939 | - box-shadow: 0 10px 24px rgba(140, 124, 240, 0.12); | 937 | + border-color: var(--brand); |
| 938 | + background: var(--panel-tint); | ||
| 939 | + box-shadow: var(--shadow-sm); | ||
| 940 | } | 940 | } |
| 941 | 941 | ||
| 942 | .plan-item-top, | 942 | .plan-item-top, |
| @@ -954,10 +954,10 @@ onMounted(loadCities) | @@ -954,10 +954,10 @@ onMounted(loadCities) | ||
| 954 | flex-wrap: wrap; | 954 | flex-wrap: wrap; |
| 955 | align-items: center; | 955 | align-items: center; |
| 956 | padding: 14px 16px; | 956 | padding: 14px 16px; |
| 957 | - border: 1px solid rgba(194, 185, 239, 0.22); | 957 | + border: 1px solid var(--line); |
| 958 | border-radius: 20px; | 958 | border-radius: 20px; |
| 959 | - background: rgba(255, 255, 255, 0.88); | ||
| 960 | - box-shadow: 0 10px 24px rgba(140, 124, 240, 0.08); | 959 | + background: var(--panel-strong); |
| 960 | + box-shadow: var(--shadow-sm); | ||
| 961 | } | 961 | } |
| 962 | 962 | ||
| 963 | .plan-toolbar-meta, | 963 | .plan-toolbar-meta, |
| @@ -995,9 +995,9 @@ onMounted(loadCities) | @@ -995,9 +995,9 @@ onMounted(loadCities) | ||
| 995 | 995 | ||
| 996 | .plan-section { | 996 | .plan-section { |
| 997 | padding: 18px 20px 20px; | 997 | padding: 18px 20px 20px; |
| 998 | - border: 1px solid rgba(194, 185, 239, 0.18); | 998 | + border: 1px solid var(--line); |
| 999 | border-radius: 22px; | 999 | border-radius: 22px; |
| 1000 | - background: rgba(255, 255, 255, 0.72); | 1000 | + background: var(--panel-strong); |
| 1001 | } | 1001 | } |
| 1002 | 1002 | ||
| 1003 | .plan-section-last { | 1003 | .plan-section-last { |
src/views/dashboard/DashboardHome.vue
| @@ -86,18 +86,16 @@ function go(path: string) { | @@ -86,18 +86,16 @@ function go(path: string) { | ||
| 86 | gap: 14px; | 86 | gap: 14px; |
| 87 | border-radius: 24px; | 87 | border-radius: 24px; |
| 88 | padding: 20px; | 88 | padding: 20px; |
| 89 | - background: | ||
| 90 | - linear-gradient(160deg, rgba(255, 255, 255, 0.82), rgba(255, 255, 255, 0.62)), | ||
| 91 | - linear-gradient(135deg, rgba(198, 185, 255, 0.72), rgba(255, 217, 236, 0.78)); | ||
| 92 | - border: 1px solid rgba(255, 255, 255, 0.62); | ||
| 93 | - box-shadow: 0 18px 44px rgba(132, 114, 212, 0.14); | 89 | + background: var(--panel-tint); |
| 90 | + border: 1px solid var(--line); | ||
| 91 | + box-shadow: var(--shadow-xl); | ||
| 94 | } | 92 | } |
| 95 | 93 | ||
| 96 | .hero-pill { | 94 | .hero-pill { |
| 97 | display: inline-flex; | 95 | display: inline-flex; |
| 98 | padding: 6px 12px; | 96 | padding: 6px 12px; |
| 99 | border-radius: 999px; | 97 | border-radius: 999px; |
| 100 | - background: rgba(255, 255, 255, 0.76); | 98 | + background: var(--panel-strong); |
| 101 | color: #6f5fe2; | 99 | color: #6f5fe2; |
| 102 | font-size: 12px; | 100 | font-size: 12px; |
| 103 | font-weight: 700; | 101 | font-weight: 700; |
| @@ -110,7 +108,7 @@ function go(path: string) { | @@ -110,7 +108,7 @@ function go(path: string) { | ||
| 110 | font-size: 24px; | 108 | font-size: 24px; |
| 111 | line-height: 1.15; | 109 | line-height: 1.15; |
| 112 | font-family: 'Outfit', sans-serif; | 110 | font-family: 'Outfit', sans-serif; |
| 113 | - color: #2f2946; | 111 | + color: var(--text-dark); |
| 114 | } | 112 | } |
| 115 | 113 | ||
| 116 | .hero-copy p { | 114 | .hero-copy p { |
| @@ -123,7 +121,7 @@ function go(path: string) { | @@ -123,7 +121,7 @@ function go(path: string) { | ||
| 123 | .hero-metric span, | 121 | .hero-metric span, |
| 124 | .quick-link span, | 122 | .quick-link span, |
| 125 | .soft-notes { | 123 | .soft-notes { |
| 126 | - color: #8d88a4; | 124 | + color: var(--text-soft); |
| 127 | } | 125 | } |
| 128 | 126 | ||
| 129 | .hero-grid { | 127 | .hero-grid { |
| @@ -136,7 +134,7 @@ function go(path: string) { | @@ -136,7 +134,7 @@ function go(path: string) { | ||
| 136 | .hero-metric { | 134 | .hero-metric { |
| 137 | min-width: 190px; | 135 | min-width: 190px; |
| 138 | border-radius: 18px; | 136 | border-radius: 18px; |
| 139 | - background: rgba(255, 255, 255, 0.74); | 137 | + background: var(--panel-strong); |
| 140 | padding: 11px 13px; | 138 | padding: 11px 13px; |
| 141 | } | 139 | } |
| 142 | 140 | ||
| @@ -150,7 +148,7 @@ function go(path: string) { | @@ -150,7 +148,7 @@ function go(path: string) { | ||
| 150 | 148 | ||
| 151 | .hero-metric strong, | 149 | .hero-metric strong, |
| 152 | .quick-link strong { | 150 | .quick-link strong { |
| 153 | - color: #322c4a; | 151 | + color: var(--text-dark); |
| 154 | font-family: 'Outfit', sans-serif; | 152 | font-family: 'Outfit', sans-serif; |
| 155 | } | 153 | } |
| 156 | 154 | ||
| @@ -204,8 +202,8 @@ function go(path: string) { | @@ -204,8 +202,8 @@ function go(path: string) { | ||
| 204 | 202 | ||
| 205 | .quick-link { | 203 | .quick-link { |
| 206 | text-align: left; | 204 | text-align: left; |
| 207 | - border: 1px solid rgba(202, 193, 240, 0.34); | ||
| 208 | - background: rgba(255, 255, 255, 0.78); | 205 | + border: 1px solid var(--line); |
| 206 | + background: var(--panel); | ||
| 209 | border-radius: 18px; | 207 | border-radius: 18px; |
| 210 | padding: 14px; | 208 | padding: 14px; |
| 211 | cursor: pointer; | 209 | cursor: pointer; |
src/views/dispatch/DispatchRuleList.vue
| @@ -515,8 +515,8 @@ onMounted(loadCities) | @@ -515,8 +515,8 @@ onMounted(loadCities) | ||
| 515 | .plan-sidebar, | 515 | .plan-sidebar, |
| 516 | .plan-content { | 516 | .plan-content { |
| 517 | border-radius: 24px; | 517 | border-radius: 24px; |
| 518 | - border: 1px solid rgba(194, 185, 239, 0.22); | ||
| 519 | - background: rgba(255, 255, 255, 0.58); | 518 | + border: 1px solid var(--line); |
| 519 | + background: var(--panel); | ||
| 520 | padding: 18px; | 520 | padding: 18px; |
| 521 | } | 521 | } |
| 522 | 522 | ||
| @@ -535,7 +535,7 @@ onMounted(loadCities) | @@ -535,7 +535,7 @@ onMounted(loadCities) | ||
| 535 | flex-direction: column; | 535 | flex-direction: column; |
| 536 | gap: 12px; | 536 | gap: 12px; |
| 537 | padding-bottom: 6px; | 537 | padding-bottom: 6px; |
| 538 | - background: linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(255, 255, 255, 0.76) 78%, rgba(255, 255, 255, 0)); | 538 | + background: var(--bg-color); |
| 539 | } | 539 | } |
| 540 | 540 | ||
| 541 | .plan-content-body { | 541 | .plan-content-body { |
| @@ -583,8 +583,8 @@ onMounted(loadCities) | @@ -583,8 +583,8 @@ onMounted(loadCities) | ||
| 583 | min-height: 36px; | 583 | min-height: 36px; |
| 584 | padding: 0 14px; | 584 | padding: 0 14px; |
| 585 | border-radius: 999px; | 585 | border-radius: 999px; |
| 586 | - border: 1px solid rgba(194, 185, 239, 0.28); | ||
| 587 | - background: rgba(246, 242, 255, 0.84); | 586 | + border: 1px solid var(--line); |
| 587 | + background: var(--panel-strong); | ||
| 588 | } | 588 | } |
| 589 | 589 | ||
| 590 | .plan-list { | 590 | .plan-list { |
| @@ -595,8 +595,8 @@ onMounted(loadCities) | @@ -595,8 +595,8 @@ onMounted(loadCities) | ||
| 595 | } | 595 | } |
| 596 | 596 | ||
| 597 | .plan-item { | 597 | .plan-item { |
| 598 | - border: 1px solid rgba(194, 185, 239, 0.22); | ||
| 599 | - background: rgba(255, 255, 255, 0.7); | 598 | + border: 1px solid var(--line); |
| 599 | + background: var(--panel-strong); | ||
| 600 | border-radius: 18px; | 600 | border-radius: 18px; |
| 601 | padding: 14px; | 601 | padding: 14px; |
| 602 | text-align: left; | 602 | text-align: left; |
| @@ -604,9 +604,9 @@ onMounted(loadCities) | @@ -604,9 +604,9 @@ onMounted(loadCities) | ||
| 604 | } | 604 | } |
| 605 | 605 | ||
| 606 | .plan-item.active { | 606 | .plan-item.active { |
| 607 | - border-color: rgba(140, 124, 240, 0.44); | ||
| 608 | - background: rgba(246, 242, 255, 0.95); | ||
| 609 | - box-shadow: 0 10px 24px rgba(140, 124, 240, 0.12); | 607 | + border-color: var(--brand); |
| 608 | + background: var(--panel-tint); | ||
| 609 | + box-shadow: var(--shadow-sm); | ||
| 610 | } | 610 | } |
| 611 | 611 | ||
| 612 | .plan-item-top, | 612 | .plan-item-top, |
| @@ -630,10 +630,10 @@ onMounted(loadCities) | @@ -630,10 +630,10 @@ onMounted(loadCities) | ||
| 630 | flex-wrap: wrap; | 630 | flex-wrap: wrap; |
| 631 | align-items: center; | 631 | align-items: center; |
| 632 | padding: 14px 16px; | 632 | padding: 14px 16px; |
| 633 | - border: 1px solid rgba(194, 185, 239, 0.22); | 633 | + border: 1px solid var(--line); |
| 634 | border-radius: 20px; | 634 | border-radius: 20px; |
| 635 | - background: rgba(255, 255, 255, 0.88); | ||
| 636 | - box-shadow: 0 10px 24px rgba(140, 124, 240, 0.08); | 635 | + background: var(--panel-strong); |
| 636 | + box-shadow: var(--shadow-sm); | ||
| 637 | } | 637 | } |
| 638 | 638 | ||
| 639 | .plan-toolbar-meta, | 639 | .plan-toolbar-meta, |
| @@ -682,9 +682,9 @@ onMounted(loadCities) | @@ -682,9 +682,9 @@ onMounted(loadCities) | ||
| 682 | 682 | ||
| 683 | .plan-section { | 683 | .plan-section { |
| 684 | padding: 18px 20px 20px; | 684 | padding: 18px 20px 20px; |
| 685 | - border: 1px solid rgba(194, 185, 239, 0.18); | 685 | + border: 1px solid var(--line); |
| 686 | border-radius: 22px; | 686 | border-radius: 22px; |
| 687 | - background: rgba(255, 255, 255, 0.72); | 687 | + background: var(--panel-strong); |
| 688 | } | 688 | } |
| 689 | 689 | ||
| 690 | .plan-section-head { | 690 | .plan-section-head { |
| @@ -778,8 +778,8 @@ onMounted(loadCities) | @@ -778,8 +778,8 @@ onMounted(loadCities) | ||
| 778 | min-height: 88px; | 778 | min-height: 88px; |
| 779 | padding: 14px 16px; | 779 | padding: 14px 16px; |
| 780 | border-radius: 18px; | 780 | border-radius: 18px; |
| 781 | - border: 1px solid rgba(194, 185, 239, 0.22); | ||
| 782 | - background: rgba(255, 255, 255, 0.84); | 781 | + border: 1px solid var(--line); |
| 782 | + background: var(--panel-strong); | ||
| 783 | transition: all 0.2s ease; | 783 | transition: all 0.2s ease; |
| 784 | } | 784 | } |
| 785 | 785 | ||
| @@ -815,11 +815,11 @@ onMounted(loadCities) | @@ -815,11 +815,11 @@ onMounted(loadCities) | ||
| 815 | display: grid; | 815 | display: grid; |
| 816 | grid-template-columns: 48px minmax(0, 1fr); | 816 | grid-template-columns: 48px minmax(0, 1fr); |
| 817 | gap: 14px; | 817 | gap: 14px; |
| 818 | - border: 1px solid rgba(194, 185, 239, 0.22); | 818 | + border: 1px solid var(--line); |
| 819 | border-radius: 20px; | 819 | border-radius: 20px; |
| 820 | - background: rgba(255, 255, 255, 0.9); | 820 | + background: var(--panel-strong); |
| 821 | padding: 16px; | 821 | padding: 16px; |
| 822 | - box-shadow: 0 12px 24px rgba(140, 124, 240, 0.08); | 822 | + box-shadow: var(--shadow-sm); |
| 823 | } | 823 | } |
| 824 | 824 | ||
| 825 | .dispatch-condition-row.disabled { | 825 | .dispatch-condition-row.disabled { |