refactor(developer-config): 移除开发者配置页面相关代码和文档

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

267
app/pages/admin/finance.vue Normal file
View File

@@ -0,0 +1,267 @@
<template>
<div class="finance-page">
<!-- 统计概览 -->
<a-row :gutter="[16, 16]" class="mb-6">
<a-col :xs="24" :sm="12" :xl="6">
<a-card class="fin-stat-card" :bordered="false">
<div class="fin-stat-inner">
<div>
<p class="fin-label">本月收入</p>
<p class="fin-value">¥ 287.5 </p>
<p class="fin-trend up"><ArrowUpOutlined /> 15.3%</p>
</div>
<div class="fin-icon green"><AccountBookOutlined /></div>
</div>
</a-card>
</a-col>
<a-col :xs="24" :sm="12" :xl="6">
<a-card class="fin-stat-card" :bordered="false">
<div class="fin-stat-inner">
<div>
<p class="fin-label">待结算</p>
<p class="fin-value">¥ 43.2 </p>
<p class="fin-trend neutral">平稳</p>
</div>
<div class="fin-icon blue"><DollarOutlined /></div>
</div>
</a-card>
</a-col>
<a-col :xs="24" :sm="12" :xl="6">
<a-card class="fin-stat-card" :bordered="false">
<div class="fin-stat-inner">
<div>
<p class="fin-label">充值总额</p>
<p class="fin-value">¥ 1,856.3 </p>
<p class="fin-trend up"><ArrowUpOutlined /> 8.6%</p>
</div>
<div class="fin-icon purple"><PayCircleOutlined /></div>
</div>
</a-card>
</a-col>
<a-col :xs="24" :sm="12" :xl="6">
<a-card class="fin-stat-card" :bordered="false">
<div class="fin-stat-inner">
<div>
<p class="fin-label">退款笔数</p>
<p class="fin-value">12 </p>
<p class="fin-trend down"><ArrowDownOutlined /> 2 </p>
</div>
<div class="fin-icon orange"><ExclamationCircleOutlined /></div>
</div>
</a-card>
</a-col>
</a-row>
<a-card :bordered="false">
<template #title>账单管理</template>
<a-tabs v-model:activeKey="activeTab">
<!-- 账单列表 -->
<a-tab-pane key="bills" tab="账单列表">
<div class="filter-bar">
<a-input-search v-model:value="searchKeyword" placeholder="企业名称、订单号..." style="width: 280px" allow-clear @search="handleSearch" />
<a-select v-model:value="filterBillType" placeholder="账单类型" style="width: 140px" allow-clear>
<a-select-option value="recharge">充值</a-select-option>
<a-select-option value="consume">消费</a-select-option>
<a-select-option value="refund">退款</a-select-option>
</a-select>
<a-select v-model:value="filterBillStatus" placeholder="支付状态" style="width: 140px" allow-clear>
<a-select-option value="paid">已支付</a-select-option>
<a-select-option value="pending">待支付</a-select-option>
<a-select-option value="failed">已失败</a-select-option>
</a-select>
<a-range-picker v-model:value="dateRange" style="width: 260px" />
<a-button @click="resetFilter">重置</a-button>
</div>
<a-table :columns="billColumns" :data-source="billData" row-key="id" :pagination="pagination">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'enterprise'">
<div class="enterprise-cell">
<a-avatar style="background: linear-gradient(135deg, #667eea, #764ba2); flex-shrink: 0" :size="32">{{ record.enterprise[0] }}</a-avatar>
<span>{{ record.enterprise }}</span>
</div>
</template>
<template v-else-if="column.key === 'amount'">
<span :class="record.type === 'refund' ? 'refund-amount' : record.type === 'consume' ? 'consume-amount' : ''">
{{ record.type === 'refund' ? '-' : record.type === 'consume' ? '-' : '+' }}¥ {{ record.amount.toLocaleString() }}
</span>
</template>
<template v-else-if="column.key === 'billType'">
<a-tag :color="billTypeColor[record.billType]">{{ billTypeMap[record.billType] }}</a-tag>
</template>
<template v-else-if="column.key === 'status'">
<a-badge :status="billStatusBadge[record.status]" :text="billStatusMap[record.status]" />
</template>
<template v-else-if="column.key === 'actions'">
<a-space>
<a-button type="link" size="small" @click="handleViewBill(record)">详情</a-button>
<a-button v-if="record.status === 'pending'" type="link" size="small" @click="handleRemind(record)">催款</a-button>
</a-space>
</template>
</template>
</a-table>
</a-tab-pane>
<!-- 充值记录 -->
<a-tab-pane key="recharge" tab="充值管理">
<div class="filter-bar">
<a-input-search v-model:value="rechargeKeyword" placeholder="企业名称、充值账号..." style="width: 280px" allow-clear />
<a-select v-model:value="rechargeChannel" placeholder="支付渠道" style="width: 140px" allow-clear>
<a-select-option value="alipay">支付宝</a-select-option>
<a-select-option value="wechat">微信支付</a-select-option>
<a-select-option value="bank">银行转账</a-select-option>
</a-select>
<a-button type="primary" @click="rechargeModalVisible = true">
<template #icon><PlusOutlined /></template>
手动充值
</a-button>
</div>
<a-table :columns="rechargeColumns" :data-source="rechargeData" row-key="id">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'enterprise'">
<span>{{ record.enterprise }}</span>
</template>
<template v-else-if="column.key === 'amount'">
<span class="income-amount">+ ¥ {{ record.amount.toLocaleString() }}</span>
</template>
<template v-else-if="column.key === 'channel'">
<a-tag>{{ channelMap[record.channel] }}</a-tag>
</template>
<template v-else-if="column.key === 'status'">
<a-badge :status="billStatusBadge[record.status]" :text="billStatusMap[record.status]" />
</template>
<template v-else-if="column.key === 'actions'">
<a-button type="link" size="small" @click="handleViewRecharge(record)">凭证</a-button>
</template>
</template>
</a-table>
</a-tab-pane>
</a-tabs>
</a-card>
<!-- 手动充值弹窗 -->
<a-modal v-model:open="rechargeModalVisible" title="手动充值" width="480px" @ok="handleRechargeSubmit">
<a-form :model="rechargeForm" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-item label="企业名称" :rules="[{ required: true, message: '请输入企业名称' }]">
<a-input v-model:value="rechargeForm.enterprise" placeholder="请输入企业名称" />
</a-form-item>
<a-form-item label="充值金额" :rules="[{ required: true, message: '请输入充值金额' }]">
<a-input-number v-model:value="rechargeForm.amount" :min="0" :precision="2" style="width: 100%" placeholder="请输入金额" />
</a-form-item>
<a-form-item label="支付渠道">
<a-select v-model:value="rechargeForm.channel">
<a-select-option value="manual">手动充值</a-select-option>
<a-select-option value="bank">银行转账</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="备注">
<a-textarea v-model:value="rechargeForm.remark" :rows="2" />
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script setup lang="ts">
import { ArrowUpOutlined, ArrowDownOutlined, AccountBookOutlined, DollarOutlined, PlusOutlined, ExclamationCircleOutlined, PayCircleOutlined } from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
definePageMeta({ layout: 'admin' })
const activeTab = ref('bills')
const searchKeyword = ref('')
const rechargeKeyword = ref('')
const filterBillType = ref<string | undefined>()
const filterBillStatus = ref<string | undefined>()
const rechargeChannel = ref<string | undefined>()
const dateRange = ref<[any, any] | null>(null)
const rechargeModalVisible = ref(false)
const rechargeForm = reactive({ enterprise: '', amount: 0, channel: 'manual', remark: '' })
const billTypeMap: Record<string, string> = { recharge: '充值', consume: '消费', refund: '退款' }
const billTypeColor: Record<string, string> = { recharge: 'green', consume: 'blue', refund: 'orange' }
const billStatusMap: Record<string, string> = { paid: '已支付', pending: '待支付', failed: '已失败' }
const billStatusBadge: Record<string, any> = { paid: 'success', pending: 'warning', failed: 'error' }
const channelMap: Record<string, string> = { alipay: '支付宝', wechat: '微信支付', bank: '银行转账', manual: '手动' }
const billColumns = [
{ title: '企业', key: 'enterprise', width: 200 },
{ title: '账单类型', key: 'billType', width: 100 },
{ title: '金额', key: 'amount', width: 140 },
{ title: '订单号', dataIndex: 'orderNo', key: 'orderNo', width: 180 },
{ title: '支付状态', key: 'status', width: 100 },
{ title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 160 },
{ title: '操作', key: 'actions', width: 120, fixed: 'right' },
]
const billData = ref([
{ id: 1, enterprise: '腾云科技有限公司', type: 'consume', billType: 'consume', amount: 12800, orderNo: 'ORD20260408001', status: 'paid', createdAt: '2026-04-08 10:30' },
{ id: 2, enterprise: '华创数据服务有限公司', type: 'recharge', billType: 'recharge', amount: 50000, orderNo: 'ORD20260408002', status: 'paid', createdAt: '2026-04-08 09:15' },
{ id: 3, enterprise: '云智科技有限公司', type: 'consume', billType: 'consume', amount: 6800, orderNo: 'ORD20260407003', status: 'pending', createdAt: '2026-04-07 16:20' },
{ id: 4, enterprise: '数智科技有限公司', type: 'refund', billType: 'refund', amount: 3200, orderNo: 'ORD20260406004', status: 'paid', createdAt: '2026-04-06 14:00' },
{ id: 5, enterprise: '万物互联有限公司', type: 'recharge', billType: 'recharge', amount: 100000, orderNo: 'ORD20260405005', status: 'paid', createdAt: '2026-04-05 11:00' },
{ id: 6, enterprise: '云帆科技有限公司', type: 'consume', billType: 'consume', amount: 9200, orderNo: 'ORD20260403006', status: 'failed', createdAt: '2026-04-03 09:30' },
])
const rechargeColumns = [
{ title: '企业', key: 'enterprise', width: 180 },
{ title: '充值金额', key: 'amount', width: 140 },
{ title: '支付渠道', key: 'channel', width: 110 },
{ title: '交易流水', dataIndex: 'flowNo', key: 'flowNo', width: 180 },
{ title: '充值时间', dataIndex: 'createdAt', key: 'createdAt', width: 160 },
{ title: '状态', key: 'status', width: 100 },
{ title: '操作', key: 'actions', width: 100 },
]
const rechargeData = ref([
{ id: 1, enterprise: '腾云科技有限公司', amount: 50000, channel: 'alipay', flowNo: 'ZF20260408001', status: 'paid', createdAt: '2026-04-08 09:15' },
{ id: 2, enterprise: '华创数据服务有限公司', amount: 100000, channel: 'bank', flowNo: 'BK20260405002', status: 'paid', createdAt: '2026-04-05 11:00' },
{ id: 3, enterprise: '云智科技有限公司', amount: 20000, channel: 'wechat', flowNo: 'WX20260403003', status: 'paid', createdAt: '2026-04-03 14:30' },
{ id: 4, enterprise: '数智科技有限公司', amount: 30000, channel: 'manual', flowNo: 'MN20260402004', status: 'paid', createdAt: '2026-04-02 10:00' },
{ id: 5, enterprise: '万物互联有限公司', amount: 50000, channel: 'alipay', flowNo: 'ZF20260328005', status: 'paid', createdAt: '2026-03-28 16:20' },
])
const pagination = reactive({ current: 1, pageSize: 10, total: 6 })
const handleSearch = () => message.info('搜索:' + searchKeyword.value)
const resetFilter = () => { searchKeyword.value = ''; filterBillType.value = undefined; filterBillStatus.value = undefined; dateRange.value = null }
const handleViewBill = (r: any) => message.info('查看账单:' + r.orderNo)
const handleRemind = (r: any) => message.success('已发送催款通知')
const handleViewRecharge = (r: any) => message.info('查看充值凭证')
const handleRechargeSubmit = () => { rechargeModalVisible.value = false; message.success('充值成功') }
</script>
<style scoped>
.mb-6 { margin-bottom: 20px; }
.fin-stat-card {
border-radius: 12px;
}
.fin-stat-inner {
display: flex;
align-items: center;
justify-content: space-between;
}
.fin-label { font-size: 13px; color: #6b7280; margin: 0 0 4px; }
.fin-value { font-size: 24px; font-weight: 700; color: #111827; margin: 0 0 4px; }
.fin-trend { font-size: 12px; margin: 0; }
.fin-trend.up { color: #22c55e; }
.fin-trend.down { color: #ef4444; }
.fin-trend.neutral { color: #6b7280; }
.fin-icon { width: 44px; height: 44px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 20px; flex-shrink: 0; }
.fin-icon.green { background: #ecfdf5; color: #10b981; }
.fin-icon.blue { background: #eff6ff; color: #3b82f6; }
.fin-icon.purple { background: #f5f3ff; color: #8b5cf6; }
.fin-icon.orange { background: #fff7ed; color: #f59e0b; }
.filter-bar {
display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap;
}
.enterprise-cell { display: flex; align-items: center; gap: 8px; }
.income-amount { color: #22c55e; font-weight: 600; }
.consume-amount { color: #ef4444; font-weight: 600; }
.refund-amount { color: #f59e0b; font-weight: 600; }
</style>