授权管理新增消耗列表

This commit is contained in:
Lrd
2026-06-08 17:56:05 +08:00
parent f0971a97b4
commit f595e8dc78
4 changed files with 123 additions and 18 deletions
+1 -1
View File
@@ -21,4 +21,4 @@ video-gen-api/dist/
# 忽略特定类型文件但保留目录
# *.pyc
# !dir/*.pyc
# !dir/*.pycnode_modules/
-8
View File
@@ -13,10 +13,6 @@ import InitialReplication from './pages/InitialReplication';
import RemoveLens from './pages/RemoveLens';
import GeneratedRecord from './pages/GeneratedRecord';
import AuthorizationPage from './pages/AuthorizationPage';
import { useAuthStore } from './store/useAuthStore';
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
@@ -96,10 +92,6 @@ const App = () => {
<Route path="generated" element={<GeneratedRecord />} />
<Route path="authorization" element={<AuthorizationPage />} />
</Route>
<Route path="*" element={<Navigate to="/projects" replace />} />
</Routes>
+122 -6
View File
@@ -1,14 +1,12 @@
import React, { useEffect, useState, useLayoutEffect, useRef, useCallback } from 'react';
import { Button, Table, Checkbox, Tag, Space, message } from 'antd';
import { PlusOutlined, CheckCircleOutlined, ClockCircleOutlined, CiCircleOutlined } from '@ant-design/icons';
import { Button, Table, Checkbox, Tag, Space, message, Modal } from 'antd';
import { PlusOutlined, CheckCircleOutlined, ClockCircleOutlined, CiCircleOutlined, EyeOutlined, XOutlined } from '@ant-design/icons';
// 模拟授权数据
const mockAuthorizations = [
{ id: '1867060028363785', status: 'active', description: '用户张三的API授权' },
{ id: '1867059757929740', status: 'pending', description: '用户李四的API授权' },
{ id: '1867059808785418', status: 'active', description: '用户王五的API授权' },
];
// 状态配置
const statusConfig = {
active: { label: '已授权', color: 'green', icon: CheckCircleOutlined },
@@ -17,10 +15,27 @@ const statusConfig = {
revoked: { label: '已撤销', color: 'gray', icon: CiCircleOutlined },
};
// 模拟消耗记录数据
const mockConsumptionRecords = [
{ id: 'C001', authorizationId: '1867060028363785', amount: 100, type: 'video', description: '视频生成消耗', createdAt: '2024-01-15 10:30:00' },
{ id: 'C002', authorizationId: '1867060028363785', amount: 50, type: 'audio', description: '音频转换消耗', createdAt: '2024-01-15 11:20:00' },
{ id: 'C003', authorizationId: '1867059808785418', amount: 200, type: 'video', description: '视频生成消耗', createdAt: '2024-01-14 14:45:00' },
{ id: 'C004', authorizationId: '1867060028363785', amount: 75, type: 'image', description: '图片处理消耗', createdAt: '2024-01-14 09:15:00' },
{ id: 'C005', authorizationId: '1867059757929740', amount: 150, type: 'video', description: '视频生成消耗', createdAt: '2024-01-13 16:00:00' },
];
// 消耗类型配置
const consumptionTypeConfig = {
video: { label: '视频生成', color: 'blue' },
audio: { label: '音频转换', color: 'purple' },
image: { label: '图片处理', color: 'green' },
};
const AuthorizationPage: React.FC = () => {
const [authorizations, setAuthorizations] = useState(mockAuthorizations);
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
const [loading, setLoading] = useState(false);
const [showConsumptionModal, setShowConsumptionModal] = useState(false);
const [consumptionRecords, setConsumptionRecords] = useState(mockConsumptionRecords);
// 状态标签渲染
const renderStatus = (status: string) => {
@@ -77,6 +92,26 @@ const AuthorizationPage: React.FC = () => {
width: 140,
render: (text: string) => renderStatus(text),
},
{
title: '操作',
dataIndex: 'operation',
key: 'operation',
width: 120,
render: (_: any, record: typeof mockAuthorizations[0]) => (
<Button
type="primary"
size="small"
style={{
borderRadius: 8,
// background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
border: 'none',
}}
onClick={() => setShowConsumptionModal(true)}
>
</Button>
),
}
];
// 处理点击授权按钮
@@ -85,7 +120,6 @@ const AuthorizationPage: React.FC = () => {
message.warning('请先选择需要授权的记录');
return;
}
setLoading(true);
// 模拟授权操作
setTimeout(() => {
@@ -107,6 +141,13 @@ const AuthorizationPage: React.FC = () => {
key: item.id,
}));
// 准备消耗记录表格数据(添加序号)
const consumptionTableData = consumptionRecords.map((item, index) => ({
...item,
index: index + 1,
key: item.id,
}));
return (
<div style={{ padding: 24, minHeight: '94vh', background: '#f8fafc' }}>
{/* 页面标题 */}
@@ -118,7 +159,7 @@ const AuthorizationPage: React.FC = () => {
</div>
{/* 操作栏 */}
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12, marginBottom: 16 }}>
<Button
type="primary"
size="large"
@@ -159,6 +200,81 @@ const AuthorizationPage: React.FC = () => {
{/* <div style={{ marginTop: 16, textAlign: 'center', color: '#94a3b8', fontSize: 13 }}>
提示:勾选记录后点击"点击授权"按钮可批量授权
</div> */}
{/* 消耗记录弹窗 */}
<Modal
title="消耗记录"
open={showConsumptionModal}
onCancel={() => setShowConsumptionModal(false)}
footer={null}
width={800}
destroyOnClose={true}
>
<div style={{ padding: 16 }}>
<Table
dataSource={consumptionTableData}
columns={[
{
title: '序号',
dataIndex: 'index',
key: 'index',
width: 80,
render: (text: number) => <span style={{ color: '#94a3b8' }}>{text}</span>,
},
{
title: '消耗ID',
dataIndex: 'id',
key: 'id',
ellipsis: true,
render: (text: string) => <span style={{ fontWeight: 500, color: '#1e293b' }}>{text}</span>,
},
{
title: '授权ID',
dataIndex: 'authorizationId',
key: 'authorizationId',
ellipsis: true,
},
{
title: '消耗类型',
dataIndex: 'type',
key: 'type',
width: 120,
render: (text: string) => {
const config = consumptionTypeConfig[text as keyof typeof consumptionTypeConfig];
return <Tag color={config?.color}>{config?.label}</Tag>;
},
},
{
title: '消耗金额',
dataIndex: 'amount',
key: 'amount',
width: 120,
render: (text: number) => <span style={{ color: '#ef4444', fontWeight: 500 }}>{text} </span>,
},
{
title: '消耗描述',
dataIndex: 'description',
key: 'description',
ellipsis: true,
},
{
title: '消耗时间',
dataIndex: 'createdAt',
key: 'createdAt',
width: 160,
},
]}
pagination={{
pageSize: 10,
showSizeChanger: true,
showTotal: (total) => `${total} 条记录`,
}}
rowKey="id"
bordered={false}
scroll={{ x: 'max-content' }}
/>
</div>
</Modal>
</div>
);
};
-3
View File
@@ -154,9 +154,6 @@ const RecordsPage: React.FC = () => {
const isGenerating = generating[record.id];
const isExpanded = expandedId === record.id;
const type: any = record.genType || 'image';
return (
<div key={record.id} className="animate-slideInCard" style={{ animationDelay: `${i * 0.04}s` }}>
{/* Record summary row */}