ForcePilot/web/src/components/StatusBar.vue
Zhao Minhao 55f2e020b5
Add dark/light theme toggle feature (#343)
* Add dark/light theme toggle feature

* update: refine dark/light theme and related components

* 调整语义化主题变量,优化暗/亮模式切换效果

* Revert "调整语义化主题变量,优化暗/亮模式切换效果"

This reverts commit 85d9373297686fdbacb252a880b2847d20a39641.

* 深色模式适配:减少 !important,迁移 CSS 变量,优化布局

* style: 替换硬编码颜色值为CSS变量以支持主题切换

将多处硬编码的颜色值(如#fff、#f0f0f0等)替换为CSS变量(如--gray-0、--gray-150等),统一管理颜色样式,便于主题切换和样式维护。主要修改包括背景色、边框色、文字色等视觉元素,同时移除不再需要的深色模式适配代码。

* style: 使用CSS变量替换硬编码颜色值

* refactor(theme): 重构主题系统,统一使用CSS变量并优化暗色模式实现

- 移除冗余的theme.js配置文件,将主题配置集中到theme store
- 使用ant-design-vue的darkAlgorithm实现暗色模式
- 在多个组件中替换硬编码颜色值为CSS变量
- 优化用户信息组件,整合文档中心和主题切换功能
- 更新基础CSS样式,完善颜色系统和滚动条样式

* reset: 恢复被覆盖的修改(96d257a7ace38c94e2b113d56472d211d1141087)

* feat(theme): 实现深色主题支持并重构样式系统

重构颜色变量系统,添加深色主题支持
移除硬编码颜色值,统一使用CSS变量
优化图表组件以响应主题切换
清理无用样式代码,提升可维护性

* docs: 更新开发规范文档

* style: 调整边框和背景颜色样式

---------

Co-authored-by: Wenjie Zhang <xerrors@163.com>
2025-11-23 01:39:44 +08:00

283 lines
5.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="status-bar">
<div class="status-bar-content">
<!-- 左侧系统信息 -->
<div class="status-left">
<div class="system-info">
<div class="system-details">
<div class="system-name">{{ branding.name }}</div>
<div class="system-subtitle">{{ branding.subtitle }}</div>
</div>
</div>
</div>
<!-- 右侧时间和用户信息 -->
<div class="status-right">
<div class="time-info">
<Clock class="icon" />
<span class="current-time">{{ currentTime }}</span>
</div>
<div class="user-info">
<User class="icon" />
<span class="user-greeting">{{ greeting }}</span>
</div>
<div class="task-center-entry" @click="openTaskCenter">
<a-badge
:count="activeTaskCount"
:overflow-count="99"
class="task-center-badge"
>
<span class="task-center-button">
<ClipboardList class="icon" />
<span class="task-center-label">任务中心</span>
</span>
</a-badge>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useInfoStore } from '@/stores/info'
import { useUserStore } from '@/stores/user'
import { Clock, User, ClipboardList } from 'lucide-vue-next'
import { useTaskerStore } from '@/stores/tasker'
import { storeToRefs } from 'pinia'
import dayjs from '@/utils/time'
// 使用 stores
const infoStore = useInfoStore()
const userStore = useUserStore()
const taskerStore = useTaskerStore()
const { activeCount: activeCountRef } = storeToRefs(taskerStore)
// 响应式数据
const currentTime = ref('')
// 计算属性
const organization = computed(() => infoStore.organization)
const branding = computed(() => infoStore.branding)
// 用户名计算属性
const currentUser = computed(() => {
return userStore.username || '游客'
})
// 问候语计算属性
const greeting = computed(() => {
const hour = dayjs().tz('Asia/Shanghai').hour()
let greetingText = ''
if (hour >= 5 && hour < 12) {
greetingText = '早上好'
} else if (hour >= 12 && hour < 14) {
greetingText = '中午好'
} else if (hour >= 14 && hour < 18) {
greetingText = '下午好'
} else if (hour >= 18 && hour < 22) {
greetingText = '晚上好'
} else {
greetingText = '夜深了'
}
return `${greetingText}${currentUser.value}`
})
const activeTaskCount = computed(() => activeCountRef.value || 0)
const openTaskCenter = () => {
taskerStore.openDrawer()
}
// 更新时间
const updateTime = () => {
const now = dayjs().tz('Asia/Shanghai')
currentTime.value = now.format('YYYY年MM月DD日 HH:mm:ss')
}
// 定时器
let timeInterval = null
onMounted(async () => {
updateTime()
timeInterval = setInterval(updateTime, 1000)
// 获取用户信息
try {
await userStore.getCurrentUser()
} catch (error) {
console.error('获取用户信息失败:', error)
}
})
onUnmounted(() => {
if (timeInterval) {
clearInterval(timeInterval)
}
})
</script>
<style scoped lang="less">
.status-bar {
// background: var(--gray-0);
// backdrop-filter: blur(10px);
// height: 60px;
display: flex;
align-items: center;
// position: sticky;
top: 0;
z-index: 100;
}
.status-bar-content {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
// padding-bottom: 0;
}
.status-left {
display: flex;
align-items: center;
}
.system-info {
display: flex;
align-items: center;
gap: 12px;
}
.system-details {
.system-name {
font-size: 20px;
font-weight: 600;
color: var(--gray-900, #111827);
line-height: 1.4;
}
.system-subtitle {
font-size: 13px;
color: var(--gray-600, #6b7280);
line-height: 1.2;
}
}
.status-right {
display: flex;
align-items: center;
gap: 18px;
font-size: 13px;
}
.task-center-entry {
display: flex;
align-items: center;
cursor: pointer;
user-select: none;
}
.task-center-badge {
display: flex;
align-items: center;
}
.task-center-button {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 14px;
border-radius: 999px;
background-color: transparent;
color: var(--main-600, #2563eb);
font-size: 13px;
font-weight: 500;
border: 1px solid rgba(37, 99, 235, 0.3);
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.task-center-button .icon {
width: 15px;
height: 15px;
color: inherit;
}
.task-center-label {
letter-spacing: 0.2px;
}
.task-center-entry:hover .task-center-button {
background-color: rgba(37, 99, 235, 0.08);
color: var(--main-700, #1d4ed8);
border-color: rgba(37, 99, 235, 0.5);
}
.task-center-badge :deep(.ant-badge-count) {
background-color: var(--main-color, #1d4ed8);
}
.time-info,
.user-info {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
line-height: 1.3;
color: var(--gray-600, #4b5563);
.icon {
width: 15px;
height: 15px;
color: var(--gray-600, #6b7280);
}
}
.current-time,
.user-greeting {
font-weight: 500;
color: var(--gray-900, #111827);
}
// 响应式设计
@media (max-width: 768px) {
.status-bar {
height: 44px;
}
.status-bar-content {
padding: 0 16px;
}
.system-details {
.system-name {
font-size: 13px;
}
.system-subtitle {
font-size: 10px;
}
}
.status-right {
gap: 12px;
}
.time-info,
.user-info {
font-size: 11px;
.icon {
width: 12px;
height: 12px;
}
}
.current-time {
display: none; // 在小屏幕上隐藏时间
}
}
</style>