|
|
@@ -2,7 +2,9 @@
|
|
|
// CONFIG-WIZARD-DRAFT-FRONTEND-1:5 步业务条件向导 + 后端草稿接入。
|
|
|
// 1) 报警机制 2) 业务维度 3) 监控对象/指标 4) 判定标准 5) 基础配置
|
|
|
// 非 MANUAL_REPORT:先 saveDraft 落库,再 generate-rule 生成正式 watch_rule。
|
|
|
-// MANUAL_REPORT:跳转主动提报,不写草稿、不写规则。
|
|
|
+// TASK-022-MANUAL-REPORT-TEMPLATE-1:MANUAL_REPORT 改为「主动提报模板」,
|
|
|
+// 仍复用 ado_s8_config_draft 表 + wizard_json 字段保存模板,但不生成 watch_rule。
|
|
|
+// 模板供员工在主动提报页面(/aidop/s8/report)下拉选择并自动带入业务参数。
|
|
|
import { computed, reactive, ref, watch } from 'vue';
|
|
|
import { useRouter } from 'vue-router';
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
@@ -165,6 +167,31 @@ const form = reactive({
|
|
|
const currentDraftId = ref<number | string | null>(null);
|
|
|
const currentDraftCode = ref<string>('');
|
|
|
const draftName = ref<string>('');
|
|
|
+
|
|
|
+// TASK-022-MANUAL-REPORT-TEMPLATE-1:MANUAL_REPORT 模板表单(不与 watch_rule 字段重叠)。
|
|
|
+// 默认值对应「生产主动申报」首条示范模板(R13 ④)。
|
|
|
+const MANUAL_REPORT_DEFAULT_DRAFT_CODE = 'MR-PROD-MANUAL-REPORT';
|
|
|
+const templateForm = reactive({
|
|
|
+ templateName: '生产主动申报',
|
|
|
+ stageCode: 'S6',
|
|
|
+ orderFlowCode: 'BODY_PRODUCTION',
|
|
|
+ title: '生产主动申报 - 产线异常',
|
|
|
+ description: '请描述异常现象、影响工序、涉及工单及初步处理建议。',
|
|
|
+ severity: 'FOLLOW' as 'FOLLOW' | 'SERIOUS',
|
|
|
+ relatedObjectCode: 'WO-DEMO-MANUAL-001',
|
|
|
+ objectCodeLabel: '工单号',
|
|
|
+});
|
|
|
+
|
|
|
+function resetTemplateForm() {
|
|
|
+ templateForm.templateName = '生产主动申报';
|
|
|
+ templateForm.stageCode = 'S6';
|
|
|
+ templateForm.orderFlowCode = 'BODY_PRODUCTION';
|
|
|
+ templateForm.title = '生产主动申报 - 产线异常';
|
|
|
+ templateForm.description = '请描述异常现象、影响工序、涉及工单及初步处理建议。';
|
|
|
+ templateForm.severity = 'FOLLOW';
|
|
|
+ templateForm.relatedObjectCode = 'WO-DEMO-MANUAL-001';
|
|
|
+ templateForm.objectCodeLabel = '工单号';
|
|
|
+}
|
|
|
// 字典演进兜底:草稿回填后若按 objectType+objectLabel 找不到字典项,标记禁用 generate。
|
|
|
const dictionaryMissing = ref(false);
|
|
|
const fallbackObjectLabel = ref('');
|
|
|
@@ -229,6 +256,7 @@ function resetForm() {
|
|
|
dictionaryMissing.value = false;
|
|
|
fallbackObjectLabel.value = '';
|
|
|
fallbackMetricLabel.value = '';
|
|
|
+ resetTemplateForm();
|
|
|
}
|
|
|
|
|
|
watch(visible, (v) => {
|
|
|
@@ -465,12 +493,26 @@ function hydrateFromDraft(draft: S8WizardDraftDetail) {
|
|
|
currentDraftId.value = draft.id;
|
|
|
currentDraftCode.value = draft.draftCode;
|
|
|
draftName.value = draft.draftName ?? '';
|
|
|
- let parsed: any = null;
|
|
|
+ let parsed: any;
|
|
|
try { parsed = JSON.parse(draft.wizardJson); } catch { parsed = null; }
|
|
|
if (!parsed || typeof parsed !== 'object') {
|
|
|
ElMessage.warning('草稿内容损坏,已重置');
|
|
|
return;
|
|
|
}
|
|
|
+ // TASK-022-MANUAL-REPORT-TEMPLATE-1:MANUAL_REPORT 模板独立 schema(templateKind=MANUAL_REPORT)。
|
|
|
+ if (parsed.templateKind === 'MANUAL_REPORT' || (draft.mechanism ?? '').toUpperCase() === 'MANUAL_REPORT') {
|
|
|
+ form.mechanism = 'MANUAL_REPORT';
|
|
|
+ if (typeof parsed.templateName === 'string') templateForm.templateName = parsed.templateName;
|
|
|
+ if (typeof parsed.stageCode === 'string') templateForm.stageCode = parsed.stageCode;
|
|
|
+ if (typeof parsed.orderFlowCode === 'string') templateForm.orderFlowCode = parsed.orderFlowCode;
|
|
|
+ if (typeof parsed.title === 'string') templateForm.title = parsed.title;
|
|
|
+ if (typeof parsed.description === 'string') templateForm.description = parsed.description;
|
|
|
+ if (parsed.severity === 'FOLLOW' || parsed.severity === 'SERIOUS') templateForm.severity = parsed.severity;
|
|
|
+ if (typeof parsed.relatedObjectCode === 'string') templateForm.relatedObjectCode = parsed.relatedObjectCode;
|
|
|
+ if (typeof parsed.objectCodeLabel === 'string') templateForm.objectCodeLabel = parsed.objectCodeLabel;
|
|
|
+ step.value = 0;
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (parsed.version && parsed.version !== 1) {
|
|
|
ElMessage.warning(`草稿版本 ${parsed.version} 不被当前前端支持,请新建`);
|
|
|
return;
|
|
|
@@ -544,10 +586,96 @@ function buildDraftPayload(): {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+// TASK-022-MANUAL-REPORT-TEMPLATE-1:MANUAL_REPORT 模板序列化(独立 schema)。
|
|
|
+function serializeManualReportTemplate(): string {
|
|
|
+ return JSON.stringify({
|
|
|
+ templateKind: 'MANUAL_REPORT',
|
|
|
+ templateName: templateForm.templateName,
|
|
|
+ title: templateForm.title,
|
|
|
+ description: templateForm.description,
|
|
|
+ sceneCode: templateForm.stageCode, // 主动提报页 sceneCode 与 stageCode 同源
|
|
|
+ stageCode: templateForm.stageCode,
|
|
|
+ orderFlowCode: templateForm.orderFlowCode,
|
|
|
+ severity: templateForm.severity,
|
|
|
+ relatedObjectCode: templateForm.relatedObjectCode,
|
|
|
+ objectCodeLabel: templateForm.objectCodeLabel,
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function buildManualReportDraftPayload() {
|
|
|
+ const code = currentDraftCode.value && currentDraftCode.value.startsWith('MR-')
|
|
|
+ ? currentDraftCode.value
|
|
|
+ : MANUAL_REPORT_DEFAULT_DRAFT_CODE;
|
|
|
+ return {
|
|
|
+ tenantId: props.tenantId ?? 1,
|
|
|
+ factoryId: props.factoryId ?? 1,
|
|
|
+ draftCode: code,
|
|
|
+ draftName: templateForm.templateName.trim() || '主动提报模板',
|
|
|
+ wizardJson: serializeManualReportTemplate(),
|
|
|
+ currentStep: 0,
|
|
|
+ mechanism: 'MANUAL_REPORT',
|
|
|
+ stageCode: templateForm.stageCode || null,
|
|
|
+ orderFlowCode: templateForm.orderFlowCode || null,
|
|
|
+ exceptionTypeCode: null,
|
|
|
+ remark: '主动提报模板,不生成自动监控规则',
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+async function saveManualReportTemplate(): Promise<boolean> {
|
|
|
+ if (!templateForm.templateName.trim()) {
|
|
|
+ ElMessage.warning('请填写模板名称');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!templateForm.title.trim()) {
|
|
|
+ ElMessage.warning('请填写默认标题');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!templateForm.stageCode) {
|
|
|
+ ElMessage.warning('请选择阶段维度');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ const payload = buildManualReportDraftPayload();
|
|
|
+ savingDraft.value = true;
|
|
|
+ try {
|
|
|
+ if (currentDraftId.value == null) {
|
|
|
+ const created = await s8ConfigApi.wizardDrafts.create(payload);
|
|
|
+ currentDraftId.value = created.id;
|
|
|
+ currentDraftCode.value = created.draftCode;
|
|
|
+ draftName.value = created.draftName ?? payload.draftName;
|
|
|
+ ElMessage.success('主动提报模板已保存,可在主动提报页面选择使用');
|
|
|
+ } else {
|
|
|
+ const updated = await s8ConfigApi.wizardDrafts.update(currentDraftId.value, {
|
|
|
+ draftName: payload.draftName,
|
|
|
+ wizardJson: payload.wizardJson,
|
|
|
+ currentStep: payload.currentStep,
|
|
|
+ mechanism: payload.mechanism,
|
|
|
+ stageCode: payload.stageCode,
|
|
|
+ orderFlowCode: payload.orderFlowCode,
|
|
|
+ exceptionTypeCode: payload.exceptionTypeCode,
|
|
|
+ remark: payload.remark,
|
|
|
+ });
|
|
|
+ draftName.value = updated.draftName ?? payload.draftName;
|
|
|
+ ElMessage.success('主动提报模板已更新');
|
|
|
+ }
|
|
|
+ emit('draftSaved');
|
|
|
+ return true;
|
|
|
+ } catch (e: any) {
|
|
|
+ const msg = e?.response?.data?.message ?? e?.message ?? '保存主动提报模板失败';
|
|
|
+ // 友好兜底:DraftCode 唯一索引冲突
|
|
|
+ if (typeof msg === 'string' && msg.includes('已存在')) {
|
|
|
+ ElMessage.error(`主动提报模板编码 ${payload.draftCode} 已存在,请在草稿列表中编辑现有模板`);
|
|
|
+ } else {
|
|
|
+ ElMessage.error(msg);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ } finally {
|
|
|
+ savingDraft.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
async function saveDraft(): Promise<boolean> {
|
|
|
if (isManualReport.value) {
|
|
|
- ElMessage.info('主动提报无需保存草稿');
|
|
|
- return false;
|
|
|
+ return saveManualReportTemplate();
|
|
|
}
|
|
|
const payload = buildDraftPayload();
|
|
|
savingDraft.value = true;
|
|
|
@@ -686,8 +814,59 @@ function handleInlineExceptionTypeCreated(payload: { typeCode: string; typeName:
|
|
|
:closable="false"
|
|
|
style="margin-top: 16px"
|
|
|
title="主动提报无需配置自动规则"
|
|
|
- description="主动提报类不依赖自动监控规则,由业务人员在主动提报页面发起。"
|
|
|
+ description="主动提报不会生成自动监控规则,可在此保存提报模板,供员工在主动提报页面选择使用。"
|
|
|
/>
|
|
|
+
|
|
|
+ <!-- TASK-022-MANUAL-REPORT-TEMPLATE-1:MANUAL_REPORT 模板字段表单 -->
|
|
|
+ <el-form
|
|
|
+ v-if="isManualReport"
|
|
|
+ label-position="top"
|
|
|
+ size="small"
|
|
|
+ style="margin-top: 16px"
|
|
|
+ >
|
|
|
+ <el-form-item label="模板名称" required>
|
|
|
+ <el-input v-model="templateForm.templateName" maxlength="64" show-word-limit />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="阶段维度(S1-S7)" required>
|
|
|
+ <el-select v-model="templateForm.stageCode" clearable filterable placeholder="选择阶段维度" style="width: 100%">
|
|
|
+ <el-option
|
|
|
+ v-for="n in stageNodes"
|
|
|
+ :key="n.nodeCode"
|
|
|
+ :label="n.nodeName"
|
|
|
+ :value="n.nodeCode"
|
|
|
+ :disabled="n.disabled"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="订单流程节点(可选)">
|
|
|
+ <el-select v-model="templateForm.orderFlowCode" clearable filterable placeholder="选择订单流程节点(可不选)" style="width: 100%">
|
|
|
+ <el-option
|
|
|
+ v-for="n in orderFlowNodes"
|
|
|
+ :key="n.nodeCode"
|
|
|
+ :label="n.nodeName"
|
|
|
+ :value="n.nodeCode"
|
|
|
+ :disabled="n.disabled"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="默认标题模板" required>
|
|
|
+ <el-input v-model="templateForm.title" maxlength="128" show-word-limit />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="默认描述模板">
|
|
|
+ <el-input v-model="templateForm.description" type="textarea" :rows="3" maxlength="500" show-word-limit />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="默认严重度">
|
|
|
+ <el-select v-model="templateForm.severity" style="width: 240px">
|
|
|
+ <el-option v-for="o in SEVERITY_OPTIONS" :key="o.value" :label="o.label" :value="o.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="对象编码示例">
|
|
|
+ <el-input v-model="templateForm.relatedObjectCode" maxlength="64" show-word-limit placeholder="如 WO-DEMO-MANUAL-001" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="对象编码标签">
|
|
|
+ <el-input v-model="templateForm.objectCodeLabel" maxlength="32" placeholder="如 工单号 / 订单号" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
</div>
|
|
|
|
|
|
<!-- Step 1: 业务维度 -->
|
|
|
@@ -849,7 +1028,8 @@ function handleInlineExceptionTypeCreated(payload: { typeCode: string; typeName:
|
|
|
<template #footer>
|
|
|
<template v-if="isManualReport">
|
|
|
<el-button @click="visible = false">关闭</el-button>
|
|
|
- <el-button type="primary" @click="goManualReport">前往主动提报</el-button>
|
|
|
+ <el-button :loading="savingDraft" type="primary" @click="saveDraft">保存模板</el-button>
|
|
|
+ <el-button @click="goManualReport">前往主动提报</el-button>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<el-button v-if="step > 0" @click="prevStep">上一步</el-button>
|