配套 service 候选 500a9a2: - flow.ts 增加 admin/def/offlineLine、enableLine(T03 线级停发/恢复) - Defs.vue 接线级停发开关与状态展示;lineOffline.ts 收敛判定与文案 - AssignPreview.vue + assignEmbed.ts 发起时指派预览(T02 契约) - def-editor 表单坐标绑定微调 - 新增 vitest 组件测试与 line-offline 契约测试(npm test 可跑)
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
<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();
|
||||
if (invalid && invalid.length) {
|
||||
error.value = invalid[0].text || invalid[0];
|
||||
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>
|
||||
Reference in New Issue
Block a user