feat(opc): 算力中心页加「我的用量」(余额+本月按模型 token/费用)

- Compute.tsx 新增用量卡片: 余额/已用(¥), 总token/费用, 按模型表(请求/token/费用, quota→¥)
- authApi 增 getOpcComputeBalance/Usage; i18n zh 补充
This commit is contained in:
Pine
2026-08-25 14:41:07 +08:00
parent c033e3f824
commit 6b5cda6a73
3 changed files with 34 additions and 0 deletions
+3
View File
@@ -252,6 +252,9 @@ export const authApi = {
getOpcComputeTokens: () => request<{ items: { id: number; name: string; key: string; created_time: number; remain_quota?: number }[] }>("/opc/compute/tokens"),
createOpcComputeToken: () => request<{ id: number; name: string; key: string }>("/opc/compute/tokens", { method: "POST", body: "{}" }),
deleteOpcComputeToken: (id: number) => request<Record<string, unknown>>(`/opc/compute/tokens/${id}`, { method: "DELETE" }),
// ---- 计量:余额 / 本月用量(按模型) ----
getOpcComputeBalance: () => request<{ id: number; quota: number; used_quota: number }>("/opc/compute/balance"),
getOpcComputeUsage: () => request<{ month: string; cost_quota: number; input_tokens: number; output_tokens: number; items: { model_name: string; requests: number; input_tokens: number; output_tokens: number; cost_quota: number }[] }>("/opc/compute/usage"),
// ---- 各业务端口工作台 ----
getEnterpriseDashboard: () => request<PortDashboardData>("/enterprise/dashboard"),
+7
View File
@@ -3751,6 +3751,13 @@
"computeInput": "输入(¥/百万)",
"computeOutput": "输出(¥/百万)",
"computeUnit": "计价",
"computeUsage": "我的用量(本月)",
"computeBalance": "当前余额",
"computeUsed": "已用",
"computeTokensTotal": "总token: {{total}}",
"computeRequests": "请求",
"computeCost": "费用",
"computeNoUsage": "本月暂无用量,使用模型后显示",
"profile": "个人资料",
"username": "用户名",
"nickname": "昵称",
+24
View File
@@ -16,6 +16,8 @@ export default function OpcCompute() {
const [models, setModels] = useState<Model[]>([]);
const [prices, setPrices] = useState<Price[]>([]);
const [tokens, setTokens] = useState<Token[]>([]);
const [balance, setBalance] = useState<{ quota: number; used_quota: number } | null>(null);
const [usage, setUsage] = useState<{ month: string; cost_quota: number; input_tokens: number; output_tokens: number; items: any[] } | null>(null);
const [loading, setLoading] = useState(false);
const load = useCallback(() => {
@@ -23,6 +25,8 @@ export default function OpcCompute() {
authApi.getOpcComputeModels().then((r) => setModels(r.items ?? [])).catch(() => {});
authApi.getOpcComputePrices().then((r) => setPrices(r.items ?? [])).catch(() => {});
authApi.getOpcComputeTokens().then((r) => setTokens(r.items ?? [])).catch(() => {});
authApi.getOpcComputeBalance().then(setBalance).catch(() => {});
authApi.getOpcComputeUsage().then(setUsage).catch(() => {});
}, []);
useEffect(() => { load(); }, [load]);
@@ -82,6 +86,26 @@ export default function OpcCompute() {
/>
</Card>
<Card title={t("role.opc.computeUsage")} style={{ marginTop: 12 }} size="small">
<Space size={24} wrap>
<div><Typography.Text type="secondary">{t("role.opc.computeBalance")}</Typography.Text>
<div className={styles.itemTitle}>¥{((balance?.quota ?? 0) / 1000000).toFixed(4)}</div></div>
<div><Typography.Text type="secondary">{t("role.opc.computeUsed")}</Typography.Text>
<div className={styles.itemTitle}>¥{((balance?.used_quota ?? 0) / 1000000).toFixed(4)}</div></div>
<div><Typography.Text type="secondary">{t("role.opc.computeTokensTotal", { total: usage?.input_tokens ?? 0 })}</Typography.Text>
<div className={styles.itemTitle}>{usage?.cost_quota != null ? `¥${(usage.cost_quota / 1000000).toFixed(4)}` : "-"}</div></div>
</Space>
{usage?.items?.length ? (
<Table size="small" rowKey="model_name" pagination={false} style={{ marginTop: 8 }} columns={[
{ title: t("role.opc.computeModel"), dataIndex: "model_name" },
{ title: t("role.opc.computeRequests"), dataIndex: "requests" },
{ title: t("role.opc.computeInput"), dataIndex: "input_tokens" },
{ title: t("role.opc.computeOutput"), dataIndex: "output_tokens" },
{ title: t("role.opc.computeCost"), dataIndex: "cost_quota", render: (v: number) => `¥${(v / 1000000).toFixed(4)}` },
]} dataSource={usage.items} />
) : <Typography.Paragraph type="secondary" style={{ marginTop: 8 }}>{t("role.opc.computeNoUsage")}</Typography.Paragraph>}
</Card>
<Row gutter={[12, 12]} style={{ marginTop: 12 }}>
<Col span={12}>
<Card title={t("role.opc.computeModels")} size="small">