feat: 完成短剧项目空间与结构化资产创作闭环 (#38)
* feat(canvas): 短剧项目空间 - 建立项目章节与画布关联 MVP * feat(canvas): 短剧项目空间 - 完善项目资产、镜头与流程闭环 Refs #30 * fix(deploy): 本地部署 - 修复前端构建并忽略本地 PostgreSQL 配置 * feat(*): 短剧项目空间 - 完成项目、章节、资产、角色与画布创作闭环 Refs #30 --------- Co-authored-by: ddCat <ddcat666@88.com>
This commit is contained in:
@@ -11,6 +11,7 @@ type CanvasNodeSpec = {
|
||||
export const NODE_DEFAULT_SIZE = {
|
||||
[CanvasNodeType.Image]: { width: 340, height: 240, title: "New Generation" },
|
||||
[CanvasNodeType.Text]: { width: 340, height: 240, title: "Note" },
|
||||
[CanvasNodeType.Drawing]: { width: 440, height: 300, title: "绘图" },
|
||||
[CanvasNodeType.Script]: { width: 920, height: 360, title: "分镜脚本" },
|
||||
[CanvasNodeType.Skill]: { width: 360, height: 220, title: "技能" },
|
||||
[CanvasNodeType.Config]: { width: 340, height: 300, title: "生成配置" },
|
||||
@@ -28,6 +29,10 @@ export const NODE_SPECS = {
|
||||
...NODE_DEFAULT_SIZE[CanvasNodeType.Text],
|
||||
metadata: { content: "", status: "idle", fontSize: 14 },
|
||||
},
|
||||
[CanvasNodeType.Drawing]: {
|
||||
...NODE_DEFAULT_SIZE[CanvasNodeType.Drawing],
|
||||
metadata: { status: "success" },
|
||||
},
|
||||
[CanvasNodeType.Script]: {
|
||||
...NODE_DEFAULT_SIZE[CanvasNodeType.Script],
|
||||
metadata: {
|
||||
|
||||
@@ -11,21 +11,24 @@ export function CreditSymbol({ className, ...props }: ComponentProps<"span">) {
|
||||
|
||||
export type ModelCreditCost = {
|
||||
model: string;
|
||||
billingMode: "fixed_request" | "per_second";
|
||||
unitPriceMicrocredits: number;
|
||||
};
|
||||
|
||||
function modelCreditCost(modelCosts: ModelCreditCost[] | undefined, model: string) {
|
||||
const microcredits = modelCosts?.find((item) => item.model === model)?.unitPriceMicrocredits;
|
||||
return microcredits === undefined ? null : microcredits / 1_000_000;
|
||||
return modelCosts?.find((item) => item.model === model) || null;
|
||||
}
|
||||
|
||||
export function formatCredits(value: number, maximumFractionDigits = 6) {
|
||||
return (value / 1_000_000).toLocaleString("zh-CN", { maximumFractionDigits });
|
||||
}
|
||||
|
||||
export function requestCreditCost(options: { channelMode: string; modelCosts?: ModelCreditCost[]; model: string; count?: string | number }) {
|
||||
export function requestCreditCost(options: { channelMode: string; modelCosts?: ModelCreditCost[]; model: string; count?: string | number; seconds?: string | number }) {
|
||||
if (options.channelMode !== "remote") return null;
|
||||
const unitCredits = modelCreditCost(options.modelCosts, options.model);
|
||||
const count = Math.max(1, Math.floor(Math.abs(Number(options.count)) || 1));
|
||||
return unitCredits === null ? null : unitCredits * count;
|
||||
const cost = modelCreditCost(options.modelCosts, options.model);
|
||||
if (!cost) return null;
|
||||
const quantity = cost.billingMode === "per_second"
|
||||
? Math.max(1, Math.floor(Math.abs(Number(options.seconds)) || 1))
|
||||
: Math.max(1, Math.floor(Math.abs(Number(options.count)) || 1));
|
||||
return (cost.unitPriceMicrocredits / 1_000_000) * quantity;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import { BookOpenCheck, Images, ListChecks, Maximize2 } from "lucide-react";
|
||||
import { FolderKanban, Images, ListChecks, Maximize2 } from "lucide-react";
|
||||
|
||||
export const navigationTools = [
|
||||
{
|
||||
slug: "projects",
|
||||
label: "短剧创作",
|
||||
icon: FolderKanban,
|
||||
},
|
||||
{
|
||||
slug: "canvas",
|
||||
label: "我的画布",
|
||||
label: "画布",
|
||||
icon: Maximize2,
|
||||
},
|
||||
{
|
||||
slug: "tasks",
|
||||
label: "任务中心",
|
||||
label: "任务",
|
||||
icon: ListChecks,
|
||||
},
|
||||
{
|
||||
slug: "assets",
|
||||
label: "我的素材",
|
||||
label: "素材",
|
||||
icon: Images,
|
||||
},
|
||||
{
|
||||
slug: "skills",
|
||||
label: "技能库大厅",
|
||||
icon: BookOpenCheck,
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type NavigationToolSlug = (typeof navigationTools)[number]["slug"];
|
||||
|
||||
Reference in New Issue
Block a user