算力功能
This commit is contained in:
@@ -121,13 +121,13 @@ const SelectionPopup: React.FC<SelectionPopupProps> = ({
|
||||
}
|
||||
} else {
|
||||
// 多选模式:原有的逻辑
|
||||
if (tempSelectedOptions.some(v => v.id === device.id)) {
|
||||
setTempSelectedOptions(
|
||||
tempSelectedOptions.filter(v => v.id !== device.id),
|
||||
);
|
||||
} else {
|
||||
const newSelectedOptions = [...tempSelectedOptions, device];
|
||||
setTempSelectedOptions(newSelectedOptions);
|
||||
if (tempSelectedOptions.some(v => v.id === device.id)) {
|
||||
setTempSelectedOptions(
|
||||
tempSelectedOptions.filter(v => v.id !== device.id),
|
||||
);
|
||||
} else {
|
||||
const newSelectedOptions = [...tempSelectedOptions, device];
|
||||
setTempSelectedOptions(newSelectedOptions);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,15 +34,15 @@ const PopupFooter: React.FC<PopupFooterProps> = ({
|
||||
{/* 分页栏 */}
|
||||
<div className={style.paginationRow}>
|
||||
{onSelectAll && (
|
||||
<div className={style.totalCount}>
|
||||
<Checkbox
|
||||
checked={isAllSelected}
|
||||
onChange={e => onSelectAll(e.target.checked)}
|
||||
className={style.selectAllCheckbox}
|
||||
>
|
||||
全选当前页
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div className={style.totalCount}>
|
||||
<Checkbox
|
||||
checked={isAllSelected}
|
||||
onChange={e => onSelectAll(e.target.checked)}
|
||||
className={style.selectAllCheckbox}
|
||||
>
|
||||
全选当前页
|
||||
</Checkbox>
|
||||
</div>
|
||||
)}
|
||||
<div className={style.paginationControls}>
|
||||
<Button
|
||||
|
||||
@@ -106,7 +106,7 @@ export default function ContentForm() {
|
||||
setUseAI(data.aiEnabled === 1);
|
||||
} else {
|
||||
// 兼容旧数据,默认根据是否有 aiPrompt 判断
|
||||
setUseAI(!!data.aiPrompt);
|
||||
setUseAI(!!data.aiPrompt);
|
||||
}
|
||||
setEnabled(data.status === 1);
|
||||
// 时间范围
|
||||
@@ -151,7 +151,7 @@ export default function ContentForm() {
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean),
|
||||
catchType,
|
||||
aiPrompt,
|
||||
aiPrompt,
|
||||
aiEnabled: useAI ? 1 : 0,
|
||||
timeEnabled: dateRange[0] || dateRange[1] ? 1 : 0,
|
||||
startTime: dateRange[0] ? formatDate(dateRange[0]) : "",
|
||||
@@ -347,37 +347,37 @@ export default function ContentForm() {
|
||||
|
||||
<div className={style["form-card"]}>
|
||||
<Collapse className={style["keyword-collapse"]}>
|
||||
<Collapse.Panel
|
||||
key="keywords"
|
||||
<Collapse.Panel
|
||||
key="keywords"
|
||||
title={
|
||||
<div className={style["keyword-header"]}>
|
||||
<span className={style["keyword-title"]}>关键词设置</span>
|
||||
<DownOutlined className={style["keyword-arrow"]} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>包含关键词</label>
|
||||
<TextArea
|
||||
placeholder="多个关键词用逗号分隔"
|
||||
value={keywordsInclude}
|
||||
onChange={e => setKeywordsInclude(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||
/>
|
||||
</div>
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>排除关键词</label>
|
||||
<TextArea
|
||||
placeholder="多个关键词用逗号分隔"
|
||||
value={keywordsExclude}
|
||||
onChange={e => setKeywordsExclude(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||
/>
|
||||
</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
>
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>包含关键词</label>
|
||||
<TextArea
|
||||
placeholder="多个关键词用逗号分隔"
|
||||
value={keywordsInclude}
|
||||
onChange={e => setKeywordsInclude(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||
/>
|
||||
</div>
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>排除关键词</label>
|
||||
<TextArea
|
||||
placeholder="多个关键词用逗号分隔"
|
||||
value={keywordsExclude}
|
||||
onChange={e => setKeywordsExclude(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||
/>
|
||||
</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
{/* 采集内容类型 */}
|
||||
@@ -400,38 +400,38 @@ export default function ContentForm() {
|
||||
);
|
||||
}}
|
||||
>
|
||||
{type === "text"
|
||||
? "文本"
|
||||
: type === "image"
|
||||
? "图片"
|
||||
: "视频"}
|
||||
{type === "text"
|
||||
? "文本"
|
||||
: type === "image"
|
||||
? "图片"
|
||||
: "视频"}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={style["form-card"]}>
|
||||
<div
|
||||
className={style["form-section"]}
|
||||
style={{ display: "flex", alignItems: "center", gap: 12 }}
|
||||
>
|
||||
<Switch checked={useAI} onChange={setUseAI} />
|
||||
<span className={style["ai-desc"]}>
|
||||
<div
|
||||
className={style["form-section"]}
|
||||
style={{ display: "flex", alignItems: "center", gap: 12 }}
|
||||
>
|
||||
<Switch checked={useAI} onChange={setUseAI} />
|
||||
<span className={style["ai-desc"]}>
|
||||
启用后,该内容库下的内容会通过AI生成
|
||||
</span>
|
||||
</div>
|
||||
{useAI && (
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>AI提示词</label>
|
||||
<TextArea
|
||||
</span>
|
||||
</div>
|
||||
{useAI && (
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>AI提示词</label>
|
||||
<TextArea
|
||||
placeholder="请输入AI提示词"
|
||||
value={aiPrompt}
|
||||
onChange={e => setAIPrompt(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 4, maxRows: 10 }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={style["form-card"]}>
|
||||
@@ -441,51 +441,51 @@ export default function ContentForm() {
|
||||
<div className={style["date-inputs"]}>
|
||||
<div className={style["date-item"]}>
|
||||
<label className={style["date-label"]}>开始时间</label>
|
||||
<AntdInput
|
||||
readOnly
|
||||
<AntdInput
|
||||
readOnly
|
||||
value={
|
||||
dateRange[0]
|
||||
? `${dateRange[0].getFullYear()}/${String(dateRange[0].getMonth() + 1).padStart(2, "0")}/${String(dateRange[0].getDate()).padStart(2, "0")}`
|
||||
: ""
|
||||
}
|
||||
placeholder="年/月/日"
|
||||
placeholder="年/月/日"
|
||||
className={style["date-input"]}
|
||||
onClick={() => setShowStartPicker(true)}
|
||||
/>
|
||||
<DatePicker
|
||||
visible={showStartPicker}
|
||||
title="开始时间"
|
||||
value={dateRange[0]}
|
||||
onClose={() => setShowStartPicker(false)}
|
||||
onConfirm={val => {
|
||||
setDateRange([val, dateRange[1]]);
|
||||
setShowStartPicker(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
onClick={() => setShowStartPicker(true)}
|
||||
/>
|
||||
<DatePicker
|
||||
visible={showStartPicker}
|
||||
title="开始时间"
|
||||
value={dateRange[0]}
|
||||
onClose={() => setShowStartPicker(false)}
|
||||
onConfirm={val => {
|
||||
setDateRange([val, dateRange[1]]);
|
||||
setShowStartPicker(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={style["date-item"]}>
|
||||
<label className={style["date-label"]}>结束时间</label>
|
||||
<AntdInput
|
||||
readOnly
|
||||
<AntdInput
|
||||
readOnly
|
||||
value={
|
||||
dateRange[1]
|
||||
? `${dateRange[1].getFullYear()}/${String(dateRange[1].getMonth() + 1).padStart(2, "0")}/${String(dateRange[1].getDate()).padStart(2, "0")}`
|
||||
: ""
|
||||
}
|
||||
placeholder="年/月/日"
|
||||
placeholder="年/月/日"
|
||||
className={style["date-input"]}
|
||||
onClick={() => setShowEndPicker(true)}
|
||||
/>
|
||||
<DatePicker
|
||||
visible={showEndPicker}
|
||||
title="结束时间"
|
||||
value={dateRange[1]}
|
||||
onClose={() => setShowEndPicker(false)}
|
||||
onConfirm={val => {
|
||||
setDateRange([dateRange[0], val]);
|
||||
setShowEndPicker(false);
|
||||
}}
|
||||
/>
|
||||
onClick={() => setShowEndPicker(true)}
|
||||
/>
|
||||
<DatePicker
|
||||
visible={showEndPicker}
|
||||
title="结束时间"
|
||||
value={dateRange[1]}
|
||||
onClose={() => setShowEndPicker(false)}
|
||||
onConfirm={val => {
|
||||
setDateRange([dateRange[0], val]);
|
||||
setShowEndPicker(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -493,7 +493,7 @@ export default function ContentForm() {
|
||||
<div className={style["form-card"]}>
|
||||
<div className={style["enable-section"]}>
|
||||
<span className={style["enable-label"]}>是否启用</span>
|
||||
<Switch checked={enabled} onChange={setEnabled} />
|
||||
<Switch checked={enabled} onChange={setEnabled} />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -4,7 +4,7 @@ import request from "@/api/request";
|
||||
export interface PowerPackage {
|
||||
id: number;
|
||||
name: string;
|
||||
tokens: number; // 算力点数
|
||||
tokens: number | string; // 算力点数(可能是字符串,如"2,800")
|
||||
price: number; // 价格(分)
|
||||
originalPrice: number; // 原价(分)
|
||||
unitPrice: number; // 单价
|
||||
@@ -13,7 +13,7 @@ export interface PowerPackage {
|
||||
isRecommend: number; // 是否推荐
|
||||
isHot: number; // 是否热门
|
||||
isVip: number; // 是否VIP
|
||||
features: string[]; // 功能特性
|
||||
features?: string[]; // 功能特性(可选)
|
||||
description: string[]; // 描述关键词
|
||||
status: number;
|
||||
createTime: string;
|
||||
|
||||
@@ -6,6 +6,9 @@ export interface Statistics {
|
||||
monthUsed: number; // 本月使用
|
||||
remainingTokens: number; // 剩余算力
|
||||
totalConsumed: number; // 总消耗
|
||||
yesterdayUsed?: number; // 昨日消耗
|
||||
historyConsumed?: number; // 历史消耗
|
||||
estimatedDays?: number; // 预计可用天数
|
||||
}
|
||||
// 算力统计接口
|
||||
export function getStatistics(): Promise<Statistics> {
|
||||
@@ -143,3 +146,42 @@ export function buyPackage(params: { id: number; price: number }) {
|
||||
export function buyCustomPower(params: { amount: number }) {
|
||||
return request("/v1/power/buy-custom", params, "POST");
|
||||
}
|
||||
|
||||
// 查询订单状态
|
||||
export interface QueryOrderResponse {
|
||||
id: number;
|
||||
mchId: number;
|
||||
companyId: number;
|
||||
userId: number;
|
||||
orderType: number;
|
||||
status: number; // 0: 待支付, 1: 已支付
|
||||
goodsId: number;
|
||||
goodsName: string;
|
||||
goodsSpecs: string;
|
||||
money: number;
|
||||
orderNo: string;
|
||||
payType: number | null;
|
||||
payTime: number | null;
|
||||
payInfo: any;
|
||||
createTime: number;
|
||||
}
|
||||
|
||||
export function queryOrder(orderNo: string): Promise<QueryOrderResponse> {
|
||||
return request("/v1/tokens/queryOrder", { orderNo }, "GET");
|
||||
}
|
||||
|
||||
// 账号信息
|
||||
export interface Account {
|
||||
id: number;
|
||||
userName: string;
|
||||
realName: string;
|
||||
nickname: string;
|
||||
departmentId: number;
|
||||
departmentName: string;
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
// 获取账号列表
|
||||
export function getAccountList(): Promise<{ list: Account[]; total: number }> {
|
||||
return request("/v1/kefu/accounts/list", undefined, "GET");
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@ export interface TokensUseRecordItem {
|
||||
|
||||
export interface TokensUseRecordList {
|
||||
list: TokensUseRecordItem[];
|
||||
total?: number;
|
||||
}
|
||||
|
||||
//算力使用明细
|
||||
|
||||
@@ -339,8 +339,8 @@
|
||||
height: 5px;
|
||||
border-radius: 10px 10px 0 0;
|
||||
background: #1677ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stat-icon-chat {
|
||||
width: 20px;
|
||||
@@ -700,7 +700,7 @@
|
||||
.adm-avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,13 +710,13 @@
|
||||
}
|
||||
|
||||
.friend-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.friend-name {
|
||||
.friend-name {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #111;
|
||||
@@ -727,7 +727,7 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.friend-tag {
|
||||
font-size: 11px;
|
||||
@@ -735,17 +735,17 @@
|
||||
border-radius: 999px;
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.friend-id-row {
|
||||
font-size: 12px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.friend-status-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
@@ -764,7 +764,7 @@
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.value-amount {
|
||||
font-size: 14px;
|
||||
|
||||
@@ -370,13 +370,13 @@ const ScenarioList: React.FC = () => {
|
||||
title={scenarioName || ""}
|
||||
right={
|
||||
scenarioId !== "10" ? (
|
||||
<Button
|
||||
size="small"
|
||||
color="primary"
|
||||
onClick={handleCreateNewPlan}
|
||||
>
|
||||
<PlusOutlined /> 新建计划
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
color="primary"
|
||||
onClick={handleCreateNewPlan}
|
||||
>
|
||||
<PlusOutlined /> 新建计划
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
@@ -427,13 +427,13 @@ const ScenarioList: React.FC = () => {
|
||||
{searchTerm ? "没有找到匹配的计划" : "暂无计划"}
|
||||
</div>
|
||||
{scenarioId !== "10" && (
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleCreateNewPlan}
|
||||
className={style["create-first-btn"]}
|
||||
>
|
||||
<PlusOutlined /> 创建第一个计划
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleCreateNewPlan}
|
||||
className={style["create-first-btn"]}
|
||||
>
|
||||
<PlusOutlined /> 创建第一个计划
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user