refactor(admin): 配置按域收口到「系统配置」页,Usage 只留用量观测 #5
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
import { adminDatasources, listModels, migrateKbStorage, type DatasourceKB, type MigrateKbResult } from "../api";
|
import { adminDatasources, listModels, migrateKbStorage, type DatasourceKB, type MigrateKbResult } from "../api";
|
||||||
|
|
||||||
// 数据源 & RAG:真数据(全平台知识库清单,来自 sundynix_kb/sundynix_doc)。
|
// 数据源 & RAG:真数据(全平台知识库清单,来自 sundynix_kb/sundynix_doc)。
|
||||||
@@ -71,16 +72,12 @@ export function DatasourcesPage() {
|
|||||||
<code className="text-xs font-mono font-medium text-gray-800">{activeEmbedding}</code>
|
<code className="text-xs font-mono font-medium text-gray-800">{activeEmbedding}</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<Link
|
||||||
href="#/models"
|
to="/admin/settings"
|
||||||
onClick={(e) => {
|
className="flex items-center gap-1 text-xs font-medium text-violet-600 hover:text-violet-700 hover:underline"
|
||||||
// 如果使用 hash 路由或 react-router,可正常导航。这里提示用户去模型页配置
|
|
||||||
window.location.hash = "#/models";
|
|
||||||
}}
|
|
||||||
className="text-xs text-violet-600 hover:text-violet-700 font-medium hover:underline flex items-center gap-1"
|
|
||||||
>
|
>
|
||||||
前往模型管理修改 →
|
前往「系统配置 → 模型」修改 →
|
||||||
</a>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 运维工具 (RAG 迁移) */}
|
{/* 运维工具 (RAG 迁移) */}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { ModelsPage } from "./ModelsPage";
|
||||||
|
import { BillingRules } from "../components/BillingRules";
|
||||||
|
import { TopupChannels } from "../components/TopupChannels";
|
||||||
|
import { OrderStream } from "../components/OrderStream";
|
||||||
|
|
||||||
|
// 系统配置:按「域」聚合 —— 配了什么,就在同一处看它相关的规则与数据。
|
||||||
|
// 模型 = 模型登记/激活/连通性测试 + 计费规则(token→积分汇率、硬拦截、按模型定价与权重)
|
||||||
|
// 支付 = 支付渠道配置(兑换码 / 积分包 / 微信参数)+ 订单流水(订单流、对账、人工退款)
|
||||||
|
// 「计费 & 用量」页只留纯用量观测(消耗趋势/租户排行/发放积分),不再混配置。
|
||||||
|
// 提示词有版本历史 + diff + 激活的完整工作流,保留独立页,不塞进 Tab。
|
||||||
|
|
||||||
|
type Tab = "models" | "payment";
|
||||||
|
|
||||||
|
const TABS: Array<{ key: Tab; label: string; desc: string }> = [
|
||||||
|
{ key: "models", label: "模型", desc: "模型登记与激活 · 计费规则与按模型定价" },
|
||||||
|
{ key: "payment", label: "支付", desc: "支付渠道配置 · 订单流水与对账退款" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function SettingsPage() {
|
||||||
|
const [tab, setTab] = useState<Tab>("models");
|
||||||
|
const current = TABS.find((t) => t.key === tab);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-5">
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-4 border-b border-gray-200 pb-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-base font-semibold text-gray-800">系统配置</h3>
|
||||||
|
<p className="text-xs text-gray-400">{current?.desc}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex rounded-lg border border-gray-200 bg-gray-50/50 p-1">
|
||||||
|
{TABS.map((t) => (
|
||||||
|
<button
|
||||||
|
key={t.key}
|
||||||
|
onClick={() => setTab(t.key)}
|
||||||
|
className={`rounded-md px-4 py-1.5 text-xs font-medium transition-all ${
|
||||||
|
tab === t.key ? "bg-white text-violet-700 shadow-sm" : "text-gray-500 hover:text-gray-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{t.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 模型域:先登记模型,紧接着配它的计价规则 */}
|
||||||
|
{tab === "models" && (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<ModelsPage />
|
||||||
|
<BillingRules />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 支付域:先配渠道,紧接着看这些渠道产生的订单流水与对账 */}
|
||||||
|
{tab === "payment" && (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<TopupChannels />
|
||||||
|
<OrderStream />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
||||||
import { adminUsage, listTenants, type TenantRow, type UsageReport, type UsageTenantSum, type UsageDay } from "../api";
|
import { adminUsage, listTenants, type TenantRow, type UsageReport, type UsageTenantSum, type UsageDay } from "../api";
|
||||||
import { BillingRules } from "../components/BillingRules";
|
|
||||||
import { TopupChannels } from "../components/TopupChannels";
|
|
||||||
import { OrderStream } from "../components/OrderStream";
|
|
||||||
import { GrantCreditsModal } from "../components/GrantCreditsModal";
|
import { GrantCreditsModal } from "../components/GrantCreditsModal";
|
||||||
|
|
||||||
|
|
||||||
@@ -109,14 +106,7 @@ export function UsagePage() {
|
|||||||
{/* 发放积分 Modal */}
|
{/* 发放积分 Modal */}
|
||||||
<GrantCreditsModal tenant={grantTenant} onClose={() => setGrantTenant(null)} onDone={() => void load()} />
|
<GrantCreditsModal tenant={grantTenant} onClose={() => setGrantTenant(null)} onDone={() => void load()} />
|
||||||
|
|
||||||
{/* 配置端:计费规则(改规则即对后续任务生效) */}
|
{/* 本页只做「用量」观测。模型/计价、支付渠道/订单流水均按域收口到「系统配置」页。 */}
|
||||||
<BillingRules onSaved={() => void load()} />
|
|
||||||
|
|
||||||
{/* 配置端:充值渠道(兑换码生成/台账 + 积分包定价 + 微信配置,P5.1/P5.2) */}
|
|
||||||
<TopupChannels />
|
|
||||||
|
|
||||||
{/* 观测端:充值订单流 + 对账(P5.3) */}
|
|
||||||
<OrderStream />
|
|
||||||
|
|
||||||
{/* 观测端:用量结果 */}
|
{/* 观测端:用量结果 */}
|
||||||
<div className="flex items-center gap-2 pt-1">
|
<div className="flex items-center gap-2 pt-1">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { lazy, type ReactNode } from "react";
|
|||||||
|
|
||||||
const DashboardPage = lazy(() => import("./pages/DashboardPage").then((m) => ({ default: m.DashboardPage })));
|
const DashboardPage = lazy(() => import("./pages/DashboardPage").then((m) => ({ default: m.DashboardPage })));
|
||||||
const UsagePage = lazy(() => import("./pages/UsagePage").then((m) => ({ default: m.UsagePage })));
|
const UsagePage = lazy(() => import("./pages/UsagePage").then((m) => ({ default: m.UsagePage })));
|
||||||
const ModelsPage = lazy(() => import("./pages/ModelsPage").then((m) => ({ default: m.ModelsPage })));
|
const SettingsPage = lazy(() => import("./pages/SettingsPage").then((m) => ({ default: m.SettingsPage })));
|
||||||
const DatasourcesPage = lazy(() => import("./pages/DatasourcesPage").then((m) => ({ default: m.DatasourcesPage })));
|
const DatasourcesPage = lazy(() => import("./pages/DatasourcesPage").then((m) => ({ default: m.DatasourcesPage })));
|
||||||
const StatusPage = lazy(() => import("./pages/StatusPage").then((m) => ({ default: m.StatusPage })));
|
const StatusPage = lazy(() => import("./pages/StatusPage").then((m) => ({ default: m.StatusPage })));
|
||||||
const TasksPage = lazy(() => import("./pages/TasksPage").then((m) => ({ default: m.TasksPage })));
|
const TasksPage = lazy(() => import("./pages/TasksPage").then((m) => ({ default: m.TasksPage })));
|
||||||
@@ -41,11 +41,11 @@ export const routes: RouteDef[] = [
|
|||||||
element: <UsagePage />,
|
element: <UsagePage />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "models",
|
path: "settings",
|
||||||
label: "模型",
|
label: "系统配置",
|
||||||
group: "配置",
|
group: "配置",
|
||||||
ready: true,
|
ready: true,
|
||||||
element: <ModelsPage />,
|
element: <SettingsPage />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "datasources",
|
path: "datasources",
|
||||||
|
|||||||
@@ -18,9 +18,10 @@ const ICON_MAP: Record<string, React.ReactNode> = {
|
|||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 8h6m-5 0a3 3 0 110 6m0-6V7a1 1 0 112 0v1m-1 5a1.5 1.5 0 100-3m0 3v1m0-1a1.5 1.5 0 100-3m-3-3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" d="M9 8h6m-5 0a3 3 0 110 6m0-6V7a1 1 0 112 0v1m-1 5a1.5 1.5 0 100-3m0 3v1m0-1a1.5 1.5 0 100-3m-3-3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
models: (
|
settings: (
|
||||||
<svg className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
|
<svg className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
|
<path strokeLinecap="round" strokeLinejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
datasources: (
|
datasources: (
|
||||||
|
|||||||
Reference in New Issue
Block a user