feat: 归档L03与L09审批发布能力

- 新增统一审批中心与审批管理页面,支持流程配置、审批详情与角色/用户审批对象

- 接入聊天助手、知识库、工作流的发布与删除审批,并补齐发布态校验与快照展示
This commit is contained in:
2026-04-07 14:41:52 +08:00
parent 7e7c236c2a
commit 3f128e977a
138 changed files with 13035 additions and 346 deletions

View File

@@ -14,7 +14,7 @@ import { useRouter } from 'vue-router';
import { EasyFlowFormModal } from '@easyflow/common-ui';
import { $t } from '@easyflow/locales';
import { Delete, Edit, Plus, Setting } from '@element-plus/icons-vue';
import { Delete, Edit, Plus, Promotion, Setting } from '@element-plus/icons-vue';
import {
ElForm,
ElFormItem,
@@ -22,16 +22,22 @@ import {
ElInputNumber,
ElMessage,
ElMessageBox,
ElTag,
} from 'element-plus';
import { tryit } from 'radash';
import { removeBotFromId } from '#/api';
import { submitBotDeleteApproval, submitBotPublishApproval } from '#/api';
import { api } from '#/api/request';
import defaultAvatar from '#/assets/ai/bot/defaultBotAvatar.png';
import HeaderSearch from '#/components/headerSearch/HeaderSearch.vue';
import CardList from '#/components/page/CardList.vue';
import PageData from '#/components/page/PageData.vue';
import PageSide from '#/components/page/PageSide.vue';
import {
isAiResourceApprovalPending,
isAiResourcePublished,
normalizeAiPublishStatus,
} from '#/views/ai/shared/publish-status';
import { useDictStore } from '#/store';
import Modal from './modal.vue';
@@ -97,37 +103,101 @@ const actions: ActionButton[] = [
},
},
{
icon: Delete,
text: $t('button.delete'),
tone: 'danger',
permission: '/api/v1/bot/remove',
icon: Promotion,
text: (row: BotInfo) =>
isAiResourcePublished(row.publishStatus)
? $t('button.republish')
: $t('button.submitPublishApproval'),
permission: '/api/v1/bot/save',
placement: 'inline',
onClick(row: BotInfo) {
removeBot(row);
handleSubmitPublishApproval(row);
},
},
{
icon: Delete,
text: $t('button.submitDeleteApproval'),
tone: 'danger',
permission: '/api/v1/bot/remove',
placement: 'menu',
onClick(row: BotInfo) {
handleSubmitDeleteApproval(row);
},
},
];
const removeBot = async (bot: BotInfo) => {
const [action] = await tryit(ElMessageBox.confirm)(
$t('message.deleteAlert'),
$t('message.noticeTitle'),
{
confirmButtonText: $t('message.ok'),
cancelButtonText: $t('message.cancel'),
type: 'warning',
},
);
if (!action) {
const [err, res] = await tryit(removeBotFromId)(bot.id);
if (!err && res.errorCode === 0) {
ElMessage.success($t('message.deleteOkMessage'));
pageDataRef.value.setQuery({});
}
const handleSubmitPublishApproval = async (bot: BotInfo) => {
if (isAiResourceApprovalPending(bot.publishStatus)) {
ElMessage.warning($t('bot.publishPendingHint'));
return;
}
try {
await ElMessageBox.confirm(
$t('bot.submitPublishApprovalConfirm'),
$t('message.noticeTitle'),
{
confirmButtonText: $t('button.confirm'),
cancelButtonText: $t('button.cancel'),
type: 'info',
},
);
} catch {
return;
}
const res = await submitBotPublishApproval(String(bot.id));
if (res.errorCode === 0) {
ElMessage.success(res.message || $t('message.saveOkMessage'));
pageDataRef.value?.reload?.();
}
};
const handleSubmitDeleteApproval = async (bot: BotInfo) => {
if (isAiResourceApprovalPending(bot.publishStatus)) {
ElMessage.warning($t('bot.deletePendingHint'));
return;
}
try {
await ElMessageBox.confirm(
$t('bot.submitDeleteApprovalConfirm'),
$t('message.noticeTitle'),
{
confirmButtonText: $t('button.confirm'),
cancelButtonText: $t('button.cancel'),
type: 'warning',
},
);
} catch {
return;
}
const res = await submitBotDeleteApproval(String(bot.id));
if (res.errorCode === 0) {
ElMessage.success(res.message || $t('message.saveOkMessage'));
pageDataRef.value?.reload?.();
}
};
function resolvePublishStatusMeta(status?: string) {
switch (normalizeAiPublishStatus(status)) {
case 'PUBLISHED':
return {
label: $t('bot.publishStatusPublished'),
type: 'success' as const,
};
case 'PUBLISH_PENDING':
return {
label: $t('bot.publishStatusPublishPending'),
type: 'warning' as const,
};
case 'DELETE_PENDING':
return {
label: $t('bot.publishStatusDeletePending'),
type: 'danger' as const,
};
default:
return {
label: $t('bot.publishStatusDraft'),
type: 'info' as const,
};
}
}
const handleSearch = (params: string) => {
pageDataRef.value.setQuery({ title: params, isQueryOr: true });
@@ -302,7 +372,18 @@ const getSideList = async () => {
:data="pageList"
:primary-action="primaryAction"
:actions="actions"
/>
>
<template #corner="{ item }">
<ElTag
size="small"
effect="plain"
round
:type="resolvePublishStatusMeta(item.publishStatus).type"
>
{{ resolvePublishStatusMeta(item.publishStatus).label }}
</ElTag>
</template>
</CardList>
</template>
</PageData>
</div>