Merge pull request #887 from SuanmoSuanyangTechnology/feature/package_zy
feat(web): package
This commit is contained in:
14
web/src/api/package.ts
Normal file
14
web/src/api/package.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { request } from '@/utils/request'
|
||||||
|
|
||||||
|
import type { Package } from '@/views/Package/types'
|
||||||
|
|
||||||
|
export const SYS_API_PREFIX = '/sys';
|
||||||
|
// 套餐列表
|
||||||
|
export const getPackageListUrl = `${SYS_API_PREFIX}/package-plans`
|
||||||
|
export const getPackageList = (query: { category: Package['category']; status: boolean; }) => {
|
||||||
|
return request.get(getPackageListUrl, query)
|
||||||
|
}
|
||||||
|
// 获取套餐详情
|
||||||
|
export const getPackageDetail = (package_plan_id: string) => {
|
||||||
|
return request.get(`${SYS_API_PREFIX}/package-plans/${package_plan_id}`)
|
||||||
|
}
|
||||||
@@ -3016,5 +3016,69 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
|||||||
apply: 'Apply',
|
apply: 'Apply',
|
||||||
tools: 'Tools',
|
tools: 'Tools',
|
||||||
},
|
},
|
||||||
|
package: {
|
||||||
|
package: 'Package Management',
|
||||||
|
saas_personal: 'SaaS Personal',
|
||||||
|
commercial_deployment: 'Commercial Deployment',
|
||||||
|
noCommercialPackages: 'No commercial deployment packages available',
|
||||||
|
|
||||||
|
addPackage: 'Add Plan',
|
||||||
|
packageName: 'Plan Name',
|
||||||
|
packageNameZh: 'Plan Name (中文)',
|
||||||
|
packageNameEn: 'Plan Name (English)',
|
||||||
|
packageNamePlaceholder: '中文, 例如:记忆体验版',
|
||||||
|
packageNamePlaceholderEn: 'English, e.g. Memory Trial Plan',
|
||||||
|
packageCategory: 'Package Category',
|
||||||
|
price: 'Price',
|
||||||
|
pricePlaceholder: 'e.g. 0, 19, 299 or Contact Us',
|
||||||
|
billingPeriod: 'Billing Period',
|
||||||
|
monthly: 'Monthly',
|
||||||
|
yearly: 'Yearly',
|
||||||
|
permanent_free: 'Permanent Free',
|
||||||
|
local_deployment: 'Local Deployment',
|
||||||
|
coreValue: 'Core Value',
|
||||||
|
coreValueZh: 'Core Value (中文)',
|
||||||
|
coreValueEn: 'Core Value (English)',
|
||||||
|
coreValuePlaceholder: '中文, 一句话描述核心价值',
|
||||||
|
coreValuePlaceholderEn: 'EngLish, describe the core value in one sentence',
|
||||||
|
tech_support: 'Technical Support',
|
||||||
|
tech_support_zh: 'Technical Support (中文)',
|
||||||
|
tech_support_en: 'Technical Support (English)',
|
||||||
|
technicalSupportPlaceholder: '中文, 例如:社群交流、工单支持',
|
||||||
|
technicalSupportPlaceholderEn: 'English, e.g. Community support, ticket support',
|
||||||
|
sla: 'SLA & Compliance',
|
||||||
|
slaZh: 'SLA & Compliance (中文)',
|
||||||
|
slaEn: 'SLA & Compliance (English)',
|
||||||
|
slaPlaceholder: '中文, 例如:无、验证力加强+审计日志',
|
||||||
|
slaPlaceholderEn: 'English, e.g. None, dedicated compute pool + audit logs',
|
||||||
|
customPage: 'Chat Page Customization',
|
||||||
|
customPageZh: 'Chat Page Customization (中文)',
|
||||||
|
customPageEn: 'Chat Page Customization (English)',
|
||||||
|
customPagePlaceholder: '中文, 例如:LOGO定制',
|
||||||
|
customPagePlaceholderEn: 'English, e.g. Logo customization',
|
||||||
|
primaryColor: 'Primary Color',
|
||||||
|
status: 'Status',
|
||||||
|
active: 'Active',
|
||||||
|
inactive: 'Inactive',
|
||||||
|
api_ops_rate_limit: 'API OPS Rate Limit',
|
||||||
|
ops: 'req/s',
|
||||||
|
pcs: 'pcs',
|
||||||
|
GB: 'GB',
|
||||||
|
tier_level: 'Tier Level',
|
||||||
|
numberPlaceholder: 'e.g. 10',
|
||||||
|
|
||||||
|
packageDetail: 'Package Detail',
|
||||||
|
basicInfo: 'Basic Info',
|
||||||
|
featureConfig: 'Billing Unit Quota',
|
||||||
|
workspace_quota: 'Workspace Quota',
|
||||||
|
skill_quota: 'Skill Library Quota',
|
||||||
|
app_quota: 'App Quota',
|
||||||
|
knowledge_capacity_quota: 'Knowledge Base Capacity',
|
||||||
|
memory_engine_quota: 'Memory Engine Quota',
|
||||||
|
end_user_quota: 'Memorable End Users',
|
||||||
|
ontology_project_quota: 'Ontology Project',
|
||||||
|
model_quota: 'Model Quota',
|
||||||
|
editPackage: 'Edit Package',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2980,5 +2980,69 @@ export const zh = {
|
|||||||
apply: '应用',
|
apply: '应用',
|
||||||
tools: '工具',
|
tools: '工具',
|
||||||
},
|
},
|
||||||
|
package: {
|
||||||
|
package: '套餐管理',
|
||||||
|
saas_personal: 'SaaS 个人版',
|
||||||
|
commercial_deployment: '商业化部署',
|
||||||
|
noCommercialPackages: '暂无商业化部署套餐',
|
||||||
|
|
||||||
|
addPackage: '添加套餐',
|
||||||
|
packageName: '套餐名称',
|
||||||
|
packageNameZh: '套餐名称 (中文)',
|
||||||
|
packageNameEn: '套餐名称 (English)',
|
||||||
|
packageNamePlaceholder: '中文, 例如:记忆体验版',
|
||||||
|
packageNamePlaceholderEn: 'English, e.g. Memory Trial Plan',
|
||||||
|
packageCategory: '套餐分类',
|
||||||
|
price: '价格',
|
||||||
|
pricePlaceholder: '例如: 0, 19, 299 或联系我们',
|
||||||
|
billingPeriod: '计费周期',
|
||||||
|
monthly: '月',
|
||||||
|
yearly: '年',
|
||||||
|
permanent_free: '永久免费',
|
||||||
|
local_deployment: '本地化部署',
|
||||||
|
coreValue: '核心价值',
|
||||||
|
coreValueZh: '核心价值 (中文)',
|
||||||
|
coreValueEn: '核心价值 (English)',
|
||||||
|
coreValuePlaceholder: '中文, 一句话描述核心价值',
|
||||||
|
coreValuePlaceholderEn: 'EngLish, describe the core value in one sentence',
|
||||||
|
tech_support: '技术支持',
|
||||||
|
tech_support_zh: '技术支持 (中文)',
|
||||||
|
tech_support_en: '技术支持 (English)',
|
||||||
|
technicalSupportPlaceholder: '中文, 例如:社群交流、工单支持',
|
||||||
|
technicalSupportPlaceholderEn: 'English, e.g. Community support, ticket support',
|
||||||
|
sla: 'SLA与合规',
|
||||||
|
slaZh: 'SLA与合规 (中文)',
|
||||||
|
slaEn: 'SLA与合规 (English)',
|
||||||
|
slaPlaceholder: '中文, 例如:无、验证力加强+审计日志',
|
||||||
|
slaPlaceholderEn: 'English, e.g. None, dedicated compute pool + audit logs',
|
||||||
|
customPage: '对应页面个性化配置',
|
||||||
|
customPageZh: '对应页面个性化配置 (中文)',
|
||||||
|
customPageEn: '对应页面个性化配置 (English)',
|
||||||
|
customPagePlaceholder: '中文, 例如:LOGO定制',
|
||||||
|
customPagePlaceholderEn: 'English, e.g. Logo customization',
|
||||||
|
primaryColor: '主题色',
|
||||||
|
status: '状态',
|
||||||
|
active: '启用',
|
||||||
|
inactive: '停用',
|
||||||
|
api_ops_rate_limit: 'API OPS 频次',
|
||||||
|
ops: '次/秒',
|
||||||
|
pcs: '个',
|
||||||
|
GB: 'GB',
|
||||||
|
tier_level: '层级',
|
||||||
|
numberPlaceholder: '如: 10',
|
||||||
|
|
||||||
|
packageDetail: '套餐详情',
|
||||||
|
basicInfo: '基础信息',
|
||||||
|
featureConfig: '计费单元配额',
|
||||||
|
workspace_quota: '空间数量',
|
||||||
|
skill_quota: '技能库数量',
|
||||||
|
app_quota: '应用数量',
|
||||||
|
knowledge_capacity_quota: '知识库容量',
|
||||||
|
memory_engine_quota: '记忆引擎数量',
|
||||||
|
end_user_quota: '可记忆终端用户数',
|
||||||
|
ontology_project_quota: '本体工程',
|
||||||
|
model_quota: '可负载模型数量',
|
||||||
|
editPackage: '编辑套餐',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-02 16:33:11
|
* @Date: 2026-02-02 16:33:11
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-02-04 18:11:34
|
* @Last Modified time: 2026-04-13 16:53:15
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Route Configuration
|
* Route Configuration
|
||||||
@@ -76,13 +76,12 @@ const componentMap: Record<string, LazyExoticComponent<ComponentType<object>>> =
|
|||||||
SpaceManagement: lazy(() => import('@/views/SpaceManagement')),
|
SpaceManagement: lazy(() => import('@/views/SpaceManagement')),
|
||||||
ApiKeyManagement: lazy(() => import('@/views/ApiKeyManagement')),
|
ApiKeyManagement: lazy(() => import('@/views/ApiKeyManagement')),
|
||||||
EmotionEngine: lazy(() => import('@/views/EmotionEngine')),
|
EmotionEngine: lazy(() => import('@/views/EmotionEngine')),
|
||||||
StatementDetail: lazy(() => import('@/views/UserMemoryDetail/pages/StatementDetail')),
|
|
||||||
ForgetDetail: lazy(() => import('@/views/UserMemoryDetail/pages/ForgetDetail')),
|
ForgetDetail: lazy(() => import('@/views/UserMemoryDetail/pages/ForgetDetail')),
|
||||||
MemoryNodeDetail: lazy(() => import('@/views/UserMemoryDetail/pages/index')),
|
MemoryNodeDetail: lazy(() => import('@/views/UserMemoryDetail/pages/index')),
|
||||||
SelfReflectionEngine: lazy(() => import('@/views/SelfReflectionEngine')),
|
SelfReflectionEngine: lazy(() => import('@/views/SelfReflectionEngine')),
|
||||||
OrderPayment: lazy(() => import('@/views/OrderPayment')),
|
OrderPayment: lazy(() => import('@/views/OrderPayment')),
|
||||||
OrderHistory: lazy(() => import('@/views/OrderHistory')),
|
OrderHistory: lazy(() => import('@/views/OrderHistory')),
|
||||||
Pricing: lazy(() => import('@/views/Pricing')),
|
Package: lazy(() => import('@/views/Package')),
|
||||||
ToolManagement: lazy(() => import('@/views/ToolManagement')),
|
ToolManagement: lazy(() => import('@/views/ToolManagement')),
|
||||||
SpaceConfig: lazy(() => import('@/views/SpaceConfig')),
|
SpaceConfig: lazy(() => import('@/views/SpaceConfig')),
|
||||||
Ontology: lazy(() => import('@/views/Ontology')),
|
Ontology: lazy(() => import('@/views/Ontology')),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
{ "path": "/model", "element": "ModelManagement" },
|
{ "path": "/model", "element": "ModelManagement" },
|
||||||
{ "path": "/space", "element": "SpaceManagement" },
|
{ "path": "/space", "element": "SpaceManagement" },
|
||||||
{ "path": "/tool", "element": "ToolManagement" },
|
{ "path": "/tool", "element": "ToolManagement" },
|
||||||
{ "path": "/pricing", "element": "Pricing" },
|
{ "path": "/pricing", "element": "Package" },
|
||||||
{ "path": "/order-pay", "element": "OrderPayment" },
|
{ "path": "/order-pay", "element": "OrderPayment" },
|
||||||
{ "path": "/orders", "element": "OrderHistory" },
|
{ "path": "/orders", "element": "OrderHistory" },
|
||||||
{ "path": "/skills", "element": "Skills" },
|
{ "path": "/skills", "element": "Skills" },
|
||||||
@@ -48,7 +48,6 @@
|
|||||||
{ "path": "/application/config/:id", "element": "ApplicationConfig" },
|
{ "path": "/application/config/:id", "element": "ApplicationConfig" },
|
||||||
{ "path": "/application/config/:id/:source", "element": "ApplicationConfig" },
|
{ "path": "/application/config/:id/:source", "element": "ApplicationConfig" },
|
||||||
{ "path": "/user-memory/neo4j/:id", "element": "Neo4jUserMemoryDetail" },
|
{ "path": "/user-memory/neo4j/:id", "element": "Neo4jUserMemoryDetail" },
|
||||||
{ "path": "/statement/:id", "element": "StatementDetail" },
|
|
||||||
{ "path": "/user-memory/detail/:id/:type", "element": "MemoryNodeDetail" },
|
{ "path": "/user-memory/detail/:id/:type", "element": "MemoryNodeDetail" },
|
||||||
{ "path": "/ontology/:id", "element": "OntologyDetail" }
|
{ "path": "/ontology/:id", "element": "OntologyDetail" }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -421,21 +421,7 @@
|
|||||||
"display": false,
|
"display": false,
|
||||||
"level": 3,
|
"level": 3,
|
||||||
"sort": 0,
|
"sort": 0,
|
||||||
"subs": [
|
"subs": []
|
||||||
{
|
|
||||||
"id": 2211,
|
|
||||||
"parent": 221,
|
|
||||||
"code": "statementDetail",
|
|
||||||
"label": "记忆详情",
|
|
||||||
"i18nKey": "menu.statementDetail",
|
|
||||||
"path": "/statement/:id",
|
|
||||||
"enable": true,
|
|
||||||
"display": false,
|
|
||||||
"level": 4,
|
|
||||||
"sort": 0,
|
|
||||||
"subs": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 222,
|
"id": 222,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-02 16:35:15
|
* @Date: 2026-02-02 16:35:15
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-06 10:39:00
|
* @Last Modified time: 2026-04-14 14:43:54
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* HTTP Request Utility Module
|
* HTTP Request Utility Module
|
||||||
@@ -23,6 +23,7 @@ import { clearAuthData } from './auth';
|
|||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import { refreshTokenUrl, refreshToken, loginUrl, logoutUrl } from '@/api/user'
|
import { refreshTokenUrl, refreshToken, loginUrl, logoutUrl } from '@/api/user'
|
||||||
import i18n from '@/i18n'
|
import i18n from '@/i18n'
|
||||||
|
import { SYS_API_PREFIX } from '@/api/package'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard API response structure
|
* Standard API response structure
|
||||||
@@ -74,6 +75,10 @@ let requests: RequestQueueItem[] = [];
|
|||||||
// Request interceptor
|
// Request interceptor
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
|
console.log('config', config, config.url?.startsWith(SYS_API_PREFIX))
|
||||||
|
if (config.url?.startsWith(SYS_API_PREFIX)) {
|
||||||
|
config.baseURL = '';
|
||||||
|
}
|
||||||
if (!config.headers.Authorization) {
|
if (!config.headers.Authorization) {
|
||||||
const token = cookieUtils.get('authToken');
|
const token = cookieUtils.get('authToken');
|
||||||
if (token) {
|
if (token) {
|
||||||
|
|||||||
40
web/src/views/Package/constant.ts
Normal file
40
web/src/views/Package/constant.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* @Author: ZhaoYing
|
||||||
|
* @Date: 2026-04-14 11:43:57
|
||||||
|
* @Last Modified by: ZhaoYing
|
||||||
|
* @Last Modified time: 2026-04-14 14:55:20
|
||||||
|
*/
|
||||||
|
export const billingUnits = [
|
||||||
|
{
|
||||||
|
key: 'workspace_quota',
|
||||||
|
unit: 'pcs', placeholder: 'numberPlaceholder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'skill_quota',
|
||||||
|
unit: 'pcs', placeholder: 'numberPlaceholder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'app_quota',
|
||||||
|
unit: 'pcs', placeholder: 'numberPlaceholder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'knowledge_capacity_quota',
|
||||||
|
unit: 'GB', placeholder: 'numberPlaceholder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'memory_engine_quota',
|
||||||
|
unit: 'pcs', placeholder: 'numberPlaceholder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'end_user_quota',
|
||||||
|
unit: 'pcs', placeholder: 'numberPlaceholder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ontology_project_quota',
|
||||||
|
unit: 'pcs', placeholder: 'numberPlaceholder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'model_quota',
|
||||||
|
unit: 'ops', placeholder: 'numberPlaceholder',
|
||||||
|
},
|
||||||
|
]
|
||||||
145
web/src/views/Package/index.tsx
Normal file
145
web/src/views/Package/index.tsx
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* @Author: ZhaoYing
|
||||||
|
* @Date: 2026-02-25
|
||||||
|
* @Last Modified by: ZhaoYing
|
||||||
|
* @Last Modified time: 2026-04-14 14:59:11
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Package Component
|
||||||
|
*
|
||||||
|
* Package management page with:
|
||||||
|
* - Tabs for SaaS Personal and Commercial Deployment
|
||||||
|
* - Package cards showing features and pricing
|
||||||
|
* - Edit and delete actions
|
||||||
|
*
|
||||||
|
* @component
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useMemo, useState, useEffect, type FC } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { Flex, Row, Col, type SegmentedProps } from 'antd';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
import type { Package } from './types'
|
||||||
|
import { getPackageList } from '@/api/package';
|
||||||
|
import PageTabs from '@/components/PageTabs'
|
||||||
|
import { billingUnits } from './constant'
|
||||||
|
import RbCard from '@/components/RbCard/Card'
|
||||||
|
import BodyWrapper from '@/components/Empty/BodyWrapper'
|
||||||
|
import { useI18n } from '@/store/locale'
|
||||||
|
import RbButton from '@/components/RbButton'
|
||||||
|
|
||||||
|
const Package: FC = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { language } = useI18n()
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [data, setData] = useState<Package[]>([])
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useState('saas_personal');
|
||||||
|
const formatTabItems = useMemo(() => {
|
||||||
|
return ['saas_personal', 'commercial_deployment'].map(value => ({
|
||||||
|
value,
|
||||||
|
label: t(`package.${value}`),
|
||||||
|
}))
|
||||||
|
}, [t])
|
||||||
|
/** Handle tab change */
|
||||||
|
const handleChangeTab = (value: SegmentedProps['value']) => {
|
||||||
|
setActiveTab(value as string);
|
||||||
|
}
|
||||||
|
const getList = () => {
|
||||||
|
getPackageList({ category: activeTab as Package['category'], status: true })
|
||||||
|
.then(res => {
|
||||||
|
setData(res as Package[] || [])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getList()
|
||||||
|
}, [activeTab])
|
||||||
|
|
||||||
|
const getKeyWithLanguage = (key: string) => {
|
||||||
|
return (language === 'en' ? `${key}_en` : key) as keyof Package
|
||||||
|
}
|
||||||
|
/** Navigate to order history */
|
||||||
|
const goToHistory = () => {
|
||||||
|
navigate('/orders');
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex justify="space-between" className="rb:mb-4!">
|
||||||
|
<PageTabs
|
||||||
|
value={activeTab}
|
||||||
|
options={formatTabItems}
|
||||||
|
onChange={handleChangeTab}
|
||||||
|
/>
|
||||||
|
<RbButton className="rb:text-[#212332] rb:font-medium!" onClick={goToHistory}>
|
||||||
|
<div
|
||||||
|
className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/order/order.svg')]"
|
||||||
|
></div>
|
||||||
|
{t('pricing.orderHistory')}
|
||||||
|
</RbButton>
|
||||||
|
</Flex>
|
||||||
|
<BodyWrapper empty={data.length < 1}>
|
||||||
|
<Row gutter={[12, 12]} className="rb:max-h-[calc(100%-48px)]! rb:overflow-y-auto">
|
||||||
|
{data.map((pkg) => (
|
||||||
|
<Col key={pkg.id} span={8}>
|
||||||
|
<RbCard
|
||||||
|
className="rb:h-full! rb:shadow-md hover:rb:shadow-lg rb:transition-shadow"
|
||||||
|
bodyClassName="rb:p-6! rb:h-full!"
|
||||||
|
headerClassName="rb:min-h-0!"
|
||||||
|
>
|
||||||
|
<Flex vertical justify="space-between" className="rb:h-full!">
|
||||||
|
<div>
|
||||||
|
{/* Header */}
|
||||||
|
<div className="rb:text-center rb:mb-6">
|
||||||
|
<h3 className="rb:text-xl rb:font-bold rb:mb-2 rb:min-h-7" style={{ color: pkg.theme_color }}>
|
||||||
|
{String(pkg[getKeyWithLanguage('name')] ?? '')}
|
||||||
|
</h3>
|
||||||
|
<p className="rb:text-sm rb:text-gray-500 rb:mb-4 rb:min-h-5">{String(pkg[getKeyWithLanguage('core_value')] ?? '')}</p>
|
||||||
|
<div className="rb:text-4xl rb:font-bold rb:mb-2">
|
||||||
|
{pkg.billing_cycle !== 'permanent_free' && <>¥{pkg.price}</>}
|
||||||
|
{pkg.billing_cycle && <span className={clsx("", {
|
||||||
|
'rb:text-base rb:font-normal rb:text-gray-500': pkg.billing_cycle !== 'permanent_free'
|
||||||
|
})}>{pkg.billing_cycle !== 'permanent_free' && '/'}{t(`package.${pkg.billing_cycle}`)}</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Features */}
|
||||||
|
<div className="rb:space-y-3">
|
||||||
|
{billingUnits.map(({ key, unit }) => {
|
||||||
|
if (typeof pkg.quotas[key as keyof Package['quotas']] === 'number') {
|
||||||
|
return (
|
||||||
|
<div key={key} className="rb:flex rb:items-center rb:justify-between rb:text-sm">
|
||||||
|
<span className="rb:text-gray-500">{t(`package.${key}`)}</span>
|
||||||
|
<span>{pkg.quotas[key as keyof Package['quotas']]}{t(`package.${unit}`)}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
{pkg.api_ops_rate_limit &&
|
||||||
|
<div className="rb:flex rb:items-center rb:justify-between rb:text-sm">
|
||||||
|
<span className="rb:text-gray-500">{t(`package.api_ops_rate_limit`)}</span>
|
||||||
|
<span>{pkg.api_ops_rate_limit}{t('package.ops')}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{pkg.tech_support &&
|
||||||
|
<div className="rb:flex rb:items-center rb:justify-between rb:text-sm">
|
||||||
|
<span className="rb:text-gray-500">{t(`package.tech_support`)}</span>
|
||||||
|
<span>{String(pkg[getKeyWithLanguage('tech_support')] ?? '')}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
</RbCard>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
</BodyWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Package;
|
||||||
61
web/src/views/Package/types.ts
Normal file
61
web/src/views/Package/types.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* @Author: ZhaoYing
|
||||||
|
* @Date: 2026-04-14 11:35:01
|
||||||
|
* @Last Modified by: ZhaoYing
|
||||||
|
* @Last Modified time: 2026-04-14 14:28:46
|
||||||
|
*/
|
||||||
|
export interface Package {
|
||||||
|
id: string;
|
||||||
|
// 名称
|
||||||
|
name: string;
|
||||||
|
name_en: string;
|
||||||
|
// 类型
|
||||||
|
category: "saas_personal" | "commercial_deployment";
|
||||||
|
tier_level: number;
|
||||||
|
// 版本
|
||||||
|
version: string;
|
||||||
|
// 状态
|
||||||
|
status: boolean;
|
||||||
|
// 价格
|
||||||
|
price: string;
|
||||||
|
// 计费周期
|
||||||
|
billing_cycle: "monthly" | "yearly" | "permanent_free" | "local_deployment";
|
||||||
|
// 核心价值
|
||||||
|
core_value: string;
|
||||||
|
core_value_en: string;
|
||||||
|
// 技术支持
|
||||||
|
tech_support: string;
|
||||||
|
tech_support_en: string;
|
||||||
|
// SLA与合规
|
||||||
|
sla_compliance: string;
|
||||||
|
sla_compliance_en: string;
|
||||||
|
// 对话页面个性化配置
|
||||||
|
page_customization: string;
|
||||||
|
page_customization_en: string;
|
||||||
|
// API OPS 频次(次/秒)
|
||||||
|
api_ops_rate_limit: number;
|
||||||
|
// 主题色
|
||||||
|
theme_color: string;
|
||||||
|
quotas: {
|
||||||
|
// 空间数量
|
||||||
|
workspace_quota: number;
|
||||||
|
// 技能库数量
|
||||||
|
skill_quota: number;
|
||||||
|
// 应用数量
|
||||||
|
app_quota: number;
|
||||||
|
// 知识库容量
|
||||||
|
knowledge_capacity_quota: string;
|
||||||
|
// 记忆引擎数量
|
||||||
|
memory_engine_quota: number;
|
||||||
|
// 可记忆终端用户数
|
||||||
|
end_user_quota: number;
|
||||||
|
// 本体工程
|
||||||
|
ontology_project_quota: number;
|
||||||
|
// 可负载模型数量
|
||||||
|
model_quota: number;
|
||||||
|
},
|
||||||
|
created_at: number;
|
||||||
|
updated_at: number;
|
||||||
|
created_by: string;
|
||||||
|
updated_by: string | null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user