Files
赵忠林 f9e1286ab1 refactor(developer-config): 移除开发者配置页面相关代码和文档
- 删除应用配置页面及相关组件,重构路由为 /developer/config/[id].vue
- 移除开发者文档页面及其导航与样式实现
- 清理开发者侧功能完善工作日志文件
- 删除全局.gitignore配置文件,清理无用忽略规则
- 优化应用配置页面的参数读取和路由结构,解决刷新404问题
- 解决数据库配置唯一键冲突,调整保存逻辑避免重复插入
- 移除对后端配置加密字段的 secret 标记,修正加密异常问题
2026-04-09 07:35:34 +08:00

384 lines
12 KiB
Vue
Raw Permalink 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.

<script setup lang="ts">
definePageMeta({ layout: 'admin' })
// 财务统计
const financeStats = ref([
{ label: '本月收入', value: '128.5', unit: '万', icon: 'fa-arrow-down', gradient: 'from-green-500 to-teal-500', change: '+18%', up: true },
{ label: '本月支出', value: '89.5', unit: '万', icon: 'fa-arrow-up', gradient: 'from-red-500 to-pink-500', change: '+12%', up: false },
{ label: '本月利润', value: '39.0', unit: '万', icon: 'fa-chart-line', gradient: 'from-blue-500 to-purple-500', change: '+25%', up: true },
{ label: '应收账款', value: '256.8', unit: '万', icon: 'fa-wallet', gradient: 'from-orange-500 to-yellow-500', change: '-5%', up: true },
])
// 收支记录
const transactions = ref([
{ id: 'TX-2026040901', type: 'income', category: '销售收款', amount: 58000, customer: '深圳市精密机械有限公司', date: '2026-04-09', status: 'completed' },
{ id: 'TX-2026040802', type: 'expense', category: '采购付款', amount: 36000, supplier: '上海五金工具厂', date: '2026-04-08', status: 'completed' },
{ id: 'TX-2026040801', type: 'expense', category: '工资支出', amount: 125000, supplier: '人力资源部', date: '2026-04-08', status: 'completed' },
{ id: 'TX-2026040703', type: 'income', category: '销售收款', amount: 42000, customer: '东莞市金属材料公司', date: '2026-04-07', status: 'completed' },
{ id: 'TX-2026040702', type: 'expense', category: '水电费', amount: 8500, supplier: '电力公司', date: '2026-04-07', status: 'completed' },
{ id: 'TX-2026040601', type: 'income', category: '销售收款', amount: 75000, customer: '苏州液压设备厂', date: '2026-04-06', status: 'completed' },
{ id: 'TX-2026040501', type: 'expense', category: '设备维修', amount: 12000, supplier: '设备维修部', date: '2026-04-05', status: 'completed' },
{ id: 'TX-2026040502', type: 'expense', category: '办公费用', amount: 3200, supplier: '办公用品供应商', date: '2026-04-05', status: 'completed' },
])
// 应收应付
const receivables = ref([
{ customer: '深圳市精密机械有限公司', amount: 85000, dueDate: '2026-04-15', status: 'pending' },
{ customer: '东莞市金属材料公司', amount: 68000, dueDate: '2026-04-20', status: 'pending' },
{ customer: '苏州液压设备厂', amount: 42000, dueDate: '2026-04-25', status: 'overdue' },
{ customer: '广州电子科技有限公司', amount: 95000, dueDate: '2026-05-01', status: 'pending' },
])
const typeFilter = ref('all')
const searchKeyword = ref('')
const activeTab = ref('transactions')
const filteredTransactions = computed(() => {
return transactions.value.filter((item) => {
const matchType = typeFilter.value === 'all' || item.type === typeFilter.value
const matchSearch = !searchKeyword.value ||
item.id.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
item.category.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
(item.customer && item.customer.toLowerCase().includes(searchKeyword.value.toLowerCase()))
return matchType && matchSearch
})
})
</script>
<template>
<div class="finance-page">
<!-- 页面标题 -->
<div class="page-header mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">财务管理</h2>
<p class="text-gray-500 mt-1">监控收支状况管理应收账款</p>
</div>
<div class="flex gap-3">
<a-button>
<template #icon><i class="fas fa-download mr-1"></i></template>
导出报表
</a-button>
<a-button type="primary">
<template #icon><i class="fas fa-plus mr-1"></i></template>
记账
</a-button>
</div>
</div>
<!-- 统计卡片 -->
<div class="grid grid-cols-4 gap-6 mb-6">
<div
v-for="stat in financeStats"
:key="stat.label"
class="glass rounded-2xl p-6 card-hover cursor-pointer"
>
<div class="flex items-center justify-between mb-4">
<div
class="w-12 h-12 rounded-xl flex items-center justify-center text-white"
:class="`bg-gradient-to-br ${stat.gradient}`"
>
<i :class="`fas ${stat.icon}`"></i>
</div>
<span
class="text-sm font-medium"
:class="stat.up ? 'text-green-500' : 'text-red-500'"
>
<i :class="stat.up ? 'fas fa-arrow-up' : 'fas fa-arrow-down'"></i>
{{ stat.change }}
</span>
</div>
<h3 class="text-3xl font-bold text-gray-800 mb-1">
<span class="text-base text-gray-500">¥</span>{{ stat.value }}<span class="text-base text-gray-500 ml-1">{{ stat.unit }}</span>
</h3>
<p class="text-gray-500 text-sm">{{ stat.label }}</p>
</div>
</div>
<!-- Tab切换 -->
<div class="glass rounded-2xl p-4 mb-6">
<a-radio-group v-model:value="activeTab" button-style="solid">
<a-radio-button value="transactions">
<i class="fas fa-list mr-1"></i>收支记录
</a-radio-button>
<a-radio-button value="receivables">
<i class="fas fa-hand-holding-usd mr-1"></i>应收账款
</a-radio-button>
</a-radio-group>
</div>
<!-- 收支记录 -->
<div v-show="activeTab === 'transactions'" class="glass rounded-2xl p-6">
<!-- 筛选 -->
<div class="flex items-center justify-between mb-4">
<div class="flex items-center gap-3">
<span class="text-gray-600 font-medium">类型筛选</span>
<a-radio-group v-model:value="typeFilter" button-style="solid">
<a-radio-button value="all">全部</a-radio-button>
<a-radio-button value="income">收入</a-radio-button>
<a-radio-button value="expense">支出</a-radio-button>
</a-radio-group>
</div>
<a-input-search
v-model:value="searchKeyword"
placeholder="搜索单号、类别、对象..."
style="width: 280px"
allow-clear
/>
</div>
<a-table
:dataSource="filteredTransactions"
:pagination="{ pageSize: 10 }"
rowKey="id"
:scroll="{ x: 1000 }"
>
<a-table-column title="单号" dataIndex="id" width="140" />
<a-table-column title="类型" dataIndex="type" width="80" align="center">
<template #default="{ text }">
<a-tag :color="text === 'income' ? 'success' : 'error'">
{{ text === 'income' ? '收入' : '支出' }}
</a-tag>
</template>
</a-table-column>
<a-table-column title="类别" dataIndex="category" width="120" />
<a-table-column title="金额(元)" dataIndex="amount" width="130" align="right">
<template #default="{ record, text }">
<span class="font-medium" :class="record.type === 'income' ? 'text-green-600' : 'text-red-600'">
{{ record.type === 'income' ? '+' : '-' }}¥{{ text.toLocaleString() }}
</span>
</template>
</a-table-column>
<a-table-column title="对方" width="200">
<template #default="{ record }">
<span class="font-medium">{{ record.customer || record.supplier || '-' }}</span>
</template>
</a-table-column>
<a-table-column title="日期" dataIndex="date" width="120" />
<a-table-column title="状态" width="100" align="center">
<template #default>
<a-tag color="success">已完成</a-tag>
</template>
</a-table-column>
<a-table-column title="操作" width="100" align="center" fixed="right">
<template #default>
<div class="flex items-center gap-2 justify-center">
<a-button type="link" size="small" title="详情">
<i class="fas fa-eye"></i>
</a-button>
</div>
</template>
</a-table-column>
</a-table>
</div>
<!-- 应收账款 -->
<div v-show="activeTab === 'receivables'" class="glass rounded-2xl p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="font-bold text-lg text-gray-800">
<i class="fas fa-hand-holding-usd text-orange-500 mr-2"></i>
应收账款列表
</h3>
<a-button type="primary">
<template #icon><i class="fas fa-plus mr-1"></i></template>
登记收款
</a-button>
</div>
<a-table
:dataSource="receivables"
:pagination="false"
rowKey="customer"
>
<a-table-column title="客户名称" dataIndex="customer">
<template #default="{ text }">
<div class="flex items-center gap-2">
<div class="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center text-white text-xs">
<i class="fas fa-building"></i>
</div>
<span class="font-medium">{{ text }}</span>
</div>
</template>
</a-table-column>
<a-table-column title="应收金额(元)" dataIndex="amount" width="150" align="right">
<template #default="{ text }">
<span class="font-medium text-orange-600">¥{{ text.toLocaleString() }}</span>
</template>
</a-table-column>
<a-table-column title="到期日期" dataIndex="dueDate" width="140" />
<a-table-column title="状态" dataIndex="status" width="100" align="center">
<template #default="{ text }">
<a-tag v-if="text === 'pending'" color="processing">待收款</a-tag>
<a-tag v-else-if="text === 'overdue'" color="error">已逾期</a-tag>
<a-tag v-else color="success">已收款</a-tag>
</template>
</a-table-column>
<a-table-column title="操作" width="160" align="center" fixed="right">
<template #default>
<div class="flex items-center gap-2 justify-center">
<a-button type="primary" size="small" ghost>催款</a-button>
<a-button type="link" size="small">详情</a-button>
</div>
</template>
</a-table-column>
</a-table>
</div>
</div>
</template>
<style scoped>
.finance-page {
padding: 0;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.glass {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.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);
}
.grid {
display: grid;
}
.grid-cols-4 {
grid-template-columns: repeat(4, 1fr);
}
@media (max-width: 1200px) {
.grid-cols-4 {
grid-template-columns: repeat(2, 1fr);
}
}
.mb-6 {
margin-bottom: 24px;
}
.mb-4 {
margin-bottom: 16px;
}
.mr-1 {
margin-right: 4px;
}
.mr-2 {
margin-right: 8px;
}
.ml-1 {
margin-left: 4px;
}
.text-2xl {
font-size: 24px;
}
.text-3xl {
font-size: 30px;
}
.text-lg {
font-size: 18px;
}
.text-sm {
font-size: 14px;
}
.text-base {
font-size: 16px;
}
.rounded-2xl {
border-radius: 16px;
}
.rounded-lg {
border-radius: 10px;
}
.p-6 {
padding: 24px;
}
.p-4 {
padding: 16px;
}
.gap-6 {
gap: 24px;
}
.gap-3 {
gap: 12px;
}
.gap-2 {
gap: 8px;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.font-bold {
font-weight: 700;
}
.font-medium {
font-weight: 500;
}
.text-gray-800 {
color: #1f2937;
}
.text-gray-600 {
color: #4b5563;
}
.text-gray-500 {
color: #6b7280;
}
.text-green-600 {
color: #16a34a;
}
.text-red-600 {
color: #dc2626;
}
.text-orange-600 {
color: #ea580c;
}
.gap-2 {
gap: 8px;
}
</style>