- 删除应用配置页面及相关组件,重构路由为 /developer/config/[id].vue - 移除开发者文档页面及其导航与样式实现 - 清理开发者侧功能完善工作日志文件 - 删除全局.gitignore配置文件,清理无用忽略规则 - 优化应用配置页面的参数读取和路由结构,解决刷新404问题 - 解决数据库配置唯一键冲突,调整保存逻辑避免重复插入 - 移除对后端配置加密字段的 secret 标记,修正加密异常问题
730 lines
24 KiB
Vue
730 lines
24 KiB
Vue
<template>
|
||
<div class="dashboard">
|
||
<!-- 顶部统计区域 -->
|
||
<div class="mb-6 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||
<!-- 待办任务 -->
|
||
<a-card class="stat-card">
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<div class="text-2xl font-bold text-gray-900">12</div>
|
||
<div class="mt-1 text-sm text-gray-500">待办任务</div>
|
||
</div>
|
||
<div class="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center">
|
||
<span class="text-2xl text-blue-600">📋</span>
|
||
</div>
|
||
</div>
|
||
<div class="mt-4 text-xs text-gray-400">今日有 3 项任务到期</div>
|
||
</a-card>
|
||
|
||
<!-- 进行中项目 -->
|
||
<a-card class="stat-card">
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<div class="text-2xl font-bold text-gray-900">8</div>
|
||
<div class="mt-1 text-sm text-gray-500">进行中项目</div>
|
||
</div>
|
||
<div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center">
|
||
<span class="text-2xl text-green-600">🚀</span>
|
||
</div>
|
||
</div>
|
||
<div class="mt-4 text-xs text-gray-400">其中有 2 个项目需关注</div>
|
||
</a-card>
|
||
|
||
<!-- 在线成员 -->
|
||
<a-card class="stat-card">
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<div class="text-2xl font-bold text-gray-900">24/32</div>
|
||
<div class="mt-1 text-sm text-gray-500">在线成员</div>
|
||
</div>
|
||
<div class="w-12 h-12 rounded-full bg-purple-100 flex items-center justify-center">
|
||
<span class="text-2xl text-purple-600">👥</span>
|
||
</div>
|
||
</div>
|
||
<div class="mt-4 text-xs text-gray-400">75% 在线率</div>
|
||
</a-card>
|
||
|
||
<!-- 文档协作 -->
|
||
<a-card class="stat-card">
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<div class="text-2xl font-bold text-gray-900">156</div>
|
||
<div class="mt-1 text-sm text-gray-500">文档协作</div>
|
||
</div>
|
||
<div class="w-12 h-12 rounded-full bg-orange-100 flex items-center justify-center">
|
||
<span class="text-2xl text-orange-600">📄</span>
|
||
</div>
|
||
</div>
|
||
<div class="mt-4 text-xs text-gray-400">今日更新 14 个文档</div>
|
||
</a-card>
|
||
</div>
|
||
|
||
<!-- 主要内容区域 -->
|
||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||
<!-- 左侧:项目概览 -->
|
||
<div class="lg:col-span-2">
|
||
<!-- 快捷操作 -->
|
||
<a-card class="mb-6">
|
||
<template #title>
|
||
<div class="flex items-center justify-between">
|
||
<span>📌 快捷操作</span>
|
||
<a-button type="link" size="small">更多</a-button>
|
||
</div>
|
||
</template>
|
||
<div class="grid grid-cols-2 gap-4 sm:grid-cols-4">
|
||
<div v-for="action in quickActions" :key="action.label"
|
||
class="quick-action-item group"
|
||
@click="navigateTo(action.to)">
|
||
<div class="quick-action-icon" :style="{ backgroundColor: action.color + '10', color: action.color }">
|
||
{{ action.icon }}
|
||
</div>
|
||
<div class="quick-action-text">
|
||
<div class="font-medium">{{ action.label }}</div>
|
||
<div class="text-xs text-gray-500">{{ action.desc }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-card>
|
||
|
||
<!-- 项目进度 -->
|
||
<a-card>
|
||
<template #title>
|
||
<div class="flex items-center justify-between">
|
||
<span>🏗️ 项目进度</span>
|
||
<a-button type="link" size="small" @click="navigateTo('/oa/projects')">查看所有</a-button>
|
||
</div>
|
||
</template>
|
||
<a-table :data-source="activeProjects" :pagination="false" size="small">
|
||
<a-table-column title="项目名称" data-index="name">
|
||
<template #default="{ record }">
|
||
<div class="flex items-center gap-2">
|
||
<div class="w-2 h-2 rounded-full" :style="{ backgroundColor: record.color }"></div>
|
||
<span>{{ record.name }}</span>
|
||
</div>
|
||
</template>
|
||
</a-table-column>
|
||
<a-table-column title="进度" data-index="progress">
|
||
<template #default="{ record }">
|
||
<div class="flex items-center gap-2">
|
||
<a-progress :percent="record.progress" :stroke-color="record.status === '紧急' ? '#ff4d4f' : record.status === '延期' ? '#faad14' : '#52c41a'" size="small" :show-info="false" />
|
||
<span class="text-sm">{{ record.progress }}%</span>
|
||
</div>
|
||
</template>
|
||
</a-table-column>
|
||
<a-table-column title="状态" data-index="status">
|
||
<template #default="{ record }">
|
||
<a-tag :color="record.status === '紧急' ? 'red' : record.status === '延期' ? 'orange' : 'green'">
|
||
{{ record.status }}
|
||
</a-tag>
|
||
</template>
|
||
</a-table-column>
|
||
<a-table-column title="截止日" data-index="deadline">
|
||
<template #default="{ record }">
|
||
<span class="text-sm">{{ record.deadline }}</span>
|
||
</template>
|
||
</a-table-column>
|
||
</a-table>
|
||
</a-card>
|
||
|
||
<!-- 今日任务 -->
|
||
<a-card class="mt-6">
|
||
<template #title>
|
||
<div class="flex items-center justify-between">
|
||
<span>✅ 今日任务</span>
|
||
<a-space>
|
||
<a-button type="link" size="small" @click="navigateTo('/oa/tasks')">查看所有</a-button>
|
||
<a-button type="primary" size="small" @click="showAddTaskModal = true">+ 新建</a-button>
|
||
</a-space>
|
||
</div>
|
||
</template>
|
||
<div class="space-y-3">
|
||
<div v-for="task in todaysTasks" :key="task.id"
|
||
class="task-item"
|
||
:class="{ completed: task.completed }"
|
||
@click="toggleTask(task.id)">
|
||
<div class="task-checkbox">
|
||
<a-checkbox :checked="task.completed" />
|
||
</div>
|
||
<div class="task-details flex-1">
|
||
<div class="task-title">{{ task.title }}</div>
|
||
<div class="task-meta">
|
||
<span class="task-project">{{ task.project }}</span>
|
||
<span class="task-due">截止: {{ task.dueDate }}</span>
|
||
<span class="task-assignee">负责人: {{ task.assignee }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="task-priority">
|
||
<a-tag :color="task.priority === 'high' ? 'red' : task.priority === 'medium' ? 'orange' : 'green'" size="small">
|
||
{{ task.priority === 'high' ? '高' : task.priority === 'medium' ? '中' : '低' }}
|
||
</a-tag>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-card>
|
||
</div>
|
||
|
||
<!-- 右侧:团队协作 -->
|
||
<div class="space-y-6">
|
||
<!-- 团队状态 -->
|
||
<a-card>
|
||
<template #title>
|
||
<div class="flex items-center justify-between">
|
||
<span>👥 团队成员</span>
|
||
<span class="text-sm text-gray-500">{{ onlineMembers }}/{{ totalMembers }} 在线</span>
|
||
</div>
|
||
</template>
|
||
<div class="space-y-3">
|
||
<div v-for="member in teamMembers" :key="member.id" class="flex items-center justify-between">
|
||
<div class="flex items-center gap-3">
|
||
<div class="relative">
|
||
<a-avatar :src="member.avatar" :size="32" />
|
||
<div class="status-indicator" :class="member.status"></div>
|
||
</div>
|
||
<div>
|
||
<div class="font-medium">{{ member.name }}</div>
|
||
<div class="text-xs text-gray-500">{{ member.role }}</div>
|
||
</div>
|
||
</div>
|
||
<div class="flex items-center gap-2">
|
||
<span class="text-sm text-gray-500">{{ member.tasks }} 任务</span>
|
||
<a-button type="link" size="small" @click="startChat(member.id)">聊天</a-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-card>
|
||
|
||
<!-- 文档协作 -->
|
||
<a-card>
|
||
<template #title>
|
||
<div class="flex items-center justify-between">
|
||
<span>📄 最近文档</span>
|
||
<a-button type="link" size="small" @click="navigateTo('/oa/documents')">查看所有</a-button>
|
||
</div>
|
||
</template>
|
||
<div class="space-y-3">
|
||
<div v-for="doc in recentDocuments" :key="doc.id"
|
||
class="document-item"
|
||
@click="openDocument(doc.id)">
|
||
<div class="flex items-center gap-3">
|
||
<div class="document-icon" :class="'type-' + doc.type">
|
||
{{ doc.type === 'doc' ? '📝' : doc.type === 'sheet' ? '📊' : doc.type === 'slide' ? '📽️' : '📋' }}
|
||
</div>
|
||
<div class="flex-1 min-w-0">
|
||
<div class="truncate font-medium">{{ doc.title }}</div>
|
||
<div class="text-xs text-gray-500">
|
||
{{ doc.owner }} · {{ doc.lastModified }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-if="doc.collaborators > 0" class="text-xs text-gray-500">
|
||
+{{ doc.collaborators }} 人在协作
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-card>
|
||
|
||
<!-- 今日会议 -->
|
||
<a-card>
|
||
<template #title>
|
||
<div class="flex items-center justify-between">
|
||
<span>🗓️ 今日会议</span>
|
||
<a-button type="link" size="small" @click="navigateTo('/oa/meetings')">查看所有</a-button>
|
||
</div>
|
||
</template>
|
||
<div class="space-y-3">
|
||
<div v-for="meeting in todaysMeetings" :key="meeting.id" class="meeting-item">
|
||
<div class="flex items-start gap-3">
|
||
<div class="flex-shrink-0">
|
||
<div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center text-blue-600">
|
||
{{ meeting.type === 'team' ? '👥' : meeting.type === 'client' ? '🤝' : '💬' }}
|
||
</div>
|
||
</div>
|
||
<div class="flex-1">
|
||
<div class="font-medium">{{ meeting.title }}</div>
|
||
<div class="text-xs text-gray-500 mt-1">
|
||
{{ meeting.time }} · {{ meeting.duration }}
|
||
</div>
|
||
<div class="text-xs text-gray-500">
|
||
{{ meeting.participants }} 人参加
|
||
</div>
|
||
</div>
|
||
<a-button type="link" size="small" @click="joinMeeting(meeting.id)">加入</a-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-card>
|
||
|
||
<!-- 公告 -->
|
||
<a-card>
|
||
<template #title>
|
||
<div class="flex items-center justify-between">
|
||
<span>📢 最新公告</span>
|
||
<a-button type="link" size="small" @click="navigateTo('/oa/announcements')">查看所有</a-button>
|
||
</div>
|
||
</template>
|
||
<div class="space-y-3">
|
||
<div v-for="ann in recentAnnouncements" :key="ann.id" class="announcement-item">
|
||
<div class="flex items-start gap-3">
|
||
<div class="announcement-avatar">
|
||
<a-avatar :src="ann.avatar" :size="24" />
|
||
</div>
|
||
<div class="flex-1">
|
||
<div class="font-medium">{{ ann.title }}</div>
|
||
<div class="text-xs text-gray-500 mt-1">{{ ann.author }} · {{ ann.time }}</div>
|
||
</div>
|
||
</div>
|
||
<div class="announcement-content text-sm text-gray-600 mt-2">
|
||
{{ ann.content }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-card>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 添加任务模态框 -->
|
||
<a-modal v-model:open="showAddTaskModal" title="新建任务" :footer="null" width="400px">
|
||
<a-form :model="newTask" layout="vertical">
|
||
<a-form-item label="任务标题">
|
||
<a-input v-model:value="newTask.title" placeholder="请输入任务标题" />
|
||
</a-form-item>
|
||
<a-form-item label="优先级">
|
||
<a-select v-model:value="newTask.priority" placeholder="请选择优先级">
|
||
<a-select-option value="high">高</a-select-option>
|
||
<a-select-option value="medium">中</a-select-option>
|
||
<a-select-option value="low">低</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
<a-form-item label="截止日期">
|
||
<a-date-picker v-model:value="newTask.dueDate" placeholder="选择截止日期" />
|
||
</a-form-item>
|
||
<a-form-item label="分配给">
|
||
<a-select v-model:value="newTask.assignee" placeholder="选择负责人">
|
||
<a-select-option v-for="member in teamMembers" :key="member.id" :value="member.name">
|
||
{{ member.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
<a-form-item label="所属项目">
|
||
<a-select v-model:value="newTask.project" placeholder="选择项目">
|
||
<a-select-option v-for="project in activeProjects" :key="project.id" :value="project.name">
|
||
{{ project.name }}
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
<a-form-item>
|
||
<a-button type="primary" block @click="addNewTask">创建任务</a-button>
|
||
</a-form-item>
|
||
</a-form>
|
||
</a-modal>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { CheckOutlined } from '@ant-design/icons-vue'
|
||
|
||
useHead({ title: '协同办公 - 概览仪表板 · 葳溯科技' })
|
||
definePageMeta({ layout: 'oa' })
|
||
|
||
interface Project {
|
||
id: number
|
||
name: string
|
||
progress: number
|
||
tasks: number
|
||
completedTasks: number
|
||
deadline: string
|
||
status: '正常' | '延期' | '紧急'
|
||
owner: string
|
||
color: string
|
||
}
|
||
|
||
interface TeamMember {
|
||
id: number
|
||
name: string
|
||
avatar: string
|
||
role: string
|
||
status: '在线' | '忙碌' | '离开' | '离线'
|
||
tasks: number
|
||
}
|
||
|
||
interface QuickTask {
|
||
id: number
|
||
title: string
|
||
priority: 'high' | 'medium' | 'low'
|
||
dueDate: string
|
||
assignee: string
|
||
project: string
|
||
completed: boolean
|
||
}
|
||
|
||
interface Document {
|
||
id: number
|
||
title: string
|
||
type: 'doc' | 'sheet' | 'slide' | 'board'
|
||
owner: string
|
||
lastModified: string
|
||
collaborators: number
|
||
}
|
||
|
||
// 快捷操作
|
||
const quickActions = [
|
||
{ icon: '📝', label: '新建文档', desc: '文档/表格/幻灯片', color: '#1890ff', to: '/oa/documents/create' },
|
||
{ icon: '📋', label: '新建任务', desc: '添加待办事项', color: '#52c41a', to: '/oa/tasks/create' },
|
||
{ icon: '🗓️', label: '安排会议', desc: '团队/客户会议', color: '#722ed1', to: '/oa/meetings/create' },
|
||
{ icon: '🚀', label: '立项申请', desc: '启动新项目', color: '#fa8c16', to: '/oa/projects/create' },
|
||
]
|
||
|
||
// 活动项目数据
|
||
const activeProjects = ref<Project[]>([
|
||
{ id: 1, name: '智慧园区管理系统', progress: 85, tasks: 42, completedTasks: 36, deadline: '2024-10-30', status: '正常', owner: '张三', color: '#1890ff' },
|
||
{ id: 2, name: '客户关系管理升级', progress: 45, tasks: 28, completedTasks: 13, deadline: '2024-11-15', status: '延期', owner: '李四', color: '#52c41a' },
|
||
{ id: 3, name: '移动端App开发', progress: 92, tasks: 35, completedTasks: 32, deadline: '2024-10-25', status: '正常', owner: '王五', color: '#722ed1' },
|
||
{ id: 4, name: '数据中心建设', progress: 68, tasks: 56, completedTasks: 38, deadline: '2024-11-05', status: '紧急', owner: '赵六', color: '#fa8c16' },
|
||
])
|
||
|
||
// 今日任务
|
||
const todaysTasks = ref<QuickTask[]>([
|
||
{ id: 1, title: '完成项目需求文档撰写', priority: 'high', dueDate: '2024-10-20 18:00', assignee: '张三', project: '智慧园区管理系统', completed: false },
|
||
{ id: 2, title: '客户演示PPT设计', priority: 'medium', dueDate: '2024-10-20 15:00', assignee: '李四', project: '客户关系管理升级', completed: true },
|
||
{ id: 3, title: 'API接口联调测试', priority: 'high', dueDate: '2024-10-20 17:00', assignee: '王五', project: '移动端App开发', completed: false },
|
||
{ id: 4, title: '服务器运维巡检', priority: 'low', dueDate: '2024-10-20 12:00', assignee: '赵六', project: '数据中心建设', completed: false },
|
||
{ id: 5, title: '团队周会纪要整理', priority: 'medium', dueDate: '2024-10-20 16:00', assignee: '张三', project: '日常', completed: false },
|
||
])
|
||
|
||
// 团队成员
|
||
const teamMembers = ref<TeamMember[]>([
|
||
{ id: 1, name: '张三', avatar: 'https://randomuser.me/api/portraits/men/32.jpg', role: '产品经理', status: '在线', tasks: 8 },
|
||
{ id: 2, name: '李四', avatar: 'https://randomuser.me/api/portraits/women/44.jpg', role: 'UI设计师', status: '在线', tasks: 5 },
|
||
{ id: 3, name: '王五', avatar: 'https://randomuser.me/api/portraits/men/67.jpg', role: '前端工程师', status: '忙碌', tasks: 12 },
|
||
{ id: 4, name: '赵六', avatar: 'https://randomuser.me/api/portraits/women/23.jpg', role: '后端工程师', status: '在线', tasks: 9 },
|
||
{ id: 5, name: '钱七', avatar: 'https://randomuser.me/api/portraits/men/89.jpg', role: '测试工程师', status: '离开', tasks: 7 },
|
||
{ id: 6, name: '孙八', avatar: 'https://randomuser.me/api/portraits/women/56.jpg', role: '运维工程师', status: '离线', tasks: 4 },
|
||
])
|
||
|
||
// 最近文档
|
||
const recentDocuments = ref<Document[]>([
|
||
{ id: 1, title: '产品需求文档_V3.2', type: 'doc', owner: '张三', lastModified: '今天 10:30', collaborators: 3 },
|
||
{ id: 2, title: 'Q3季度销售报表', type: 'sheet', owner: '李四', lastModified: '今天 09:15', collaborators: 5 },
|
||
{ id: 3, title: '项目启动会简报', type: 'slide', owner: '王五', lastModified: '昨天 16:45', collaborators: 2 },
|
||
{ id: 4, title: '技术架构讨论纪要', type: 'doc', owner: '赵六', lastModified: '昨天 14:20', collaborators: 4 },
|
||
])
|
||
|
||
// 今日会议
|
||
const todaysMeetings = ref([
|
||
{ id: 1, title: '产品设计评审会', type: 'team', time: '14:00-15:00', duration: '1小时', participants: 8 },
|
||
{ id: 2, title: '客户演示会议', type: 'client', time: '16:30-17:30', duration: '1小时', participants: 6 },
|
||
{ id: 3, title: '技术方案讨论会', type: 'team', time: '11:00-12:00', duration: '1小时', participants: 5 },
|
||
])
|
||
|
||
// 最新公告
|
||
const recentAnnouncements = ref([
|
||
{ id: 1, title: '关于启用新的绩效考核制度', content: '自下月起,公司正式启用新的季度绩效考核制度,请各位同事认真学习相关文档。', author: '人力资源部', avatar: 'https://randomuser.me/api/portraits/men/12.jpg', time: '今天 09:00' },
|
||
{ id: 2, title: '国庆节放假通知', content: '根据国家法定节假日安排,公司将于10月1日至7日放假,10月8日正常上班。', author: '行政部', avatar: 'https://randomuser.me/api/portraits/women/34.jpg', time: '昨天 17:30' },
|
||
])
|
||
|
||
// 在线成员统计
|
||
const onlineMembers = computed(() => teamMembers.value.filter(m => m.status === '在线' || m.status === '忙碌').length)
|
||
const totalMembers = computed(() => teamMembers.value.length)
|
||
|
||
// 添加任务模态框状态
|
||
const showAddTaskModal = ref(false)
|
||
const newTask = ref({
|
||
title: '',
|
||
priority: 'medium',
|
||
dueDate: null,
|
||
assignee: '',
|
||
project: ''
|
||
})
|
||
|
||
// 方法
|
||
function toggleTask(taskId: number) {
|
||
const task = todaysTasks.value.find(t => t.id === taskId)
|
||
if (task) {
|
||
task.completed = !task.completed
|
||
}
|
||
}
|
||
|
||
function addNewTask() {
|
||
if (!newTask.value.title.trim()) {
|
||
message.error('请填写任务标题')
|
||
return
|
||
}
|
||
|
||
const newId = todaysTasks.value.length + 1
|
||
todaysTasks.value.unshift({
|
||
id: newId,
|
||
title: newTask.value.title,
|
||
priority: newTask.value.priority,
|
||
dueDate: newTask.value.dueDate ? newTask.value.dueDate.format('YYYY-MM-DD HH:mm') : '今天 18:00',
|
||
assignee: newTask.value.assignee || '张三',
|
||
project: newTask.value.project || '日常',
|
||
completed: false
|
||
})
|
||
|
||
message.success('任务创建成功')
|
||
showAddTaskModal.value = false
|
||
resetNewTask()
|
||
}
|
||
|
||
function resetNewTask() {
|
||
newTask.value = {
|
||
title: '',
|
||
priority: 'medium',
|
||
dueDate: null,
|
||
assignee: '',
|
||
project: ''
|
||
}
|
||
}
|
||
|
||
function startChat(userId: number) {
|
||
navigateTo(`/oa/chat?user=${userId}`)
|
||
}
|
||
|
||
function openDocument(docId: number) {
|
||
navigateTo(`/oa/documents/${docId}`)
|
||
}
|
||
|
||
function joinMeeting(meetingId: number) {
|
||
message.info(`正在加入会议 ${meetingId}`)
|
||
// 实际应用中这里是跳转到视频会议页面
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.dashboard {
|
||
min-height: calc(100vh - 140px);
|
||
}
|
||
|
||
/* 统计卡片 */
|
||
.stat-card {
|
||
background: linear-gradient(135deg, #fff 0%, #fafafa 100%);
|
||
border: 1px solid #f0f0f0;
|
||
border-radius: 12px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.stat-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||
border-color: #d9d9d9;
|
||
}
|
||
|
||
/* 快捷操作 */
|
||
.quick-action-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
border: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.quick-action-item:hover {
|
||
background-color: #fafafa;
|
||
border-color: #d9d9d9;
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.quick-action-icon {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 20px;
|
||
flex-shrink: 0;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.quick-action-text {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* 任务项 */
|
||
.task-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
border: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.task-item:hover {
|
||
background-color: #fafafa;
|
||
border-color: #d9d9d9;
|
||
}
|
||
|
||
.task-item.completed {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.task-item.completed .task-title {
|
||
text-decoration: line-through;
|
||
color: #999;
|
||
}
|
||
|
||
.task-checkbox {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.task-details {
|
||
min-width: 0;
|
||
}
|
||
|
||
.task-title {
|
||
font-weight: 500;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.task-meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
font-size: 12px;
|
||
color: #666;
|
||
}
|
||
|
||
.task-project::before {
|
||
content: "#";
|
||
margin-right: 2px;
|
||
opacity: 0.6;
|
||
}
|
||
|
||
.task-due {
|
||
color: #fa8c16;
|
||
}
|
||
|
||
/* 成员状态指示器 */
|
||
.status-indicator {
|
||
position: absolute;
|
||
bottom: 0;
|
||
right: 0;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
border: 2px solid #fff;
|
||
}
|
||
|
||
.status-indicator.在线 {
|
||
background-color: #52c41a;
|
||
}
|
||
|
||
.status-indicator.忙碌 {
|
||
background-color: #fa8c16;
|
||
}
|
||
|
||
.status-indicator.离开 {
|
||
background-color: #fadb14;
|
||
}
|
||
|
||
.status-indicator.离线 {
|
||
background-color: #d9d9d9;
|
||
}
|
||
|
||
/* 文档项 */
|
||
.document-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
border: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.document-item:hover {
|
||
background-color: #fafafa;
|
||
border-color: #d9d9d9;
|
||
}
|
||
|
||
.document-icon {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 18px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.document-icon.type-doc {
|
||
background-color: #e6f7ff;
|
||
color: #1890ff;
|
||
}
|
||
|
||
.document-icon.type-sheet {
|
||
background-color: #f6ffed;
|
||
color: #52c41a;
|
||
}
|
||
|
||
.document-icon.type-slide {
|
||
background-color: #fff7e6;
|
||
color: #fa8c16;
|
||
}
|
||
|
||
.document-icon.type-board {
|
||
background-color: #f9f0ff;
|
||
color: #722ed1;
|
||
}
|
||
|
||
/* 会议项 */
|
||
.meeting-item {
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
border: 1px solid #f0f0f0;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.meeting-item:hover {
|
||
background-color: #fafafa;
|
||
border-color: #d9d9d9;
|
||
}
|
||
|
||
/* 公告项 */
|
||
.announcement-item {
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
border: 1px solid #f0f0f0;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.announcement-item:hover {
|
||
background-color: #fafafa;
|
||
border-color: #d9d9d9;
|
||
}
|
||
|
||
.announcement-avatar {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.announcement-content {
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* 响应式调整 */
|
||
@media (max-width: 768px) {
|
||
.dashboard {
|
||
padding: 12px;
|
||
}
|
||
|
||
.task-meta {
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
:deep(.ant-card) {
|
||
margin-bottom: 12px;
|
||
}
|
||
}
|
||
</style>
|