feat: 收敛AI资源发布审批生命周期

- 统一工作流、知识库、聊天助手的发布、重新发布、下线与删除链路

- 收敛审批编排、生命周期状态机与展示态,补齐审批管理和快照预览

- 调整审批管理权限模型为单入口页面加内部按钮权限
This commit is contained in:
2026-04-09 17:13:54 +08:00
parent 81125ce55c
commit 4e565aef99
68 changed files with 3859 additions and 817 deletions

View File

@@ -0,0 +1,38 @@
import { h } from 'vue';
export interface OfflineImpactBinding {
id?: number | string;
title?: string;
}
export interface OfflineImpactCheck {
canProceed: boolean;
hasBotBindings: boolean;
hasWorkflowUsages: boolean;
botBindings: OfflineImpactBinding[];
workflowUsages: OfflineImpactBinding[];
message?: string;
}
function resolveTitle(item: OfflineImpactBinding) {
return item.title || String(item.id || '');
}
export function joinOfflineImpactTitles(items: OfflineImpactBinding[] = []) {
return items.map(resolveTitle).filter(Boolean).join('、');
}
export function buildOfflineImpactMessage(
intro: string,
items: OfflineImpactBinding[] = [],
footer?: string,
) {
return h('div', [
h('p', intro),
h(
'ul',
items.map((item) => h('li', { key: String(item.id || item.title || '') }, resolveTitle(item))),
),
footer ? h('p', footer) : null,
]);
}

View File

@@ -1,6 +1,8 @@
export type AiPublishStatus =
| 'DELETE_PENDING'
| 'DRAFT'
| 'OFFLINE'
| 'OFFLINE_PENDING'
| 'PUBLISHED'
| 'PUBLISH_PENDING';
@@ -13,6 +15,8 @@ export function normalizeAiPublishStatus(
switch (value) {
case 'PUBLISHED':
case 'PUBLISH_PENDING':
case 'OFFLINE':
case 'OFFLINE_PENDING':
case 'DELETE_PENDING':
return value;
default:
@@ -20,6 +24,17 @@ export function normalizeAiPublishStatus(
}
}
/**
* 解析用于页面展示的发布状态。
* 已发布资源若存在当前审批实例,则视为“发布审批中”,用于统一状态文案与动作禁用。
*/
export function resolveAiResourceDisplayStatus(
displayValue?: null | string,
_fallbackValue?: null | string,
): AiPublishStatus {
return normalizeAiPublishStatus(displayValue);
}
/**
* 当前资源是否已有正式线上版本。
*/
@@ -27,12 +42,23 @@ export function isAiResourcePublished(value?: null | string) {
return normalizeAiPublishStatus(value) === 'PUBLISHED';
}
/**
* 当前资源是否处于已下线状态。
*/
export function isAiResourceOffline(value?: null | string) {
return normalizeAiPublishStatus(value) === 'OFFLINE';
}
/**
* 当前资源是否允许对外可见。
*/
export function isAiResourceExternallyVisible(value?: null | string) {
const normalized = normalizeAiPublishStatus(value);
return normalized === 'PUBLISHED' || normalized === 'DELETE_PENDING';
return (
normalized === 'PUBLISHED' ||
normalized === 'DELETE_PENDING' ||
normalized === 'OFFLINE_PENDING'
);
}
/**
@@ -45,7 +71,68 @@ export function isAiResourceSelectableForBot(value?: null | string) {
/**
* 当前资源是否处于审批处理中。
*/
export function isAiResourceApprovalPending(value?: null | string) {
const normalized = normalizeAiPublishStatus(value);
return normalized === 'PUBLISH_PENDING' || normalized === 'DELETE_PENDING';
export function isAiResourceApprovalPending(
displayValue?: null | string,
_fallbackValue?: null | string,
) {
const normalized = resolveAiResourceDisplayStatus(displayValue);
return (
normalized === 'PUBLISH_PENDING' ||
normalized === 'OFFLINE_PENDING' ||
normalized === 'DELETE_PENDING'
);
}
/**
* 当前资源是否允许发起发布。
*/
export function canAiResourcePublish(
displayValue?: null | string,
_fallbackValue?: null | string,
) {
if (isAiResourceApprovalPending(displayValue)) {
return false;
}
const normalized = resolveAiResourceDisplayStatus(displayValue);
return normalized === 'DRAFT' || normalized === 'OFFLINE';
}
/**
* 当前资源是否允许重新发布。
*/
export function canAiResourceRepublish(
displayValue?: null | string,
_fallbackValue?: null | string,
) {
if (isAiResourceApprovalPending(displayValue)) {
return false;
}
return resolveAiResourceDisplayStatus(displayValue) === 'PUBLISHED';
}
/**
* 当前资源是否允许发起下线。
*/
export function canAiResourceOffline(
displayValue?: null | string,
_fallbackValue?: null | string,
) {
if (isAiResourceApprovalPending(displayValue)) {
return false;
}
return resolveAiResourceDisplayStatus(displayValue) === 'PUBLISHED';
}
/**
* 当前资源是否允许发起删除。
*/
export function canAiResourceDelete(
displayValue?: null | string,
_fallbackValue?: null | string,
) {
if (isAiResourceApprovalPending(displayValue)) {
return false;
}
const normalized = resolveAiResourceDisplayStatus(displayValue);
return normalized === 'DRAFT' || normalized === 'OFFLINE';
}