Files
tiantian-system/app/pages/office.vue
赵忠林 ecdc1cc986 feat(collaboration): 添加完整协同办公模块及设备管理和文档协同页面
- 新增现代化企业协同办公系统,包含概览仪表板、项目管理、任务看板和文档协同
- 使用Vue 3、TypeScript、Nuxt.js及Ant Design Vue实现前端结构和交互
- 设计响应式布局、渐变背景及毛玻璃视觉效果,优化移动端体验
- 创建设备管理页面,实现设备台账、巡检、维修和报警管理
- 新建文档协同页面,支持文档搜索、筛选、分类显示及多种视图切换
- 配置协同办公导航及布局文件,完善模块化和组件化架构
- 修复管理页语法错误,完善演示数据和统计信息展示
- 提供新建、导入、分享、重命名和删除文档等核心操作功能
- 添加设备健康度及维修记录展示模块,方便车间设备管理和维护
- 更新.gitignore忽略输出目录,提升项目环境整洁性
2026-04-09 12:13:35 +08:00

328 lines
17 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="flex min-h-screen bg-gradient-to-br from-gray-50 to-gray-100">
<aside class="w-64 fixed h-full text-white flex flex-col sidebar">
<div class="p-6 border-b border-white/10">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-white/20 rounded-xl flex items-center justify-center">
<BlockOutlined class="text-xl" />
</div>
<div>
<h1 class="font-bold text-lg">DEMO演示系统</h1>
<p class="text-xs text-white/70">ERP 管理平台</p>
</div>
</div>
</div>
<nav class="flex-1 py-6 px-3">
<div class="space-y-1">
<NuxtLink to="/" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer">
<HomeOutlined class="text-base" /><span>工作台</span>
</NuxtLink>
<NuxtLink to="/device" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer">
<SettingOutlined class="text-base" /><span>设备管理</span>
</NuxtLink>
<NuxtLink to="/procurement" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer">
<ShoppingCartOutlined class="text-base" /><span>采购管理</span>
</NuxtLink>
<NuxtLink to="/warehouse" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer">
<InboxOutlined class="text-base" /><span>仓储物流</span>
</NuxtLink>
<NuxtLink to="/finance" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer">
<WalletOutlined class="text-base" /><span>财务管理</span>
</NuxtLink>
<NuxtLink to="/hr" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer">
<TeamOutlined class="text-base" /><span>人力资源</span>
</NuxtLink>
<NuxtLink to="/office" class="sidebar-item active flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer">
<ProjectOutlined class="text-base" /><span>协同办公</span>
</NuxtLink>
</div>
<div class="mt-8 pt-6 border-t border-white/10">
<p class="px-4 text-xs text-white/50 mb-3">个人</p>
<a href="#" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer">
<UserOutlined class="text-base" /><span>个人信息</span>
</a>
<a href="#" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer">
<SettingOutlined class="text-base" /><span>系统设置</span>
</a>
</div>
</nav>
<div class="p-4 border-t border-white/10">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-white/20 rounded-full flex items-center justify-center"><UserOutlined /></div>
<div class="flex-1">
<p class="font-medium">管理员</p>
<p class="text-xs text-white/70">超级管理员</p>
</div>
<button class="text-white/70 hover:text-white"><LogoutOutlined /></button>
</div>
</div>
</aside>
<main class="flex-1 ml-64">
<header class="bg-white/85 backdrop-blur-xl sticky top-0 z-50 px-8 py-4 border-b border-white/30">
<div class="flex items-center justify-between">
<div class="relative w-96">
<a-input v-model:value="searchKeyword" placeholder="搜索任务、文档、公告..." size="large">
<template #prefix><SearchOutlined class="text-gray-400" /></template>
</a-input>
</div>
<div class="flex items-center gap-6">
<button class="text-gray-500 hover:text-purple-600 transition-colors">
<FullscreenOutlined class="text-lg" />
</button>
<a-badge count="8" :offset="[-2, 2]">
<BellOutlined class="text-gray-500 hover:text-purple-600 transition-colors text-lg cursor-pointer" />
</a-badge>
<div class="flex items-center gap-3 pl-6 border-l border-gray-200">
<a-avatar class="bg-gradient-to-br from-purple-500 to-pink-500">A</a-avatar>
<div>
<p class="font-medium text-gray-800">Admin</p>
<p class="text-xs text-gray-500">超级管理员</p>
</div>
</div>
</div>
</div>
</header>
<div class="p-8">
<div class="mb-8">
<div class="flex items-center gap-3 mb-2">
<NuxtLink to="/" class="text-gray-500 hover:text-purple-600 transition-colors">
<ArrowLeftOutlined />
</NuxtLink>
<h2 class="text-3xl font-bold text-gray-800">协同办公</h2>
</div>
<p class="text-gray-500">管理工作任务审批流程企业公告及团队日历</p>
</div>
<!-- 数据概览 -->
<div class="grid grid-cols-4 gap-6 mb-8">
<div class="stat-card blue bg-white rounded-2xl p-6 card-hover shadow-sm">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 bg-blue-100 rounded-xl flex items-center justify-center">
<CheckSquareOutlined class="text-blue-600 text-xl" />
</div>
<span class="text-green-500 text-sm font-medium">本周</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">24</h3>
<p class="text-gray-500 text-sm">待完成任务</p>
</div>
<div class="stat-card orange bg-white rounded-2xl p-6 card-hover shadow-sm">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 bg-orange-100 rounded-xl flex items-center justify-center">
<AuditOutlined class="text-orange-600 text-xl" />
</div>
<span class="text-red-500 text-sm font-medium">紧急</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">8</h3>
<p class="text-gray-500 text-sm">待我审批</p>
</div>
<div class="stat-card green bg-white rounded-2xl p-6 card-hover shadow-sm">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 bg-green-100 rounded-xl flex items-center justify-center">
<FileTextOutlined class="text-green-600 text-xl" />
</div>
<span class="text-green-500 text-sm font-medium">本月</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">56</h3>
<p class="text-gray-500 text-sm">共享文档</p>
</div>
<div class="stat-card purple bg-white rounded-2xl p-6 card-hover shadow-sm">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 bg-purple-100 rounded-xl flex items-center justify-center">
<CalendarOutlined class="text-purple-600 text-xl" />
</div>
<span class="text-blue-500 text-sm font-medium">今日</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">3</h3>
<p class="text-gray-500 text-sm">今日会议</p>
</div>
</div>
<!-- 操作栏 -->
<div class="bg-white/85 backdrop-blur-xl rounded-2xl p-4 mb-8 shadow-sm">
<div class="flex items-center justify-between">
<div class="flex gap-3">
<a-button type="primary" class="bg-gradient-to-r from-purple-600 to-purple-700 border-0">
<PlusOutlined />新建任务
</a-button>
<a-button><FileAddOutlined class="text-blue-500" />新建文档</a-button>
<a-button><SoundOutlined class="text-orange-500" />发布公告</a-button>
</div>
<div class="flex gap-3">
<a-button><FilterOutlined class="text-gray-500" />筛选</a-button>
</div>
</div>
</div>
<!-- 标签页 -->
<div class="bg-white/85 backdrop-blur-xl rounded-2xl p-2 mb-8 inline-flex shadow-sm">
<a-segmented v-model:value="activeTab" :options="tabOptions" />
</div>
<!-- 任务列表 -->
<div class="bg-white/85 backdrop-blur-xl rounded-2xl overflow-hidden mb-8 shadow-sm">
<div class="p-6 border-b border-gray-100">
<div class="flex items-center justify-between">
<h3 class="font-bold text-lg text-gray-800 flex items-center gap-2">
<CheckSquareOutlined class="text-purple-500" />
我的任务
<a-tag color="purple">24</a-tag>
</h3>
<div class="flex gap-2">
<a-tag
v-for="f in taskFilters"
:key="f.key"
:color="activeFilter === f.key ? 'purple' : 'default'"
class="cursor-pointer"
@click="activeFilter = f.key"
>{{ f.label }}</a-tag>
</div>
</div>
</div>
<div class="p-6 space-y-3">
<div
v-for="task in tasks"
:key="task.id"
class="task-item flex items-center gap-4 p-4 bg-gray-50 rounded-xl hover:bg-purple-50 transition-all cursor-pointer"
>
<a-checkbox :checked="task.done" class="flex-shrink-0" />
<div class="flex-1">
<p :class="['font-medium', task.done ? 'line-through text-gray-400' : 'text-gray-800']">{{ task.title }}</p>
<div class="flex items-center gap-3 mt-1">
<span class="text-xs text-gray-500"><UserOutlined class="mr-1" />{{ task.assignee }}</span>
<span class="text-xs text-gray-500"><CalendarOutlined class="mr-1" />{{ task.deadline }}</span>
<span class="text-xs text-gray-500"><FolderOutlined class="mr-1" />{{ task.project }}</span>
</div>
</div>
<a-tag :color="getPriorityColor(task.priority)">{{ task.priorityText }}</a-tag>
<a-tag :color="getTaskStatusColor(task.status)">{{ task.statusText }}</a-tag>
</div>
</div>
<div class="p-4 border-t border-gray-100">
<div class="flex items-center justify-between">
<p class="text-sm text-gray-500">显示 5 24 </p>
<a-pagination v-model:current="currentPage" :total="24" :pageSize="5" show-less-items />
</div>
</div>
</div>
<!-- 底部 -->
<div class="grid grid-cols-2 gap-6">
<!-- 审批流程 -->
<div class="bg-white/85 backdrop-blur-xl rounded-2xl p-6 card-hover shadow-sm">
<div class="flex items-center justify-between mb-6">
<h3 class="font-bold text-lg text-gray-800 flex items-center gap-2">
<AuditOutlined class="text-orange-500" />待我审批
</h3>
<a-badge count="8" />
</div>
<div class="space-y-3">
<div v-for="a in approvals" :key="a.id" class="flex items-center gap-4 p-3 bg-orange-50 rounded-xl border border-orange-100">
<a-avatar :style="{ background: a.avatarBg }">{{ a.submitter[0] }}</a-avatar>
<div class="flex-1">
<p class="font-medium text-gray-800">{{ a.title }}</p>
<p class="text-xs text-gray-500">{{ a.submitter }} 提交 · {{ a.time }}</p>
</div>
<div class="flex gap-1">
<a-button type="primary" size="small">通过</a-button>
<a-button size="small" danger>拒绝</a-button>
</div>
</div>
</div>
</div>
<!-- 最新公告 -->
<div class="bg-white/85 backdrop-blur-xl rounded-2xl p-6 card-hover shadow-sm">
<div class="flex items-center justify-between mb-6">
<h3 class="font-bold text-lg text-gray-800 flex items-center gap-2">
<SoundOutlined class="text-red-500" />企业公告
</h3>
<button class="text-purple-600 text-sm font-medium hover:underline">全部公告</button>
</div>
<div class="space-y-4">
<div
v-for="notice in notices"
:key="notice.id"
:class="['p-4 rounded-xl border-l-4 cursor-pointer hover:scale-[1.02] transition-transform', notice.bgColor, notice.borderColor]"
>
<h4 class="font-medium text-gray-800 mb-1">{{ notice.title }}</h4>
<div class="flex items-center gap-4 text-xs text-gray-500">
<span><UserOutlined class="mr-1" />{{ notice.author }}</span>
<span><ClockCircleOutlined class="mr-1" />{{ notice.date }}</span>
<span><EyeOutlined class="mr-1" />{{ notice.views }} 阅读</span>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import {
HomeOutlined, SettingOutlined, ShoppingCartOutlined, InboxOutlined, WalletOutlined,
TeamOutlined, ProjectOutlined, UserOutlined, LogoutOutlined, SearchOutlined,
FullscreenOutlined, BellOutlined, ArrowLeftOutlined,
PlusOutlined, FilterOutlined, CalendarOutlined,
CheckSquareOutlined, AuditOutlined, FileTextOutlined, SoundOutlined,
EyeOutlined, ClockCircleOutlined, FolderOutlined, BlockOutlined, FileAddOutlined,
} from '@ant-design/icons-vue'
definePageMeta({ layout: 'blank' })
const searchKeyword = ref('')
const currentPage = ref(1)
const activeTab = ref('任务管理')
const activeFilter = ref('all')
const tabOptions = ['任务管理', '审批流程', '共享文档', '企业公告', '团队日历']
const taskFilters = [
{ key: 'all', label: '全部' },
{ key: 'urgent', label: '紧急' },
{ key: 'today', label: '今日截止' },
{ key: 'done', label: '已完成' },
]
const tasks = [
{ id: 1, title: '完成Q2产品需求文档评审', assignee: '张三', deadline: '2026-04-10', project: '产品迭代', priority: 'high', priorityText: '紧急', status: 'pending', statusText: '进行中', done: false },
{ id: 2, title: '优化设备管理模块性能', assignee: '李四', deadline: '2026-04-12', project: '系统优化', priority: 'medium', priorityText: '普通', status: 'pending', statusText: '进行中', done: false },
{ id: 3, title: '采购流程说明文档整理', assignee: '王五', deadline: '2026-04-09', project: '文档管理', priority: 'high', priorityText: '紧急', status: 'overdue', statusText: '已逾期', done: false },
{ id: 4, title: '完成4月份财务月报', assignee: '赵六', deadline: '2026-04-15', project: '财务管理', priority: 'medium', priorityText: '普通', status: 'pending', statusText: '未开始', done: false },
{ id: 5, title: '新员工入职材料归档', assignee: '孙七', deadline: '2026-04-08', project: '人事管理', priority: 'low', priorityText: '低', status: 'done', statusText: '已完成', done: true },
]
const approvals = [
{ id: 1, title: '差旅费报销申请 ¥2,580', submitter: '张三', time: '2小时前', avatarBg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' },
{ id: 2, title: '加班申请 - 本周六', submitter: '李四', time: '4小时前', avatarBg: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)' },
{ id: 3, title: '外出采购审批', submitter: '王五', time: '昨天', avatarBg: 'linear-gradient(135deg, #f5576c 0%, #f093fb 100%)' },
]
const notices = [
{ id: 1, title: '关于清明节放假安排的通知', author: '人事行政部', date: '2026-04-01', views: '1,258', bgColor: 'bg-red-50', borderColor: 'border-red-500' },
{ id: 2, title: '2026年第一季度经营总结会议通知', author: '总经办', date: '2026-03-28', views: '986', bgColor: 'bg-blue-50', borderColor: 'border-blue-500' },
{ id: 3, title: '新版ERP系统正式上线公告', author: '技术部', date: '2026-03-25', views: '1,056', bgColor: 'bg-green-50', borderColor: 'border-green-500' },
]
const getPriorityColor = (p: string) => ({ high: 'error', medium: 'warning', low: 'default' }[p] || 'default')
const getTaskStatusColor = (s: string) => ({ pending: 'processing', done: 'success', overdue: 'error' }[s] || 'default')
</script>
<style scoped>
.sidebar { background: linear-gradient(180deg, #667eea 0%, #764ba2 100%); }
.sidebar-item { color: white; }
.sidebar-item:hover, .sidebar-item.active { background: rgba(255,255,255,0.2); }
.card-hover { transition: all 0.3s cubic-bezier(0.4,0,0.2,1); }
.card-hover:hover { transform: translateY(-4px); box-shadow: 0 20px 40px rgba(0,0,0,0.1); }
.stat-card { position: relative; overflow: hidden; }
.stat-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 4px; }
.stat-card.blue::before { background: linear-gradient(90deg, #667eea, #764ba2); }
.stat-card.green::before { background: linear-gradient(90deg, #11998e, #38ef7d); }
.stat-card.orange::before { background: linear-gradient(90deg, #f093fb, #f5576c); }
.stat-card.purple::before { background: linear-gradient(90deg, #a8edea, #fed6e3); }
.task-item { transition: all 0.2s ease; }
</style>