Files
flow-center-front/src/views/flow/AssignPreview.vue
T
chenxing c93313cdb7
build / build (push) Successful in 1m28s
feat(flow): 流程图入口/失败提示/指派预览/线级停发闭环/配置分层/选人宿主
- 修复办理页「查看流程图」双 hash URL:改 router.resolve + toAppHashUrl 单 hash
- 修复管理端办件抽屉「查看大图」域名尾点(origin + BASE_URL("./") 拼接)
- 新增 flowError 口径层与办理页内联失败条,审批失败提示可读、不泄漏堆栈
- 审批节点抽屉新增「预览候选人」真入口 + AssigneePreviewModal(只读,不写回定义)
- 线级停发/恢复失败改为单条错误面,避免同错双 toast
- 审批抽屉核心区/高级设置分层;插入菜单常用/高级分组
- 新增 FlowAssignEmbed 选人 iframe 父侧宿主(origin/source/信封/context 四校验)
- 修复 /diagram、/assign-preview 面包屑误显示「流程编辑器」
2026-10-28 03:18:54 +08:00

105 lines
3.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="assign-preview-page">
<a-alert v-if="error" type="error" show-icon :message="error" />
<a-alert v-else-if="!flowGuid" type="warning" show-icon message="缺少 flowGuid 查询参数" />
<template v-else>
<FlowAssignSelector ref="selectorRef" :flow-guid="flowGuid" />
<div class="actions">
<Button type="primary" :loading="submitting" @click="onConfirm">确认选人</Button>
<Button @click="onCancel">取消</Button>
</div>
</template>
</div>
</template>
<script lang="ts" setup>
/**
* 托管选人 iframe 页(2026-10-25 T07,F6 正式接线):
* 业务页面以 iframe 打开本页(query: flowGuid & contextId),本页 CAS 会话预览候选;
* 确认=只向父页回传最终选择+版本锚(postMessage,精确 origin),<b>不调用 apply、
* 不建实例、不建临时流程</b>;取消=只通知关闭。正式提交由父页所在业务按 apply 终审。
* 重新确认以最近一次为准(父页内存覆盖;本页每次确认即时回传)。
*/
import { computed, onMounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import { Button } from 'view-ui-plus';
import FlowAssignSelector from '@/components/FlowAssignSelector.vue';
import { flowApi } from '@/api/module/flow';
import { AssignEmbedMessage, buildMessage, parentOrigin, ASSIGN_EMBED_PROTO, ASSIGN_EMBED_VERSION } from '@/utils/assignEmbed';
const route = useRoute();
const flowGuid = computed(() => String(route.query.flowGuid || ''));
/** 本次打开的关联标识(父页生成);迟到/异 context 消息按它失效 */
const contextId = computed(() => String(route.query.contextId || ''));
const lockedOrigin = parentOrigin();
const selectorRef = ref();
const error = ref('');
const submitting = ref(false);
onMounted(() => {
if (!contextId.value) {
error.value = '缺少 contextId(父页必须生成本次打开的关联标识)';
}
});
async function onConfirm(): Promise<void> {
const selector = selectorRef.value;
if (!selector) {
return;
}
submitting.value = true;
try {
const invalid = selector.validate();
// validate() 返回的是错误文案字符串(空串=通过):早先按数组取值会把提示截成第一个字符
if (invalid) {
error.value = invalid;
return;
}
const preview = await flowApi.applyPreview(flowGuid.value).send();
const assigns = selector.collect();
const msg = buildMessage({
proto: ASSIGN_EMBED_PROTO,
v: ASSIGN_EMBED_VERSION,
type: 'assign-confirmed',
contextId: contextId.value,
flowGuid: flowGuid.value,
defVersion: preview?.defVersion,
assigns,
});
if (!msg || !lockedOrigin) {
error.value = !msg ? '消息构造失败(形状不完整)' : '无法锁定父页 origin(referrer 缺失),拒绝回传';
return;
}
// 精确 origin 回传(同 taskEmbed.postToParent 纪律);source 由父页按 contentWindow 核
window.parent.postMessage(msg, lockedOrigin);
} finally {
submitting.value = false;
}
}
function onCancel(): void {
if (!lockedOrigin || !contextId.value) {
return; // 无法安全回传就静默(父页有关闭超时兜底)
}
const msg: AssignEmbedMessage = {
proto: ASSIGN_EMBED_PROTO,
v: ASSIGN_EMBED_VERSION,
type: 'assign-cancelled',
contextId: contextId.value,
};
window.parent.postMessage(msg, lockedOrigin);
}
</script>
<style scoped>
.assign-preview-page {
padding: 16px;
}
.actions {
margin-top: 16px;
display: flex;
gap: 12px;
}
</style>