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

326 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 active 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 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="1" :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">
<RiseOutlined class="text-blue-600 text-xl" />
</div>
<span class="text-green-500 text-sm font-medium flex items-center gap-1"><ArrowUpOutlined /> 18%</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">¥328<span class="text-lg"></span></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">
<FallOutlined class="text-orange-600 text-xl" />
</div>
<span class="text-red-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">¥238<span class="text-lg"></span></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">
<DollarOutlined 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">¥90<span class="text-lg"></span></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">
<ClockCircleOutlined class="text-purple-600 text-xl" />
</div>
<span class="text-orange-500 text-sm font-medium">待处理</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">15</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><FormOutlined class="text-blue-500" />费用报销</a-button>
<a-button><FileTextOutlined class="text-green-500" />财务报表</a-button>
</div>
<div class="flex gap-3">
<a-button><CalendarOutlined class="text-gray-500" />2026年4月</a-button>
<a-button><ExportOutlined class="text-green-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">
<UnorderedListOutlined class="text-purple-500" />
资金流水
<a-tag color="purple">本月 86 </a-tag>
</h3>
<a-input-search v-model:value="listSearchKeyword" placeholder="搜索摘要、单号..." class="w-64" />
</div>
</div>
<a-table :dataSource="billData" :columns="columns" :pagination="false">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'type'">
<a-tag :color="record.type === 'income' ? 'success' : 'error'">
{{ record.type === 'income' ? '收入' : '支出' }}
</a-tag>
</template>
<template v-if="column.key === 'amount'">
<span :class="record.type === 'income' ? 'text-green-600 font-bold' : 'text-red-600 font-bold'">
{{ record.type === 'income' ? '+' : '-' }}{{ record.amount }}
</span>
</template>
<template v-if="column.key === 'status'">
<a-tag :color="record.status === 'done' ? 'success' : 'warning'">
{{ record.status === 'done' ? '已确认' : '待审批' }}
</a-tag>
</template>
<template v-if="column.key === 'action'">
<div class="flex items-center justify-center gap-2">
<a-button type="text" size="small" class="text-blue-600"><EyeOutlined /></a-button>
<a-button type="text" size="small" class="text-green-600"><EditOutlined /></a-button>
</div>
</template>
</template>
</a-table>
<div class="p-6 border-t border-gray-100">
<div class="flex items-center justify-between">
<p class="text-sm text-gray-500">显示 1-5 86 </p>
<a-pagination v-model:current="currentPage" :total="86" :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">
<PieChartOutlined class="text-blue-500" />支出构成
</h3>
</div>
<div class="space-y-4">
<div v-for="item in expenseBreakdown" :key="item.name">
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-gray-600">{{ item.name }}</span>
<span class="text-sm font-medium text-gray-800">{{ item.amount }} ({{ item.percent }}%)</span>
</div>
<a-progress :percent="item.percent" :stroke-color="item.color" :show-info="false" />
</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">
<FileTextOutlined class="text-orange-500" />待审批报销
</h3>
<a-badge count="15" />
</div>
<div class="space-y-3">
<div v-for="r in pendingReimburse" :key="r.id" class="flex items-center gap-4 p-3 bg-orange-50 rounded-xl border border-orange-100">
<a-avatar :style="{ background: r.avatarBg }">{{ r.name[0] }}</a-avatar>
<div class="flex-1">
<p class="font-medium text-gray-800">{{ r.name }} <span class="text-gray-500 text-sm font-normal">- {{ r.category }}</span></p>
<p class="text-xs text-gray-500">{{ r.desc }}</p>
</div>
<div class="text-right">
<p class="font-bold text-red-600">¥{{ r.amount }}</p>
<div class="flex gap-1 mt-1">
<a-button type="primary" size="small" class="text-xs">通过</a-button>
<a-button size="small" class="text-xs" danger>拒绝</a-button>
</div>
</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, ArrowUpOutlined,
PlusOutlined, ExportOutlined, FilterOutlined, UnorderedListOutlined, CalendarOutlined,
DollarOutlined, ClockCircleOutlined, EyeOutlined, EditOutlined,
RiseOutlined, FallOutlined, PieChartOutlined, FileTextOutlined, FormOutlined,
BlockOutlined,
} from '@ant-design/icons-vue'
definePageMeta({ layout: 'blank' })
const searchKeyword = ref('')
const listSearchKeyword = ref('')
const currentPage = ref(1)
const activeTab = ref('资金流水')
const tabOptions = ['资金流水', '费用报销', '应收账款', '应付账款', '财务报表']
const columns = [
{ title: '凭证编号', dataIndex: 'code', key: 'code' },
{ title: '类型', key: 'type' },
{ title: '摘要', dataIndex: 'desc', key: 'desc' },
{ title: '金额', key: 'amount' },
{ title: '账户', dataIndex: 'account', key: 'account' },
{ title: '日期', dataIndex: 'date', key: 'date' },
{ title: '状态', key: 'status' },
{ title: '操作', key: 'action', align: 'center' },
]
const billData = [
{ key: '1', code: 'CW-2026-0409-001', type: 'income', desc: '客户货款回收 - 深圳华强公司', amount: '¥58,000', account: '工商银行基本户', date: '2026-04-09', status: 'done' },
{ key: '2', code: 'CW-2026-0409-002', type: 'expense', desc: '采购货款支付 - 钢材原料', amount: '¥128,000', account: '工商银行基本户', date: '2026-04-09', status: 'pending' },
{ key: '3', code: 'CW-2026-0408-005', type: 'income', desc: '产品销售款 - 广州客户', amount: '¥86,500', account: '建设银行账户', date: '2026-04-08', status: 'done' },
{ key: '4', code: 'CW-2026-0408-004', type: 'expense', desc: '员工薪资发放 - 4月工资', amount: '¥156,000', account: '工商银行基本户', date: '2026-04-08', status: 'done' },
{ key: '5', code: 'CW-2026-0407-008', type: 'expense', desc: '办公室租金 - 4月', amount: '¥25,000', account: '工商银行基本户', date: '2026-04-07', status: 'done' },
]
const expenseBreakdown = [
{ name: '人工成本', amount: '¥156万', percent: 65, color: { from: '#667eea', to: '#764ba2' } },
{ name: '原材料采购', amount: '¥45万', percent: 19, color: { from: '#11998e', to: '#38ef7d' } },
{ name: '运营费用', amount: '¥25万', percent: 11, color: { from: '#f5576c', to: '#f093fb' } },
{ name: '其他支出', amount: '¥12万', percent: 5, color: { from: '#ffecd2', to: '#fcb69f' } },
]
const pendingReimburse = [
{ id: 1, name: '张三', category: '差旅费', desc: '出差北京3天往返', amount: '2,580', avatarBg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' },
{ id: 2, name: '李四', category: '餐饮招待', desc: '客户宴请4人', amount: '1,280', avatarBg: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)' },
{ id: 3, name: '王五', category: '办公用品', desc: '文具及耗材采购', amount: '480', avatarBg: 'linear-gradient(135deg, #f5576c 0%, #f093fb 100%)' },
]
</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); }
</style>