<script setup lang="ts">
|
import type { DevApprovalProgress } from '#/api/x/development/approval';
|
|
import { onBeforeUnmount, ref, watch } from 'vue';
|
|
import { useMessage } from '@jnpf/hooks';
|
|
import { getDevApprovalRun, inspectDevApproval, startDevApprovalRun, stopDevApprovalRun } from '#/api/x/development/approval';
|
import { onlineUtils } from '#/utils/jnpf';
|
|
const props = defineProps<{ disabled?: boolean; flowState?: number | string; taskId?: string }>();
|
const emit = defineEmits<{ busy: [value: boolean]; finished: [] }>();
|
const { createMessage } = useMessage();
|
const available = ref(false);
|
const availabilityError = ref('');
|
const checking = ref(false);
|
const running = ref(false);
|
const progress = ref('');
|
const runId = ref('');
|
const stopping = ref(false);
|
let generation = 0;
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
function cancelPolling() {
|
if (timer) clearTimeout(timer);
|
timer = undefined;
|
}
|
function setBusy(value: boolean) {
|
running.value = value;
|
emit('busy', value);
|
}
|
function schedulePoll(taskId: string, version: number) {
|
cancelPolling();
|
timer = setTimeout(() => poll(taskId, version), 2000);
|
}
|
function applyProgress(data: DevApprovalProgress, taskId: string, version: number, notify: boolean) {
|
if (version !== generation) return;
|
const wasRunning = running.value;
|
runId.value = data.runId || '';
|
stopping.value = data.status === 'STOPPING';
|
const active = data.status === 'RUNNING' || data.status === 'STOPPING';
|
setBusy(active);
|
progress.value = `${stopping.value ? '当前节点完成后停止' : '后台审批'} · 已完成 ${data.completed} 个待办${data.nodeName ? ` · ${data.nodeName}` : ''}`;
|
if (active) {
|
schedulePoll(taskId, version);
|
} else {
|
cancelPolling();
|
if (data.status === 'PASSED') available.value = false;
|
if (notify && data.status === 'IDLE' && wasRunning) {
|
createMessage.warning('未找到本轮后台任务记录,请刷新流程核对已完成节点后重新签名;服务重启不会自动续跑。', 10);
|
emit('finished');
|
}
|
if (notify && data.status !== 'IDLE') {
|
if (data.status === 'PASSED') createMessage.success('流程已审核通过;文件正式生效仍按原生效日期执行');
|
else createMessage.warning(data.message || '后台审批已停止,请查看流程状态', 10);
|
emit('finished');
|
}
|
}
|
}
|
async function poll(taskId: string, version: number) {
|
try {
|
const response = await getDevApprovalRun(taskId);
|
applyProgress(response.data, taskId, version, true);
|
} catch {
|
if (version !== generation) return;
|
progress.value = '进度暂时不可用,正在重新连接;后台任务不受页面连接影响';
|
schedulePoll(taskId, version);
|
}
|
}
|
async function checkAvailability() {
|
const taskId = props.taskId;
|
const version = ++generation;
|
cancelPolling();
|
available.value = false;
|
availabilityError.value = '';
|
checking.value = false;
|
runId.value = '';
|
stopping.value = false;
|
progress.value = '';
|
if (running.value) setBusy(false);
|
if (!import.meta.env.DEV || !taskId || taskId === '0' || String(props.flowState) !== '1') return;
|
checking.value = true;
|
try {
|
const result = await inspectDevApproval(taskId);
|
if (version !== generation) return;
|
available.value = result.data.enabled && result.data.status === 'READY';
|
if (available.value) {
|
const response = await getDevApprovalRun(taskId);
|
applyProgress(response.data, taskId, version, false);
|
}
|
} catch (error) {
|
if (version === generation) availabilityError.value = error instanceof Error ? error.message : '请求失败';
|
} finally {
|
if (version === generation) checking.value = false;
|
}
|
}
|
watch(() => [props.taskId, props.flowState], checkAvailability, { immediate: true });
|
onBeforeUnmount(() => {
|
generation += 1;
|
cancelPolling();
|
// Deliberately do not send a stop request: the server owns execution.
|
});
|
async function execute() {
|
const taskId = props.taskId;
|
const version = generation;
|
if (!taskId || running.value || props.disabled || String(props.flowState) !== '1') return;
|
setBusy(true);
|
let submitted = false;
|
try {
|
progress.value = '等待电子签名';
|
const signId = await new Promise<string | undefined>((resolve) => {
|
onlineUtils.sign({
|
title: '开发一键审批电子签名(本轮复用)',
|
isFaceToFace: false,
|
metaData: {
|
biz_button: '开发一键审批',
|
biz_data: [taskId],
|
biz_module: 'workflow-dev-approval',
|
biz_title: '本流程后续审批复用此电子签名和签名用户',
|
is_biz_form: true,
|
},
|
onSubmit: (data) => resolve(data?.biz_sign),
|
onCancel: () => resolve(undefined),
|
});
|
});
|
if (!signId || version !== generation) return;
|
submitted = true;
|
const response = await startDevApprovalRun(taskId, signId);
|
if (version !== generation) return;
|
applyProgress(response.data, taskId, version, true);
|
} catch (error) {
|
if (version !== generation) return;
|
createMessage.warning(`启动结果暂未确认:${error instanceof Error ? error.message : '请求失败'}。正在查询后台状态,请勿重复启动。`, 10);
|
if (submitted) schedulePoll(taskId, version);
|
} finally {
|
if (!submitted && version === generation) setBusy(false);
|
}
|
}
|
async function stop() {
|
if (!props.taskId || !runId.value || stopping.value) return;
|
const version = generation;
|
try {
|
const response = await stopDevApprovalRun(props.taskId, runId.value);
|
applyProgress(response.data, props.taskId, version, true);
|
} catch (error) {
|
if (version !== generation) return;
|
createMessage.warning(`停止请求未确认:${error instanceof Error ? error.message : '请求失败'},请查看后台进度`, 10);
|
}
|
}
|
</script>
|
|
<template>
|
<a-space v-if="availabilityError || checking" class="mr-[10px]">
|
<span :title="availabilityError">{{ checking ? '正在检查一键审核' : '一键审核入口检查失败' }}</span>
|
<a-button v-if="availabilityError" size="small" :disabled="disabled" @click="checkAvailability">重试</a-button>
|
</a-space>
|
<a-space v-if="available" class="mr-[10px]">
|
<a-button :loading="running" :disabled="disabled && !running" @click="execute">开发一键审核</a-button>
|
<template v-if="running">
|
<span>{{ progress }}</span>
|
<a-button v-if="runId" size="small" :disabled="stopping" @click="stop">停止后续审核</a-button>
|
</template>
|
</a-space>
|
</template>
|