@@ -0,0 +1,40 @@
import React , { useEffect , useState } from 'react'
import { createRoot } from 'react-dom/client'
import { App , Card , ConfigProvider , Layout , Menu , Space , Statistic , Table , Tag , Typography , message } from 'antd'
import { AccountBookOutlined , ApiOutlined , DashboardOutlined , FileSearchOutlined , SettingOutlined , ShopOutlined } from '@ant-design/icons'
import axios from 'axios'
import './style.css'
const api = axios . create ( { baseURL : '/api' } )
type Site = { id : number ; site_name : string ; site_code : string ; domain : string ; status : string ; display_account_enabled : boolean ; tax_enabled : boolean ; tax_rate_bps : number }
type Order = { order_id : number ; order_no : string ; site_id : number ; status : string ; settlement_status : string ; net_paid_amount_cent : number ; payment_fee_cent : number ; service_fee_cent : number ; tax_amount_cent : number ; created_at : string }
function Dashboard() {
const [ sites , setSites ] = useState < Site [ ] > ( [ ] )
const [ orders , setOrders ] = useState < Order [ ] > ( [ ] )
useEffect ( ( ) = > { Promise . all ( [ api . get ( '/admin/distribution/sites' ) , api . get ( '/admin/distribution/orders?kind=all' ) ] ) . then ( ( [ a , b ] ) = > { setSites ( a . data . data . items ? ? [ ] ) ; setOrders ( b . data . data . items ? ? [ ] ) } ) . catch ( ( ) = > message . error ( '数据加载失败' ) ) } , [ ] )
const total = orders . reduce ( ( sum , item ) = > sum + item . net_paid_amount_cent , 0 )
return < Space direction = "vertical" size = "large" className = "page" > < Typography.Title level = { 3 } > 分 销 运 营 概 览 < / Typography.Title > < div className = "stats" > < Card > < Statistic title = "分销网站" value = { sites . length } / > < / Card > < Card > < Statistic title = "正常订单" value = { orders . length } / > < / Card > < Card > < Statistic title = "净支付金额" value = { ( total / 100 ) . toFixed ( 2 ) } suffix = "元" / > < / Card > < / div > < Card title = "最近订单" > < Table rowKey = "order_id" dataSource = { orders . slice ( 0 , 10 ) } pagination = { false } columns = { [ { title : '订单号' , dataIndex : 'order_no' } , { title : '状态' , dataIndex : 'status' , render : ( v ) = > < Tag color = { v === 'completed' ? 'green' : 'blue' } > { v } < / Tag > } , { title : '净支付' , dataIndex : 'net_paid_amount_cent' , render : ( v ) = > ` ¥ ${ ( v / 100 ) . toFixed ( 2 ) } ` } , { title : '创建时间' , dataIndex : 'created_at' } ] } / > < / Card > < / Space >
}
function Sites() {
const [ items , setItems ] = useState < Site [ ] > ( [ ] )
useEffect ( ( ) = > { api . get ( '/admin/distribution/sites' ) . then ( ( r ) = > setItems ( r . data . data . items ? ? [ ] ) ) . catch ( ( ) = > message . error ( '站点加载失败' ) ) } , [ ] )
return < Space direction = "vertical" className = "page" > < Typography.Title level = { 3 } > 分 销 网 站 < / Typography.Title > < Card > < Table rowKey = "id" dataSource = { items } columns = { [ { title : '名称' , dataIndex : 'site_name' } , { title : '编码' , dataIndex : 'site_code' } , { title : '域名' , dataIndex : 'domain' } , { title : '状态' , dataIndex : 'status' } , { title : '账号展示' , dataIndex : 'display_account_enabled' , render : ( v ) = > v ? < Tag color = "green" > 开 启 < / Tag > : < Tag > 关 闭 < / Tag > } , { title : '税费' , render : ( _ , r ) = > r . tax_enabled ? ` ${ r . tax_rate_bps / 100 } % ` : '关闭' } ] } / > < / Card > < / Space >
}
function Orders ( { kind } : { kind : 'sold' | 'receive' } ) {
const [ items , setItems ] = useState < Order [ ] > ( [ ] )
useEffect ( ( ) = > { api . get ( ` /admin/distribution/orders?kind= ${ kind } ` ) . then ( ( r ) = > setItems ( r . data . data . items ? ? [ ] ) ) . catch ( ( ) = > message . error ( '订单加载失败' ) ) } , [ kind ] )
return < Space direction = "vertical" className = "page" > < Typography.Title level = { 3 } > { kind === 'sold' ? '售出账号订单' : '分销收号订单' } < / Typography.Title > < Card > < Table rowKey = "order_id" dataSource = { items } columns = { [ { title : '订单号' , dataIndex : 'order_no' } , { title : '订单状态' , dataIndex : 'status' } , { title : '净支付' , dataIndex : 'net_paid_amount_cent' , render : ( v ) = > ` ¥ ${ ( v / 100 ) . toFixed ( 2 ) } ` } , { title : '手续费' , dataIndex : 'payment_fee_cent' , render : ( v ) = > ` ¥ ${ ( v / 100 ) . toFixed ( 2 ) } ` } , { title : '服务费' , dataIndex : 'service_fee_cent' , render : ( v ) = > ` ¥ ${ ( v / 100 ) . toFixed ( 2 ) } ` } , { title : '结算' , dataIndex : 'settlement_status' } ] } / > < / Card > < / Space >
}
function Placeholder ( { title } : { title : string } ) { return < Space direction = "vertical" className = "page" > < Typography.Title level = { 3 } > { title } < / Typography.Title > < Card > < Typography.Text type = "secondary" > 该 模 块 已 预 留 接 口 入 口 , 可 在 后 台 继 续 配 置 。 < / Typography.Text > < / Card > < / Space > }
function Shell() {
const [ key , setKey ] = useState ( 'dashboard' )
const content = key === 'dashboard' ? < Dashboard / > : key === 'sites' ? < Sites / > : key === 'sold' ? < Orders kind = "sold" / > : key === 'receive' ? < Orders kind = "receive" / > : < Placeholder title = { ( { settlements : '结算管理' , payments : '支付通道' , accounts : '账号展示' , rules : '比例与税费规则' , qrcodes : '客服组与二维码' , chats : '客服消息' } as Record < string , string > ) [ key ] ? ? '分销后台' } / >
return < Layout className = "app" > < Layout.Sider breakpoint = "lg" collapsedWidth = "0" > < div className = "brand" > HFB 分 销 后 台 < / div > < Menu theme = "dark" selectedKeys = { [ key ] } onClick = { ( e ) = > setKey ( e . key ) } items = { [ { key : 'dashboard' , icon : < DashboardOutlined / > , label : '仪表盘' } , { key : 'sites' , icon : < ShopOutlined / > , label : '分销网站' } , { key : 'sold' , icon : < FileSearchOutlined / > , label : '售出账号' } , { key : 'receive' , icon : < AccountBookOutlined / > , label : '分销收号' } , { key : 'settlements' , icon : < AccountBookOutlined / > , label : '结算管理' } , { key : 'payments' , icon : < ApiOutlined / > , label : '支付通道' } , { key : 'accounts' , icon : < SettingOutlined / > , label : '账号展示' } , { key : 'rules' , icon : < SettingOutlined / > , label : '比例与税费' } , { key : 'qrcodes' , icon : < SettingOutlined / > , label : '客服二维码' } , { key : 'chats' , icon : < SettingOutlined / > , label : '客服消息' } ] } / > < / Layout.Sider > < Layout > < Layout.Header className = "header" > < Typography.Text > 主 站 hao858 . com · 分 销 子 域 名 distribution . hao858 . com < / Typography.Text > < / Layout.Header > < Layout.Content > { content } < / Layout.Content > < / Layout > < / Layout >
}
createRoot ( document . getElementById ( 'root' ) ! ) . render ( < React.StrictMode > < ConfigProvider theme = { { token : { colorPrimary : '#1677ff' , borderRadius : 6 } } } > < App > < Shell / > < / App > < / ConfigProvider > < / React.StrictMode > )