build / build (push) Successful in 1m28s
- 修复办理页「查看流程图」双 hash URL:改 router.resolve + toAppHashUrl 单 hash
- 修复管理端办件抽屉「查看大图」域名尾点(origin + BASE_URL("./") 拼接)
- 新增 flowError 口径层与办理页内联失败条,审批失败提示可读、不泄漏堆栈
- 审批节点抽屉新增「预览候选人」真入口 + AssigneePreviewModal(只读,不写回定义)
- 线级停发/恢复失败改为单条错误面,避免同错双 toast
- 审批抽屉核心区/高级设置分层;插入菜单常用/高级分组
- 新增 FlowAssignEmbed 选人 iframe 父侧宿主(origin/source/信封/context 四校验)
- 修复 /diagram、/assign-preview 面包屑误显示「流程编辑器」
105 lines
3.5 KiB
Vue
105 lines
3.5 KiB
Vue
<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>
|