|
|
@@ -1,62 +1,497 @@
|
|
|
<script setup lang="ts" name="aidopS8WatchRuleConfig">
|
|
|
-import S8CrudConfigPage from '../components/config/S8CrudConfigPage.vue';
|
|
|
-import { s8ConfigApi } from '../api/s8ConfigApi';
|
|
|
-
|
|
|
-const columns = [
|
|
|
- { key: 'ruleCode', label: '规则编码', width: 160 },
|
|
|
- { key: 'sceneCode', label: '场景编码', width: 140 },
|
|
|
- { key: 'dataSourceId', label: '数据源', width: 120 },
|
|
|
- { key: 'watchObjectType', label: '监视对象', width: 140 },
|
|
|
- { key: 'severity', label: '严重度', width: 120 },
|
|
|
- { key: 'pollIntervalSeconds', label: '轮询间隔', width: 120 },
|
|
|
- { key: 'enabled', label: '启用', width: 90 },
|
|
|
- ];
|
|
|
-
|
|
|
-const fields = [
|
|
|
- { key: 'ruleCode', label: '规则编码', type: 'input', required: true },
|
|
|
- { key: 'sceneCode', label: '场景编码', type: 'select', required: true, optionsKey: 'scenes' },
|
|
|
- { key: 'dataSourceId', label: '数据源', type: 'select', required: true, optionsKey: 'dataSources' },
|
|
|
- { key: 'watchObjectType', label: '监视对象', type: 'input', required: true },
|
|
|
- { key: 'expression', label: '表达式', type: 'textarea' },
|
|
|
- { key: 'severity', label: '严重度', type: 'select', required: true, optionsKey: 'severities' },
|
|
|
- { key: 'pollIntervalSeconds', label: '轮询间隔(秒)', type: 'number', required: true },
|
|
|
- { key: 'enabled', label: '启用', type: 'switch' },
|
|
|
-] as const;
|
|
|
-
|
|
|
-const buildDefault = () => ({
|
|
|
- ruleCode: '',
|
|
|
- sceneCode: '',
|
|
|
- dataSourceId: undefined,
|
|
|
- watchObjectType: '',
|
|
|
- expression: '',
|
|
|
- severity: 'MEDIUM',
|
|
|
- pollIntervalSeconds: 300,
|
|
|
- enabled: true,
|
|
|
+import { computed, onMounted, reactive, ref } from 'vue';
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
+import AidopDemoShell from '../../components/AidopDemoShell.vue';
|
|
|
+import {
|
|
|
+ s8ConfigApi,
|
|
|
+ type S8ExceptionTypeConfigRow,
|
|
|
+ type S8WatchRuleConfigRow,
|
|
|
+ type S8WatchRuleType,
|
|
|
+} from '../api/s8ConfigApi';
|
|
|
+
|
|
|
+const TENANT_ID = 1;
|
|
|
+const FACTORY_ID = 1;
|
|
|
+
|
|
|
+const loading = ref(false);
|
|
|
+const saving = ref(false);
|
|
|
+const rows = ref<S8WatchRuleConfigRow[]>([]);
|
|
|
+const exceptionTypes = ref<S8ExceptionTypeConfigRow[]>([]);
|
|
|
+
|
|
|
+const drawerOpen = ref(false);
|
|
|
+const editingRow = ref<S8WatchRuleConfigRow | null>(null);
|
|
|
+
|
|
|
+interface TimeoutForm {
|
|
|
+ dueAtField: string;
|
|
|
+ statusField: string;
|
|
|
+ completedStates: string;
|
|
|
+ objectCodeField: string;
|
|
|
+ objectIdField: string;
|
|
|
+ graceMinutes: number;
|
|
|
+ exceptionTypeCode: string;
|
|
|
+}
|
|
|
+interface ShortageForm {
|
|
|
+ targetQtyField: string;
|
|
|
+ actualQtyField: string;
|
|
|
+ objectCodeField: string;
|
|
|
+ objectIdField: string;
|
|
|
+ toleranceAbs: number;
|
|
|
+ toleranceRatio: number;
|
|
|
+ exceptionTypeCode: string;
|
|
|
+}
|
|
|
+interface OutOfRangeForm {
|
|
|
+ measuredValueField: string;
|
|
|
+ objectCodeField: string;
|
|
|
+ objectIdField: string;
|
|
|
+ lowerBound: number | null;
|
|
|
+ upperBound: number | null;
|
|
|
+ lowerBoundField: string;
|
|
|
+ upperBoundField: string;
|
|
|
+ toleranceAbs: number;
|
|
|
+ toleranceRatio: number;
|
|
|
+ exceptionTypeCode: string;
|
|
|
+}
|
|
|
+
|
|
|
+const timeoutForm = reactive<TimeoutForm>({
|
|
|
+ dueAtField: '',
|
|
|
+ statusField: '',
|
|
|
+ completedStates: '',
|
|
|
+ objectCodeField: '',
|
|
|
+ objectIdField: '',
|
|
|
+ graceMinutes: 0,
|
|
|
+ exceptionTypeCode: '',
|
|
|
+});
|
|
|
+const shortageForm = reactive<ShortageForm>({
|
|
|
+ targetQtyField: '',
|
|
|
+ actualQtyField: '',
|
|
|
+ objectCodeField: '',
|
|
|
+ objectIdField: '',
|
|
|
+ toleranceAbs: 0,
|
|
|
+ toleranceRatio: 0,
|
|
|
+ exceptionTypeCode: '',
|
|
|
+});
|
|
|
+const outOfRangeForm = reactive<OutOfRangeForm>({
|
|
|
+ measuredValueField: '',
|
|
|
+ objectCodeField: '',
|
|
|
+ objectIdField: '',
|
|
|
+ lowerBound: null,
|
|
|
+ upperBound: null,
|
|
|
+ lowerBoundField: '',
|
|
|
+ upperBoundField: '',
|
|
|
+ toleranceAbs: 0,
|
|
|
+ toleranceRatio: 0,
|
|
|
+ exceptionTypeCode: '',
|
|
|
+});
|
|
|
+const enabledForm = ref(true);
|
|
|
+const rawParamsJson = ref<string>('');
|
|
|
+
|
|
|
+const ruleTypeOf = (row: S8WatchRuleConfigRow | null): S8WatchRuleType | '' =>
|
|
|
+ (row?.ruleType ?? '').toUpperCase();
|
|
|
+
|
|
|
+const formTitle = computed(() => {
|
|
|
+ if (!editingRow.value) return '编辑规则参数';
|
|
|
+ return `编辑规则参数 — ${editingRow.value.ruleCode}`;
|
|
|
});
|
|
|
|
|
|
-async function loadOptions() {
|
|
|
- const [scenes, severities, dataSources] = await Promise.all([
|
|
|
- s8ConfigApi.scenes({ tenantId: 1, factoryId: 1 }),
|
|
|
- s8ConfigApi.severities(),
|
|
|
- s8ConfigApi.dataSources({ tenantId: 1, factoryId: 1 }),
|
|
|
- ]);
|
|
|
- return {
|
|
|
- scenes: scenes.map((item: any) => ({ label: item.sceneName, value: item.sceneCode })),
|
|
|
- severities,
|
|
|
- dataSources: dataSources.map((item: any) => ({ label: `${item.dataSourceCode}${item.enabled ? '' : '(未启用)'}`, value: item.id })),
|
|
|
- };
|
|
|
+const ruleTypeLabel = (t?: string | null) => {
|
|
|
+ switch ((t ?? '').toUpperCase()) {
|
|
|
+ case 'TIMEOUT': return '超时';
|
|
|
+ case 'SHORTAGE': return '数量不足';
|
|
|
+ case 'OUT_OF_RANGE': return '数据超差';
|
|
|
+ default: return t || '—';
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const exceptionTypeOptions = computed(() =>
|
|
|
+ exceptionTypes.value
|
|
|
+ .filter((t) => t.enabled)
|
|
|
+ .map((t) => ({ label: `${t.typeName} (${t.typeCode})`, value: t.typeCode })),
|
|
|
+);
|
|
|
+
|
|
|
+async function loadRows() {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ rows.value = await s8ConfigApi.watchRules.list({ tenantId: TENANT_ID, factoryId: FACTORY_ID });
|
|
|
+ } catch (e: any) {
|
|
|
+ ElMessage.error(e?.message ?? '加载监视规则失败');
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function loadExceptionTypes() {
|
|
|
+ try {
|
|
|
+ exceptionTypes.value = await s8ConfigApi.exceptionTypes.list({
|
|
|
+ tenantId: TENANT_ID,
|
|
|
+ factoryId: FACTORY_ID,
|
|
|
+ enabledOnly: true,
|
|
|
+ });
|
|
|
+ } catch (e: any) {
|
|
|
+ ElMessage.error(e?.message ?? '加载异常类型失败');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function resetForms() {
|
|
|
+ Object.assign(timeoutForm, {
|
|
|
+ dueAtField: '', statusField: '', completedStates: '',
|
|
|
+ objectCodeField: '', objectIdField: '',
|
|
|
+ graceMinutes: 0, exceptionTypeCode: '',
|
|
|
+ });
|
|
|
+ Object.assign(shortageForm, {
|
|
|
+ targetQtyField: '', actualQtyField: '',
|
|
|
+ objectCodeField: '', objectIdField: '',
|
|
|
+ toleranceAbs: 0, toleranceRatio: 0, exceptionTypeCode: '',
|
|
|
+ });
|
|
|
+ Object.assign(outOfRangeForm, {
|
|
|
+ measuredValueField: '', objectCodeField: '', objectIdField: '',
|
|
|
+ lowerBound: null, upperBound: null,
|
|
|
+ lowerBoundField: '', upperBoundField: '',
|
|
|
+ toleranceAbs: 0, toleranceRatio: 0, exceptionTypeCode: '',
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function loadFormFromRow(row: S8WatchRuleConfigRow) {
|
|
|
+ resetForms();
|
|
|
+ enabledForm.value = !!row.enabled;
|
|
|
+ rawParamsJson.value = row.paramsJson ?? '';
|
|
|
+
|
|
|
+ if (!row.paramsJson) return;
|
|
|
+ let parsed: Record<string, any>;
|
|
|
+ try {
|
|
|
+ parsed = JSON.parse(row.paramsJson);
|
|
|
+ } catch {
|
|
|
+ ElMessage.warning('当前 params_json 不是合法 JSON,将以空表单展示');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const t = ruleTypeOf(row);
|
|
|
+ if (t === 'TIMEOUT') {
|
|
|
+ Object.assign(timeoutForm, {
|
|
|
+ dueAtField: parsed.dueAtField ?? '',
|
|
|
+ statusField: parsed.statusField ?? '',
|
|
|
+ completedStates: Array.isArray(parsed.completedStates) ? parsed.completedStates.join(',') : '',
|
|
|
+ objectCodeField: parsed.objectCodeField ?? '',
|
|
|
+ objectIdField: parsed.objectIdField ?? '',
|
|
|
+ graceMinutes: typeof parsed.graceMinutes === 'number' ? parsed.graceMinutes : 0,
|
|
|
+ exceptionTypeCode: parsed.exceptionTypeCode ?? '',
|
|
|
+ });
|
|
|
+ } else if (t === 'SHORTAGE') {
|
|
|
+ Object.assign(shortageForm, {
|
|
|
+ targetQtyField: parsed.targetQtyField ?? '',
|
|
|
+ actualQtyField: parsed.actualQtyField ?? '',
|
|
|
+ objectCodeField: parsed.objectCodeField ?? '',
|
|
|
+ objectIdField: parsed.objectIdField ?? '',
|
|
|
+ toleranceAbs: typeof parsed.toleranceAbs === 'number' ? parsed.toleranceAbs : 0,
|
|
|
+ toleranceRatio: typeof parsed.toleranceRatio === 'number' ? parsed.toleranceRatio : 0,
|
|
|
+ exceptionTypeCode: parsed.exceptionTypeCode ?? '',
|
|
|
+ });
|
|
|
+ } else if (t === 'OUT_OF_RANGE') {
|
|
|
+ // 兼容旧 single_threshold:只读展示,保存时转为新协议。
|
|
|
+ const measured = parsed.measuredValueField ?? parsed.valueField ?? '';
|
|
|
+ let lower: number | null = parsed.lowerBound ?? null;
|
|
|
+ let upper: number | null = parsed.upperBound ?? null;
|
|
|
+ if (parsed.mode === 'single_threshold' && typeof parsed.threshold === 'number') {
|
|
|
+ const op = String(parsed.operator ?? '').trim();
|
|
|
+ if (op === '>' || op === '>=') upper = upper ?? parsed.threshold;
|
|
|
+ else if (op === '<' || op === '<=') lower = lower ?? parsed.threshold;
|
|
|
+ }
|
|
|
+ Object.assign(outOfRangeForm, {
|
|
|
+ measuredValueField: measured,
|
|
|
+ objectCodeField: parsed.objectCodeField ?? '',
|
|
|
+ objectIdField: parsed.objectIdField ?? '',
|
|
|
+ lowerBound: lower,
|
|
|
+ upperBound: upper,
|
|
|
+ lowerBoundField: parsed.lowerBoundField ?? '',
|
|
|
+ upperBoundField: parsed.upperBoundField ?? '',
|
|
|
+ toleranceAbs: typeof parsed.toleranceAbs === 'number' ? parsed.toleranceAbs : 0,
|
|
|
+ toleranceRatio: typeof parsed.toleranceRatio === 'number' ? parsed.toleranceRatio : 0,
|
|
|
+ exceptionTypeCode: parsed.exceptionTypeCode ?? 'EQUIP_FAULT',
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function openEdit(row: S8WatchRuleConfigRow) {
|
|
|
+ editingRow.value = row;
|
|
|
+ loadFormFromRow(row);
|
|
|
+ drawerOpen.value = true;
|
|
|
}
|
|
|
+
|
|
|
+function closeDrawer() {
|
|
|
+ drawerOpen.value = false;
|
|
|
+ editingRow.value = null;
|
|
|
+}
|
|
|
+
|
|
|
+function buildPayloadParamsJson(): string | null {
|
|
|
+ const t = ruleTypeOf(editingRow.value);
|
|
|
+ if (t === 'TIMEOUT') {
|
|
|
+ const states = timeoutForm.completedStates
|
|
|
+ .split(/[,\s]+/)
|
|
|
+ .map((s) => s.trim())
|
|
|
+ .filter(Boolean);
|
|
|
+ return JSON.stringify({
|
|
|
+ dueAtField: timeoutForm.dueAtField,
|
|
|
+ statusField: timeoutForm.statusField,
|
|
|
+ completedStates: states,
|
|
|
+ objectCodeField: timeoutForm.objectCodeField,
|
|
|
+ objectIdField: timeoutForm.objectIdField,
|
|
|
+ graceMinutes: Number(timeoutForm.graceMinutes) || 0,
|
|
|
+ exceptionTypeCode: timeoutForm.exceptionTypeCode,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (t === 'SHORTAGE') {
|
|
|
+ return JSON.stringify({
|
|
|
+ targetQtyField: shortageForm.targetQtyField,
|
|
|
+ actualQtyField: shortageForm.actualQtyField,
|
|
|
+ objectCodeField: shortageForm.objectCodeField,
|
|
|
+ objectIdField: shortageForm.objectIdField,
|
|
|
+ toleranceAbs: Number(shortageForm.toleranceAbs) || 0,
|
|
|
+ toleranceRatio: Number(shortageForm.toleranceRatio) || 0,
|
|
|
+ exceptionTypeCode: shortageForm.exceptionTypeCode,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (t === 'OUT_OF_RANGE') {
|
|
|
+ return JSON.stringify({
|
|
|
+ measuredValueField: outOfRangeForm.measuredValueField,
|
|
|
+ objectCodeField: outOfRangeForm.objectCodeField,
|
|
|
+ objectIdField: outOfRangeForm.objectIdField,
|
|
|
+ lowerBound: outOfRangeForm.lowerBound,
|
|
|
+ upperBound: outOfRangeForm.upperBound,
|
|
|
+ lowerBoundField: outOfRangeForm.lowerBoundField || undefined,
|
|
|
+ upperBoundField: outOfRangeForm.upperBoundField || undefined,
|
|
|
+ toleranceAbs: Number(outOfRangeForm.toleranceAbs) || 0,
|
|
|
+ toleranceRatio: Number(outOfRangeForm.toleranceRatio) || 0,
|
|
|
+ exceptionTypeCode: outOfRangeForm.exceptionTypeCode,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return rawParamsJson.value || null;
|
|
|
+}
|
|
|
+
|
|
|
+async function save() {
|
|
|
+ if (!editingRow.value) return;
|
|
|
+ saving.value = true;
|
|
|
+ try {
|
|
|
+ const payload = {
|
|
|
+ paramsJson: buildPayloadParamsJson(),
|
|
|
+ enabled: enabledForm.value,
|
|
|
+ };
|
|
|
+ await s8ConfigApi.watchRules.updateParams(editingRow.value.id, payload);
|
|
|
+ ElMessage.success('保存成功');
|
|
|
+ closeDrawer();
|
|
|
+ await loadRows();
|
|
|
+ } catch (e: any) {
|
|
|
+ ElMessage.error(e?.response?.data?.message ?? e?.message ?? '保存失败');
|
|
|
+ } finally {
|
|
|
+ saving.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function toggleEnabled(row: S8WatchRuleConfigRow) {
|
|
|
+ const next = !row.enabled;
|
|
|
+ const action = next ? '启用' : '停用';
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm(`确认${action}规则 ${row.ruleCode}?`, '提示', { type: 'warning' });
|
|
|
+ } catch { return; }
|
|
|
+ try {
|
|
|
+ await s8ConfigApi.watchRules.updateParams(row.id, {
|
|
|
+ paramsJson: row.paramsJson ?? null,
|
|
|
+ enabled: next,
|
|
|
+ });
|
|
|
+ ElMessage.success(`${action}成功`);
|
|
|
+ await loadRows();
|
|
|
+ } catch (e: any) {
|
|
|
+ ElMessage.error(e?.response?.data?.message ?? e?.message ?? `${action}失败`);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ loadRows();
|
|
|
+ loadExceptionTypes();
|
|
|
+});
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
- <S8CrudConfigPage
|
|
|
- title="监视规则配置"
|
|
|
- subtitle="S8 / 配置 / 监视规则"
|
|
|
- endpoint="/api/aidop/s8/config/watch-rules"
|
|
|
- :columns="columns"
|
|
|
- :fields="fields"
|
|
|
- :build-default="buildDefault"
|
|
|
- :load-options="loadOptions"
|
|
|
- :enable-test="true"
|
|
|
- />
|
|
|
+ <AidopDemoShell title="监视规则配置" subtitle="S8 / 配置 / 监视规则(仅模板化参数编辑,不开放 SQL 编辑)">
|
|
|
+ <el-table v-loading="loading" :data="rows" border stripe size="small">
|
|
|
+ <el-table-column prop="ruleCode" label="规则编码" width="170" />
|
|
|
+ <el-table-column label="规则类型" width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="row.ruleType ? 'success' : 'info'" size="small">
|
|
|
+ {{ ruleTypeLabel(row.ruleType) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="sceneCode" label="场景" width="160" />
|
|
|
+ <el-table-column prop="sourceObjectType" label="源对象" width="120">
|
|
|
+ <template #default="{ row }">{{ row.sourceObjectType ?? row.watchObjectType ?? '—' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="启用" width="90">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="row.enabled ? 'success' : 'info'" size="small">
|
|
|
+ {{ row.enabled ? '启用' : '停用' }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="updatedAt" label="最近更新" width="170">
|
|
|
+ <template #default="{ row }">{{ row.updatedAt ?? row.createdAt ?? '—' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="180" fixed="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button size="small" type="primary" link @click="openEdit(row)">编辑参数</el-button>
|
|
|
+ <el-button size="small" :type="row.enabled ? 'warning' : 'success'" link @click="toggleEnabled(row)">
|
|
|
+ {{ row.enabled ? '停用' : '启用' }}
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <el-drawer v-model="drawerOpen" :title="formTitle" size="520px" direction="rtl" :before-close="(done: () => void) => { closeDrawer(); done(); }">
|
|
|
+ <div v-if="editingRow" class="rule-edit">
|
|
|
+ <el-descriptions :column="1" size="small" border class="rule-meta">
|
|
|
+ <el-descriptions-item label="规则编码">{{ editingRow.ruleCode }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="规则类型">{{ ruleTypeLabel(editingRow.ruleType) }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="场景">{{ editingRow.sceneCode }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="源对象">{{ editingRow.sourceObjectType ?? editingRow.watchObjectType ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="表达式(只读)">
|
|
|
+ <pre class="rule-expr">{{ editingRow.expression || '—' }}</pre>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+
|
|
|
+ <el-divider content-position="left">参数</el-divider>
|
|
|
+
|
|
|
+ <el-form v-if="ruleTypeOf(editingRow) === 'TIMEOUT'" label-position="top" size="small">
|
|
|
+ <el-form-item label="dueAtField(到期时间字段名)">
|
|
|
+ <el-input v-model="timeoutForm.dueAtField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="statusField(状态字段名)">
|
|
|
+ <el-input v-model="timeoutForm.statusField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="completedStates(完成状态,逗号分隔)">
|
|
|
+ <el-input v-model="timeoutForm.completedStates" placeholder="如 DONE,CLOSED" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="objectCodeField">
|
|
|
+ <el-input v-model="timeoutForm.objectCodeField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="objectIdField">
|
|
|
+ <el-input v-model="timeoutForm.objectIdField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="graceMinutes(宽限分钟,≥0)">
|
|
|
+ <el-input-number v-model="timeoutForm.graceMinutes" :min="0" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="exceptionTypeCode">
|
|
|
+ <el-select v-model="timeoutForm.exceptionTypeCode" filterable placeholder="选择异常类型">
|
|
|
+ <el-option v-for="o in exceptionTypeOptions" :key="o.value" :label="o.label" :value="o.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-form v-else-if="ruleTypeOf(editingRow) === 'SHORTAGE'" label-position="top" size="small">
|
|
|
+ <el-form-item label="targetQtyField">
|
|
|
+ <el-input v-model="shortageForm.targetQtyField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="actualQtyField">
|
|
|
+ <el-input v-model="shortageForm.actualQtyField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="objectCodeField">
|
|
|
+ <el-input v-model="shortageForm.objectCodeField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="objectIdField">
|
|
|
+ <el-input v-model="shortageForm.objectIdField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="toleranceAbs(绝对容差,≥0)">
|
|
|
+ <el-input-number v-model="shortageForm.toleranceAbs" :min="0" :step="0.1" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="toleranceRatio(比例容差,0–1)">
|
|
|
+ <el-input-number v-model="shortageForm.toleranceRatio" :min="0" :max="1" :step="0.01" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="exceptionTypeCode">
|
|
|
+ <el-select v-model="shortageForm.exceptionTypeCode" filterable placeholder="选择异常类型">
|
|
|
+ <el-option v-for="o in exceptionTypeOptions" :key="o.value" :label="o.label" :value="o.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-form v-else-if="ruleTypeOf(editingRow) === 'OUT_OF_RANGE'" label-position="top" size="small">
|
|
|
+ <el-form-item label="measuredValueField">
|
|
|
+ <el-input v-model="outOfRangeForm.measuredValueField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="objectCodeField">
|
|
|
+ <el-input v-model="outOfRangeForm.objectCodeField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="objectIdField">
|
|
|
+ <el-input v-model="outOfRangeForm.objectIdField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="upperBound(固定上限,可空)">
|
|
|
+ <el-input-number v-model="outOfRangeForm.upperBound" :step="0.1" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="lowerBound(固定下限,可空)">
|
|
|
+ <el-input-number v-model="outOfRangeForm.lowerBound" :step="0.1" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="upperBoundField(行内上限字段,可空)">
|
|
|
+ <el-input v-model="outOfRangeForm.upperBoundField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="lowerBoundField(行内下限字段,可空)">
|
|
|
+ <el-input v-model="outOfRangeForm.lowerBoundField" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="toleranceAbs(绝对容差,≥0)">
|
|
|
+ <el-input-number v-model="outOfRangeForm.toleranceAbs" :min="0" :step="0.1" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="toleranceRatio(比例容差,0–1)">
|
|
|
+ <el-input-number v-model="outOfRangeForm.toleranceRatio" :min="0" :max="1" :step="0.01" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="exceptionTypeCode">
|
|
|
+ <el-select v-model="outOfRangeForm.exceptionTypeCode" filterable placeholder="选择异常类型">
|
|
|
+ <el-option v-for="o in exceptionTypeOptions" :key="o.value" :label="o.label" :value="o.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-alert v-else type="warning" :closable="false" show-icon>
|
|
|
+ 当前规则 rule_type 为空或非三类已知值,本页面仅展示原始 params_json,不支持模板化编辑。
|
|
|
+ <pre class="rule-raw">{{ rawParamsJson || '(空)' }}</pre>
|
|
|
+ </el-alert>
|
|
|
+
|
|
|
+ <el-divider content-position="left">启用</el-divider>
|
|
|
+ <el-form label-position="top" size="small">
|
|
|
+ <el-form-item label="启用状态">
|
|
|
+ <el-switch v-model="enabledForm" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <div class="rule-edit__footer">
|
|
|
+ <el-button @click="closeDrawer">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="saving" @click="save">保存</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+ </AidopDemoShell>
|
|
|
</template>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.rule-edit {
|
|
|
+ padding: 0 4px 80px;
|
|
|
+}
|
|
|
+.rule-meta {
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+.rule-expr {
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-break: break-all;
|
|
|
+ margin: 0;
|
|
|
+ font-family: var(--el-font-family-mono, monospace);
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
+}
|
|
|
+.rule-raw {
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-break: break-all;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+.rule-edit__footer {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ padding: 12px 16px;
|
|
|
+ background: var(--el-bg-color);
|
|
|
+ border-top: 1px solid var(--el-border-color-light);
|
|
|
+ text-align: right;
|
|
|
+}
|
|
|
+</style>
|