视频提词优化管理后台配置BUG修复
This commit is contained in:
-407
File diff suppressed because one or more lines are too long
+407
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -28,7 +28,7 @@
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-CYRc4RUo.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-Te6nFn2L.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -58,6 +58,17 @@ const NODE_TYPES: { label: string; value: VideoPromptSchemaNodeType }[] = [
|
||||
{ label: '流程数组', value: 'flow' },
|
||||
];
|
||||
|
||||
const VIDEO_SPEC_SECTION_KEY = '画面属性';
|
||||
const VIDEO_SPEC_LOCKED_FIELDS = new Set(['视频时长', '视频比例', '清晰度', '帧率', '推荐分辨率']);
|
||||
|
||||
function isLockedVideoSpecField(sectionKey: string | undefined, fieldKey: string | undefined): boolean {
|
||||
return sectionKey === VIDEO_SPEC_SECTION_KEY && !!fieldKey && VIDEO_SPEC_LOCKED_FIELDS.has(fieldKey);
|
||||
}
|
||||
|
||||
function isLockedVideoSpecSection(sectionKey: string | undefined): boolean {
|
||||
return sectionKey === VIDEO_SPEC_SECTION_KEY;
|
||||
}
|
||||
|
||||
function clone<T>(value: T): T {
|
||||
return JSON.parse(JSON.stringify(value ?? null));
|
||||
}
|
||||
@@ -137,17 +148,19 @@ function normalizeConfig(data: any): VideoPromptSchemaConfigData {
|
||||
};
|
||||
}
|
||||
|
||||
function toApiNode(node: VideoPromptSchemaNode): any {
|
||||
function toApiNode(node: VideoPromptSchemaNode, parentKey?: string): any {
|
||||
const lockedSection = isLockedVideoSpecSection(node.key);
|
||||
const lockedField = isLockedVideoSpecField(parentKey, node.key);
|
||||
const base: any = {
|
||||
key: node.key,
|
||||
label: node.label,
|
||||
type: node.type,
|
||||
enabled: !!node.enabled,
|
||||
editable: !!node.editable,
|
||||
type: lockedField ? 'string' : node.type,
|
||||
enabled: lockedSection || lockedField ? true : !!node.enabled,
|
||||
editable: lockedField ? false : !!node.editable,
|
||||
max_length: node.maxLength,
|
||||
};
|
||||
if (node.type === 'object') {
|
||||
base.children = (node.children || []).map(toApiNode);
|
||||
base.children = (node.children || []).map(child => toApiNode(child, node.key));
|
||||
} else if (node.type === 'flow') {
|
||||
base.content_key = node.contentKey;
|
||||
base.content_aliases = node.contentAliases || [];
|
||||
@@ -169,7 +182,7 @@ function toApiConfig(config: VideoPromptSchemaConfigData, enabled: boolean): any
|
||||
return {
|
||||
version: config.version,
|
||||
enabled,
|
||||
sections: (config.sections || []).map(toApiNode),
|
||||
sections: (config.sections || []).map(section => toApiNode(section)),
|
||||
time_plan_rules: (config.timePlanRules || []).map(rule => ({
|
||||
min_duration: rule.minDuration,
|
||||
max_duration: rule.maxDuration,
|
||||
@@ -364,6 +377,12 @@ const AdminVideoPromptSchemaConfig: React.FC = () => {
|
||||
|
||||
const updateSection = (index: number, patch: Partial<VideoPromptSchemaNode>) => {
|
||||
updateConfig(draft => {
|
||||
const current = draft.sections[index];
|
||||
if (isLockedVideoSpecSection(current?.key)) {
|
||||
delete patch.key;
|
||||
delete patch.type;
|
||||
delete patch.enabled;
|
||||
}
|
||||
draft.sections[index] = { ...draft.sections[index], ...patch };
|
||||
if (patch.type === 'object') {
|
||||
draft.sections[index].children = draft.sections[index].children || [];
|
||||
@@ -392,7 +411,15 @@ const AdminVideoPromptSchemaConfig: React.FC = () => {
|
||||
|
||||
const updateChild = (sectionIndex: number, childIndex: number, patch: Partial<VideoPromptSchemaNode>) => {
|
||||
updateConfig(draft => {
|
||||
const children = draft.sections[sectionIndex].children || [];
|
||||
const section = draft.sections[sectionIndex];
|
||||
const children = section.children || [];
|
||||
const child = children[childIndex];
|
||||
if (isLockedVideoSpecField(section?.key, child?.key)) {
|
||||
delete patch.key;
|
||||
delete patch.type;
|
||||
delete patch.enabled;
|
||||
patch.editable = false;
|
||||
}
|
||||
children[childIndex] = { ...children[childIndex], ...patch };
|
||||
draft.sections[sectionIndex].children = children;
|
||||
});
|
||||
@@ -470,29 +497,30 @@ const AdminVideoPromptSchemaConfig: React.FC = () => {
|
||||
};
|
||||
|
||||
const sectionItems = useMemo(() => sections.map((section, sectionIndex) => ({
|
||||
key: `${section.key}-${sectionIndex}`,
|
||||
key: `section-${sectionIndex}`,
|
||||
label: (
|
||||
<Space>
|
||||
<Text strong>{section.label || section.key || '未命名分组'}</Text>
|
||||
{section.key && section.key !== section.label && <Tag color="blue">key: {section.key}</Tag>}
|
||||
<Tag>{section.type}</Tag>
|
||||
{!section.enabled && <Tag color="default">已禁用</Tag>}
|
||||
{isLockedVideoSpecSection(section.key) && <Tag color="red">系统锁定</Tag>}
|
||||
{!section.enabled && !isLockedVideoSpecSection(section.key) && <Tag color="default">已禁用</Tag>}
|
||||
</Space>
|
||||
),
|
||||
children: (
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
||||
<Row gutter={12} align="middle">
|
||||
<Col span={4}><Input addonBefore="key" value={section.key} maxLength={64} placeholder="传给 AI 的字段名" onChange={e => updateSection(sectionIndex, { key: e.target.value })} /></Col>
|
||||
<Col span={4}><Input addonBefore="key" value={section.key} maxLength={64} placeholder="传给 AI 的字段名" disabled={isLockedVideoSpecSection(section.key)} onChange={e => updateSection(sectionIndex, { key: e.target.value })} /></Col>
|
||||
<Col span={4}><Input addonBefore="label" value={section.label} maxLength={64} placeholder="后台显示名称" onChange={e => updateSection(sectionIndex, { label: e.target.value })} /></Col>
|
||||
<Col span={4}>
|
||||
<Select value={section.type as any} options={NODE_TYPES} style={{ width: '100%' }} onChange={value => updateSection(sectionIndex, { type: value })} />
|
||||
<Select value={section.type as any} options={NODE_TYPES} style={{ width: '100%' }} disabled={isLockedVideoSpecSection(section.key)} onChange={value => updateSection(sectionIndex, { type: value })} />
|
||||
</Col>
|
||||
<Col span={3}><Space>启用<Switch checked={section.enabled} onChange={checked => updateSection(sectionIndex, { enabled: checked })} /></Space></Col>
|
||||
<Col span={3}><Space>启用<Switch checked={isLockedVideoSpecSection(section.key) ? true : section.enabled} disabled={isLockedVideoSpecSection(section.key)} onChange={checked => updateSection(sectionIndex, { enabled: checked })} /></Space></Col>
|
||||
<Col span={3}><Space>可编辑<Switch checked={section.editable} onChange={checked => updateSection(sectionIndex, { editable: checked })} /></Space></Col>
|
||||
<Col span={4}><InputNumber min={1} max={20000} value={section.maxLength} addonBefore="长度" style={{ width: '100%' }} onChange={value => updateSection(sectionIndex, { maxLength: Number(value || 1000) })} /></Col>
|
||||
<Col span={2} style={{ textAlign: 'right' }}>
|
||||
<Popconfirm title="确认删除该分组?" onConfirm={() => deleteSection(sectionIndex)}>
|
||||
<Button danger icon={<DeleteOutlined />} />
|
||||
<Popconfirm title="确认删除该分组?" disabled={isLockedVideoSpecSection(section.key)} onConfirm={() => deleteSection(sectionIndex)}>
|
||||
<Button danger icon={<DeleteOutlined />} disabled={isLockedVideoSpecSection(section.key)} />
|
||||
</Popconfirm>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -501,18 +529,18 @@ const AdminVideoPromptSchemaConfig: React.FC = () => {
|
||||
<Card size="small" title="对象字段" extra={<Button size="small" icon={<PlusOutlined />} onClick={() => addChild(sectionIndex)}>新增字段</Button>}>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
{(section.children || []).map((child, childIndex) => (
|
||||
<Row gutter={8} align="middle" key={`${child.key}-${childIndex}`}>
|
||||
<Col span={4}><Input addonBefore="key" value={child.key} maxLength={64} placeholder="传给 AI 的字段名" onChange={e => updateChild(sectionIndex, childIndex, { key: e.target.value })} /></Col>
|
||||
<Row gutter={8} align="middle" key={`child-${sectionIndex}-${childIndex}`}>
|
||||
<Col span={4}><Input addonBefore="key" value={child.key} maxLength={64} placeholder="传给 AI 的字段名" disabled={isLockedVideoSpecField(section.key, child.key)} onChange={e => updateChild(sectionIndex, childIndex, { key: e.target.value })} /></Col>
|
||||
<Col span={4}><Input addonBefore="label" value={child.label} maxLength={64} placeholder="后台显示名称" onChange={e => updateChild(sectionIndex, childIndex, { label: e.target.value })} /></Col>
|
||||
<Col span={3}><Select value={child.type as any} options={NODE_TYPES.filter(item => item.value !== 'object' && item.value !== 'flow')} style={{ width: '100%' }} onChange={value => updateChild(sectionIndex, childIndex, { type: value })} /></Col>
|
||||
<Col span={2}><Space>启用<Switch checked={child.enabled} onChange={checked => updateChild(sectionIndex, childIndex, { enabled: checked })} /></Space></Col>
|
||||
<Col span={2}><Space>编辑<Switch checked={child.editable} onChange={checked => updateChild(sectionIndex, childIndex, { editable: checked })} /></Space></Col>
|
||||
<Col span={3}><Select value={child.type as any} options={NODE_TYPES.filter(item => item.value !== 'object' && item.value !== 'flow')} style={{ width: '100%' }} disabled={isLockedVideoSpecField(section.key, child.key)} onChange={value => updateChild(sectionIndex, childIndex, { type: value })} /></Col>
|
||||
<Col span={2}><Space>启用<Switch checked={isLockedVideoSpecField(section.key, child.key) ? true : child.enabled} disabled={isLockedVideoSpecField(section.key, child.key)} onChange={checked => updateChild(sectionIndex, childIndex, { enabled: checked })} /></Space></Col>
|
||||
<Col span={2}><Space>编辑<Switch checked={isLockedVideoSpecField(section.key, child.key) ? false : child.editable} disabled={isLockedVideoSpecField(section.key, child.key)} onChange={checked => updateChild(sectionIndex, childIndex, { editable: checked })} /></Space></Col>
|
||||
<Col span={3}><InputNumber min={1} max={20000} value={child.maxLength} addonBefore="长度" style={{ width: '100%' }} onChange={value => updateChild(sectionIndex, childIndex, { maxLength: Number(value || 1000) })} /></Col>
|
||||
<Col span={5}>
|
||||
<Input value={Array.isArray(child.value) ? child.value.join('/') : String(child.value ?? '')} maxLength={child.maxLength || 1000} placeholder="默认值" onChange={e => updateChild(sectionIndex, childIndex, { value: child.type === 'array' ? e.target.value.split('/').filter(Boolean) : e.target.value })} />
|
||||
</Col>
|
||||
<Col span={1} style={{ textAlign: 'right' }}>
|
||||
<Button danger size="small" icon={<DeleteOutlined />} onClick={() => deleteChild(sectionIndex, childIndex)} />
|
||||
<Button danger size="small" icon={<DeleteOutlined />} disabled={isLockedVideoSpecField(section.key, child.key)} onClick={() => deleteChild(sectionIndex, childIndex)} />
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
@@ -528,7 +556,7 @@ const AdminVideoPromptSchemaConfig: React.FC = () => {
|
||||
</Row>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
{(section.itemFields || []).map((field, fieldIndex) => (
|
||||
<Row gutter={8} align="middle" key={`${field.key}-${fieldIndex}`}>
|
||||
<Row gutter={8} align="middle" key={`flow-${sectionIndex}-${fieldIndex}`}>
|
||||
<Col span={5}><Input addonBefore="key" value={field.key} maxLength={64} placeholder="流程对象字段名" onChange={e => updateFlowField(sectionIndex, fieldIndex, { key: e.target.value })} /></Col>
|
||||
<Col span={5}><Input addonBefore="label" value={field.label} maxLength={64} placeholder="后台显示名称" onChange={e => updateFlowField(sectionIndex, fieldIndex, { label: e.target.value })} /></Col>
|
||||
<Col span={3}><Space>启用<Switch checked={field.enabled} onChange={checked => updateFlowField(sectionIndex, fieldIndex, { enabled: checked })} /></Space></Col>
|
||||
@@ -612,7 +640,7 @@ const AdminVideoPromptSchemaConfig: React.FC = () => {
|
||||
type="warning"
|
||||
showIcon
|
||||
message="字段说明"
|
||||
description="key 是最终传给 AI 和保存到 prompt_schema 的真实字段名,例如「素材理解」「动作流程」;label 只用于管理后台显示,帮助运营/管理员理解这个字段,不会传给 AI;启用关闭后该字段不会进入运行时 schema;可编辑关闭后用户在第 4 步修改时不能改这个字段。"
|
||||
description="key 是最终传给 AI 和保存到 prompt_schema 的真实字段名,例如「素材理解」「动作流程」;label 只用于管理后台显示,帮助运营/管理员理解这个字段,不会传给 AI;启用关闭后该字段不会进入运行时 schema;可编辑关闭后用户在第 4 步修改时不能改这个字段。「画面属性」里的视频时长、视频比例、清晰度、帧率、推荐分辨率属于系统锁定参数,不能改 key、不能禁用、不能开放用户编辑。"
|
||||
/>
|
||||
<Button icon={<PlusOutlined />} onClick={addSection}>新增一级分组</Button>
|
||||
<Collapse items={sectionItems} />
|
||||
|
||||
Reference in New Issue
Block a user