Files
tiantian-system/app/pages/index.vue
赵忠林 d8c559b5b1 feat(ui): 新增“天天系统”ERP管理平台主页布局与控制台页面优化
- 为控制台首页添加页面标题动态设置
- 为应用中心页面添加页面标题动态设置
- 修改控制台布局,实现动态浏览器标签页标题更新
- 新增“天天系统”ERP管理平台主页,包含侧边栏导航、顶部栏及数据概览模块
- 实现主页搜索框、通知、语言和用户信息区域交互
- 添加欢迎区、快捷入口、最近使用应用列表及应用详情抽屉功能
- 支持小程序扫码弹窗展示和应用类型图标及颜色区分
- 优化页面样式,支持响应式布局及交互效果
- 更新Nuxt国际化重定向路径片段标识符以兼容新版本
2026-04-09 00:58:15 +08:00

497 lines
20 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="sidebar w-64 fixed h-full text-white flex flex-col">
<!-- Logo -->
<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">天天系统</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">
<a href="#" class="sidebar-item active flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer" @click.prevent="activeMenu = 'home'">
<HomeOutlined class="text-base" />
<span>工作台</span>
</a>
<a href="#" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer" @click.prevent="activeMenu = 'device'">
<SettingOutlined class="text-base" />
<span>设备管理</span>
</a>
<a href="#" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer" @click.prevent="activeMenu = 'purchase'">
<ShoppingCartOutlined class="text-base" />
<span>采购管理</span>
</a>
<a href="#" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer" @click.prevent="activeMenu = 'warehouse'">
<InboxOutlined class="text-base" />
<span>仓储物流</span>
</a>
<a href="#" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer" @click.prevent="activeMenu = 'finance'">
<WalletOutlined class="text-base" />
<span>财务管理</span>
</a>
<a href="#" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer" @click.prevent="activeMenu = 'hr'">
<TeamOutlined class="text-base" />
<span>人力资源</span>
</a>
<a href="#" class="sidebar-item flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer" @click.prevent="activeMenu = 'office'">
<ProjectOutlined class="text-base" />
<span>协同办公</span>
</a>
</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 border-b border-white/20 sticky top-0 z-50 px-8 py-4">
<div class="flex items-center justify-between">
<!-- 搜索 -->
<div class="relative w-96">
<input
v-model="searchQuery"
type="text"
placeholder="🔍 搜索设备、订单、员工、文档..."
class="w-full px-5 py-3 bg-gray-100 rounded-xl border-0 focus:ring-2 focus:ring-purple-500 focus:bg-white transition-all"
>
</div>
<!-- 右侧 -->
<div class="flex items-center gap-6">
<!-- 全屏 -->
<button class="text-gray-500 hover:text-purple-600 transition-colors" @click="toggleFullscreen">
<ExpandOutlined class="text-lg" />
</button>
<!-- 消息 -->
<a-popover trigger="click">
<template #content>
<div class="w-64">
<div class="font-medium mb-2">通知中心</div>
<div class="text-sm text-gray-500">暂无新通知</div>
</div>
</template>
<button class="relative text-gray-500 hover:text-purple-600 transition-colors">
<Badge :count="3" :offset="[-2, 2]">
<BellOutlined class="text-lg" />
</Badge>
</button>
</a-popover>
<!-- 语言 -->
<button class="text-gray-500 hover:text-purple-600 transition-colors">
<GlobalOutlined class="text-lg" />
</button>
<!-- 用户 -->
<div class="flex items-center gap-3 pl-6 border-l border-gray-200">
<div class="w-10 h-10 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl flex items-center justify-center text-white font-bold">
A
</div>
<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">
<h2 class="text-3xl font-bold text-gray-800 mb-2">
早上好管理员
</h2>
<p class="text-gray-500">今天是 {{ currentDate }}本周还有 <span class="text-purple-600 font-semibold">{{ remainingDays }}</span> 天工作日</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">
<SettingOutlined class="text-blue-600 text-xl" />
</div>
<span class="text-green-500 text-sm font-medium flex items-center gap-1"><ArrowUpOutlined /> 12%</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">128</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">
<AppstoreOutlined class="text-green-600 text-xl" />
</div>
<span class="text-green-500 text-sm font-medium flex items-center gap-1"><ArrowUpOutlined /> 8%</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">5,230</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">
<ClockCircleOutlined class="text-orange-600 text-xl" />
</div>
<span class="text-red-500 text-sm font-medium flex items-center gap-1"><ArrowUpOutlined /> 5</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">12</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">
<LineChartOutlined class="text-purple-600 text-xl" />
</div>
<span class="text-green-500 text-sm font-medium flex items-center gap-1"><ArrowUpOutlined /> 15%</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">¥89.5<span class="text-lg"></span></h3>
<p class="text-gray-500 text-sm">本月支出</p>
</div>
</div>
<!-- 中间区域 -->
<div class="grid grid-cols-3 gap-6 mb-8">
<!-- 快捷入口 -->
<div class="bg-white rounded-2xl p-6 card-hover shadow-sm">
<h3 class="font-bold text-lg text-gray-800 mb-4 flex items-center gap-2">
<BoltOutlined class="text-yellow-500" />
快捷入口
</h3>
<div class="grid grid-cols-2 gap-3">
<button
v-for="shortcut in shortcuts"
:key="shortcut.label"
class="quick-btn flex items-center gap-2 px-4 py-3 bg-gray-50 rounded-xl text-gray-700 text-sm font-medium hover:bg-purple-500 hover:text-white transition-all"
>
<component :is="shortcut.icon" :style="{ color: shortcut.color }" />
{{ shortcut.label }}
</button>
</div>
</div>
<!-- 待办事项 -->
<div class="bg-white rounded-2xl p-6 card-hover shadow-sm col-span-2">
<div class="flex items-center justify-between mb-4">
<h3 class="font-bold text-lg text-gray-800 flex items-center gap-2">
<CheckSquareOutlined class="text-red-500" />
待办事项
<span class="bg-red-100 text-red-600 px-2 py-0.5 rounded-full text-xs font-medium">5</span>
</h3>
<button class="text-purple-600 text-sm font-medium hover:underline">查看全部</button>
</div>
<div class="space-y-3">
<div
v-for="todo in todos"
:key="todo.id"
class="todo-item flex items-center gap-4 p-3 rounded-xl cursor-pointer hover:bg-purple-50 transition-all"
>
<div :class="['w-10 h-10 rounded-xl flex items-center justify-center flex-shrink-0', todo.bgColor]">
<component :is="todo.icon" :class="[todo.textColor]" />
</div>
<div class="flex-1">
<p class="font-medium text-gray-800">{{ todo.title }}</p>
<p class="text-xs text-gray-500">{{ todo.subtitle }}</p>
</div>
<span :class="['text-xs font-medium', todo.timeColor]">{{ todo.time }}</span>
</div>
</div>
</div>
</div>
<!-- 应用模块 -->
<div class="mb-8">
<div class="flex items-center justify-between mb-6">
<h3 class="font-bold text-xl text-gray-800">
<AppstoreOutlined class="text-purple-500 mr-2" />
应用模块
</h3>
<button class="text-purple-600 text-sm font-medium hover:underline">自定义模块</button>
</div>
<div class="grid grid-cols-6 gap-4">
<div
v-for="app in apps"
:key="app.name"
class="app-card bg-white rounded-2xl p-6 flex flex-col items-center justify-center card-hover cursor-pointer shadow-sm"
@click="navigateToApp(app.route)"
>
<div
class="app-icon w-16 h-16 rounded-2xl flex items-center justify-center mb-4 shadow-lg"
:style="{ background: app.gradient }"
>
<component :is="app.icon" class="text-white text-2xl" />
</div>
<h4 class="font-semibold text-gray-800 mb-1">{{ app.name }}</h4>
<p class="text-xs text-gray-500">{{ app.stats }}</p>
</div>
</div>
</div>
<!-- 底部区域 -->
<div class="grid grid-cols-2 gap-6">
<!-- 经营概览图表 -->
<div class="bg-white 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">
<BarChartOutlined class="text-blue-500" />
经营概览
</h3>
<div class="flex gap-2">
<button
v-for="period in ['本周', '本月', '本季']"
:key="period"
:class="['px-3 py-1 text-xs rounded-lg font-medium', activePeriod === period ? 'bg-purple-100 text-purple-600' : 'bg-gray-100 text-gray-600']"
@click="activePeriod = period"
>
{{ period }}
</button>
</div>
</div>
<div class="h-48 flex items-end gap-4">
<div
v-for="(bar, index) in chartData"
:key="index"
class="flex-1 flex flex-col items-center"
>
<div
class="w-full bg-gradient-to-t from-purple-500 to-purple-300 rounded-t-lg transition-all hover:opacity-80 cursor-pointer"
:style="{ height: bar + '%' }"
></div>
<span class="text-xs text-gray-500 mt-2">{{ weekDays[index] }}</span>
</div>
</div>
</div>
<!-- 最新公告 -->
<div class="bg-white 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, computed, h } from 'vue'
import {
HomeOutlined,
SettingOutlined,
ShoppingCartOutlined,
InboxOutlined,
WalletOutlined,
TeamOutlined,
ProjectOutlined,
UserOutlined,
LogoutOutlined,
ExpandOutlined,
BellOutlined,
GlobalOutlined,
ArrowUpOutlined,
CheckSquareOutlined,
BoltOutlined,
AppstoreOutlined,
BarChartOutlined,
SoundOutlined,
EyeOutlined,
ClockCircleOutlined,
LineChartOutlined,
SearchOutlined,
ShoppingOutlined,
TruckOutlined,
CalendarOutlined,
DollarOutlined,
BoxPlotOutlined,
ToolOutlined,
AuditOutlined,
ExperimentOutlined,
FileTextOutlined,
} from '@ant-design/icons-vue'
// 响应式数据
const searchQuery = ref('')
const activeMenu = ref('home')
const activePeriod = ref('本周')
// 当前日期
const currentDate = computed(() => {
const now = new Date()
return `${now.getFullYear()}${now.getMonth() + 1}${now.getDate()}日 星期${['日', '一', '二', '三', '四', '五', '六'][now.getDay()]}`
})
// 剩余工作日
const remainingDays = computed(() => {
const now = new Date()
const day = now.getDay()
if (day === 0 || day === 6) return 0
return 5 - day
})
// 快捷入口
const shortcuts = [
{ label: '设备巡检', icon: SearchOutlined, color: '#667eea' },
{ label: '采购申请', icon: ShoppingOutlined, color: '#11998e' },
{ label: '入库登记', icon: TruckOutlined, color: '#f5576c' },
{ label: '请假申请', icon: CalendarOutlined, color: '#fc8181' },
{ label: '费用报销', icon: DollarOutlined, color: '#f6ad55' },
{ label: '库存查询', icon: BoxPlotOutlined, color: '#63b3ed' },
]
// 待办事项
const todos = [
{ id: 1, title: '审批 - 张三的报销单', subtitle: '差旅费 ¥2,580.00', time: '2小时前', icon: CheckSquareOutlined, bgColor: 'bg-red-100', textColor: 'text-red-600', timeColor: 'text-red-500' },
{ id: 2, title: '巡检 - 3号车间设备待检', subtitle: '包含12台设备', time: '4小时前', icon: SearchOutlined, bgColor: 'bg-yellow-100', textColor: 'text-yellow-600', timeColor: 'text-yellow-600' },
{ id: 3, title: '采购 - 办公用品采购单', subtitle: '待审批', time: '1天前', icon: ShoppingCartOutlined, bgColor: 'bg-blue-100', textColor: 'text-blue-600', timeColor: 'text-blue-600' },
{ id: 4, title: '保养 - 空压机定期保养', subtitle: '例行保养', time: '2天前', icon: ToolOutlined, bgColor: 'bg-green-100', textColor: 'text-green-600', timeColor: 'text-green-600' },
]
// 应用模块
const apps = [
{ name: '设备管理', icon: SettingOutlined, gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', stats: '128 设备', route: '/admin/device' },
{ name: '采购管理', icon: ShoppingCartOutlined, gradient: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)', stats: '23 订单', route: '/admin/purchase' },
{ name: '仓储物流', icon: InboxOutlined, gradient: 'linear-gradient(135deg, #f5576c 0%, #f093fb 100%)', stats: '5,230 物料', route: '/admin/warehouse' },
{ name: '财务管理', icon: WalletOutlined, gradient: 'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)', stats: '¥89.5万', route: '/admin/finance' },
{ name: '人力资源', icon: TeamOutlined, gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', stats: '56 员工', route: '/admin/hr' },
{ name: '协同办公', icon: ProjectOutlined, gradient: 'linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)', stats: '8 流程', route: '/admin/office' },
]
// 图表数据
const chartData = [60, 75, 45, 90, 85, 40, 30]
const weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
// 公告
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: '新版本系统功能更新说明', author: '技术部', date: '2026-03-25', views: '756', bgColor: 'bg-green-50', borderColor: 'border-green-500' },
]
// 方法
const toggleFullscreen = () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen()
} else {
document.exitFullscreen()
}
}
const navigateToApp = (route: string) => {
navigateTo(route)
}
</script>
<style scoped>
.sidebar {
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
}
.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);
}
.app-card {
aspect-ratio: 1;
transition: all 0.3s ease;
}
.app-card:hover {
transform: scale(1.05);
}
.app-card:hover .app-icon {
transform: scale(1.1);
}
.app-icon {
transition: transform 0.3s ease;
}
.notification-badge {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
.quick-btn {
transition: all 0.2s ease;
}
.quick-btn:hover {
transform: translateX(4px);
}
</style>