Browse Source

[Mod 0000]数据质量新增问题时新增校验

wangkeyi 3 months ago
parent
commit
6b8dc93b0a

+ 190 - 86
src/views/dashboard/basicInfo/dataQuality/components/DataQualityModal.vue

@@ -11,70 +11,87 @@
     destroyOnClose
     :bodyStyle="{ padding: '20px' }"
   >
-    <!-- 查看模式 -->
-    <div class="que-container" v-if="mode === 'view'">
-      <div class="que-status">
-        <div>矿井状态:</div>
-        <div>
-          <span :class="getStatusClass(currentRecord?.mineLinkStatus)" class="status-dot">
-            {{ getStatusText(currentRecord?.mineLinkStatus) }}
-          </span>
-        </div>
-      </div>
-      <div 
-        class="que-item" 
-        v-for="(item, index) in queList" 
-        :key="index"
-      >
-        <div class="que-details">
-          <div class="que-field">
-            <span class="que-value que-goafName">{{ item.goafName || '-' }}</span>
-          </div>
-          <div class="que-field">
-            <span class="que-label">问题描述:</span>
-            <span class="que-value">{{ item.queCon || '-' }}</span>
-          </div>
-          <div class="que-field time-field">
-            <span class="que-label">时间:</span>
-            <span class="que-value">{{ formatDate(item.startTime) || '-' }}</span>
-            <span class="time-separator">-----</span>
-            <span class="que-value">{{ formatDate(item.endTime) || '-' }}</span>
+    <a-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      layout="horizontal"
+      :label-col="{ span: 4 }"
+      :wrapper-col="{ span: 20 }"
+    >
+      <!-- 查看模式 -->
+      <div class="que-container" v-if="mode === 'view'">
+        <div class="que-status">
+          <div>矿井状态:</div>
+          <div>
+            <span :class="getStatusClass(currentRecord?.mineLinkStatus)" class="status-dot">
+              {{ getStatusText(currentRecord?.mineLinkStatus) }}
+            </span>
           </div>
-          <div class="que-field">
-            <span class="que-label">参数:</span>
-            <span class="que-value">{{ (item.param || '-').replace(/,/g, ' ') }}</span>
+        </div>
+        <div 
+          class="que-item" 
+          v-for="(item, index) in queList" 
+          :key="index"
+        >
+          <div class="que-details">
+            <div class="que-field">
+              <span class="que-value que-goafName">{{ item.goafName || '-' }}</span>
+            </div>
+            <div class="que-field">
+              <span class="que-label">问题描述:</span>
+              <span class="que-value">{{ item.queCon || '-' }}</span>
+            </div>
+            <div class="que-field time-field">
+              <span class="que-label">时间:</span>
+              <span class="que-value">{{ formatDate(item.startTime) || '-' }}</span>
+              <span class="time-separator">-----</span>
+              <span class="que-value">{{ formatDate(item.endTime) || '-' }}</span>
+            </div>
+            <div class="que-field">
+              <span class="que-label">参数:</span>
+              <span class="que-value">{{ (item.param || '-').replace(/,/g, ' ') }}</span>
+            </div>
           </div>
         </div>
       </div>
-    </div>
-
-    <!-- 编辑/新增模式 -->
-    <div v-else class="edit-container">
-      <!-- 动态渲染topFormSchema字段(编辑/新增模式) -->
-      <div class="mine-base-info" v-if="mode === 'add'">
-        <div class="form-item" v-for="schema in topFormSchema" :key="schema.field">
-          <div class="form-label">{{ schema.label }}:</div>
-          <component 
-            :is="getComponent(schema.component)"
-            v-model:value="currentRecord[schema.field]" 
-            v-bind="schema.componentProps"
-            :placeholder="`请输入${schema.label}`"
-            style="width: 100%"
-          />
+
+      <!-- 编辑/新增模式 -->
+      <div v-else class="edit-container">
+        <!-- 动态渲染topFormSchema字段(编辑/新增模式) -->
+        <div class="mine-base-info" v-if="mode === 'add'">
+          <a-form-item
+            v-for="schema in topFormSchema"
+            :key="schema.field"
+            :name="schema.field"
+            :label="schema.label"
+            :rules="schema.rules"
+          >
+            <component 
+              :is="getComponent(schema.component)"
+              v-model:value="currentRecord[schema.field]" 
+              v-bind="schema.componentProps"
+              :placeholder="`请输入${schema.label}`"
+              style="width: 100%"
+            />
+          </a-form-item>
         </div>
-      </div>
 
-      <!-- 问题项编辑区 -->
-      <div 
-        class="que-item" 
-        v-for="(item, index) in queList" 
-        :key="item.orderNum || index"
-      >
-        <div class="que-index">问题 {{ index + 1 }}</div>
-        <div class="edit-fields">
-          <template v-for="schema in formSchema" :key="schema.field">
-            <div class="form-item">
-              <div class="form-label">{{ schema.label }}:</div>
+        <!-- 问题项编辑区 -->
+        <div 
+          class="que-item" 
+          v-for="(item, index) in queList" 
+          :key="item.orderNum || index"
+        >
+          <div class="que-index">问题 {{ index + 1 }}</div>
+          <div class="edit-fields">
+            <a-form-item
+              v-for="schema in formSchema"
+              :key="`${schema.field}-${index}`"
+              :name="[`queList`, index, schema.field]"
+              :label="schema.label"
+              :rules="schema.rules"
+            >
               <component 
                 :is="getComponent(schema.component)"
                 v-model:value="item[schema.field]"
@@ -82,41 +99,41 @@
                 :placeholder="`请输入${schema.label}`"
                 style="width: 100%"
               />
+            </a-form-item>
+            <div class="form-actions">
+              <a-button 
+                type="text" 
+                danger 
+                @click="removeItem(index)"
+                :disabled="queList.length <= 1"
+              >
+                删除
+              </a-button>
             </div>
-          </template>
-          <div class="form-actions">
-            <a-button 
-              type="text" 
-              danger 
-              @click="removeItem(index)"
-              :disabled="queList.length <= 1"
-            >
-              删除
-            </a-button>
           </div>
         </div>
-      </div>
 
-      <a-button 
-        type="dashed" 
-        class="add-btn"
-        @click="addNewItem"
-      >
-        <plus-outlined /> 新增问题
-      </a-button>
-    </div>
+        <a-button 
+          type="dashed" 
+          class="add-btn"
+          @click="addNewItem"
+        >
+          <plus-outlined /> 新增问题
+        </a-button>
+      </div>
+    </a-form>
   </BasicModal>
 </template>
 
 <script setup lang="ts">
-import { ref, computed } from 'vue';
+import { ref, computed, reactive, watch } from 'vue';
 import { BasicModal, useModalInner } from '/@/components/Modal';
 import { formSchema, topFormSchema } from '../dataQuality.data';
-import { Button as AButton, Select, Input, DatePicker } from 'ant-design-vue';
+import { Select, Input, DatePicker, message, } from 'ant-design-vue';
 import { PlusOutlined } from '@ant-design/icons-vue';
 import dayjs, { Dayjs } from 'dayjs';
-// 请根据实际项目路径调整 MineCascader 引入路径
 import MineCascader from '/@/components/Form/src/jeecg/components/MineCascader/MineCascader.vue';
+import type { FormInstance, RuleObject } from 'ant-design-vue/es/form';
 
 // 组件映射表
 const componentMap = {
@@ -143,6 +160,75 @@ const currentRecord = ref<any>({
 // 问题列表
 const queList = ref<any[]>([]);
 
+// 表单实例(用于校验)
+const formRef = ref<FormInstance>();
+// 表单数据聚合(适配Form组件的model)
+const formData = reactive({
+  mineCode: '',
+  queList: queList.value
+});
+
+// 合并表单规则(从schema中提取)
+const formRules = reactive({
+  mineCode: topFormSchema.find(item => item.field === 'mineCode')?.rules || [],
+  queList: {
+    type: 'array',
+    required: true,
+    validator: (rule: RuleObject, value: any[], callback: Function) => {
+      // 校验每个问题项
+      if (value.length === 0) {
+        callback(new Error('至少需要填写一条问题信息'));
+        return;
+      }
+      // 逐个校验问题项的必填字段
+      for (let i = 0; i < value.length; i++) {
+        const item = value[i];
+        // 校验工作面名称
+        if (!item.goafName) {
+          callback(new Error(`第${i+1}条问题:工作面名称不能为空`));
+          return;
+        }
+        // 校验问题描述
+        if (!item.queCon) {
+          callback(new Error(`第${i+1}条问题:问题描述不能为空`));
+          return;
+        }
+        // 校验开始时间
+        if (!item.startTime) {
+          callback(new Error(`第${i+1}条问题:开始时间不能为空`));
+          return;
+        }
+        // 校验结束时间
+        if (!item.endTime) {
+          callback(new Error(`第${i+1}条问题:结束时间不能为空`));
+          return;
+        }
+        // 校验结束时间不能早于开始时间
+        if (dayjs(item.endTime).isBefore(dayjs(item.startTime))) {
+          callback(new Error(`第${i+1}条问题:结束时间不能早于开始时间`));
+          return;
+        }
+        // 校验参数
+        if (!item.param) {
+          callback(new Error(`第${i+1}条问题:参数不能为空`));
+          return;
+        }
+      }
+      callback();
+    }
+  }
+});
+
+// 监听queList变化,同步到formData
+watch(queList, (newVal) => {
+  formData.queList = newVal;
+}, { deep: true });
+
+// 监听currentRecord变化,同步mineCode到formData
+watch(() => currentRecord.value.mineCode, (newVal) => {
+  formData.mineCode = newVal;
+});
+
 // 根据组件名获取对应组件
 const getComponent = (componentName: string) => {
   return componentMap[componentName as keyof typeof componentMap];
@@ -179,6 +265,8 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
     updateTime: '',
     createTime: ''
   };
+  // 同步mineCode到formData
+  formData.mineCode = currentRecord.value.mineCode;
   
   // 初始化问题列表
   if (mode.value === 'add') {
@@ -205,6 +293,8 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
         startTime: item.startTime ? dayjs(item.startTime) : null,
         endTime: item.endTime ? dayjs(item.endTime) : null
       }));
+      // 同步到formData
+      formData.queList = queList.value;
     } catch (error) {
       console.error('解析问题数据失败:', error);
       queList.value = [{
@@ -217,6 +307,10 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
       }];
     }
   }
+  // 重置表单校验状态
+  if (formRef.value) {
+    formRef.value.resetFields();
+  }
 });
 
 // 模态框标题计算属性
@@ -253,30 +347,40 @@ const removeItem = (index: number) => {
 // 提交表单处理
 async function handleSubmit() {
   try {
+    // 查看模式直接提交
+    if (mode.value === 'view') {
+      closeModal();
+      return;
+    }
+
+    // 1. 表单校验
+    if (!formRef.value) return;
+    const validateResult = await formRef.value.validate();
+    if (!validateResult) return;
+
     setModalProps({ confirmLoading: true });
-    // 1. 处理问题列表数据(空日期字段置空,避免空字符串)
+    // 2. 处理问题列表数据(空日期字段置空,避免空字符串)
     const submitQueList = queList.value.map((item) => ({
       ...item,
       startTime: item.startTime ? item.startTime.format('YYYY-MM-DD HH:mm:ss') : null,
       endTime: item.endTime ? item.endTime.format('YYYY-MM-DD HH:mm:ss') : null,
     }));
-    // 2. 构造完整提交数据(修正 createTime/updateTime 逻辑)
+    // 3. 构造完整提交数据
     const now = dayjs().format('YYYY-MM-DD HH:mm:ss');
     const result = {
       ...currentRecord.value,
       queJson: JSON.stringify(submitQueList),
-      // 新增模式:创建时间=当前时间,状态=未解决;编辑模式:保留原有创建时间
       createTime: mode.value === 'add' ? now : (currentRecord.value.createTime || now),
-      // 无论新增/编辑,更新时间都为当前时间
       updateTime: now,
-      // 新增模式默认未解决,编辑模式保留原有状态
       isOk: mode.value === 'add' ? false : currentRecord.value.isOk,
     };
     console.log('最终提交数据:', result);
     emit('success', result);
     closeModal();
-  } catch (error) {
+  } catch (error: any) {
     console.error('提交失败:', error);
+    // 显示校验错误提示
+    message.error(error.message || '表单校验失败,请检查必填项');
   } finally {
     setModalProps({ confirmLoading: false });
   }

+ 30 - 2
src/views/dashboard/basicInfo/dataQuality/dataQuality.data.ts

@@ -130,26 +130,44 @@ export function getSearchFormSchema(dynamicStatusOptions: Ref<{ label: string; v
   ];
   return searchFormSchema;
 }
-export const topFormSchema: FormSchema[] = [
+export const topFormSchema :FormSchema[] = [
   {
     field: 'mineCode',
     label: '煤矿名称',
     component: 'MineCascader',
+    componentProps: {
+      changeOnSelect: false,
+      initFromStore: false,
+      syncToStore: false
+    },
     required: true,
+    rules: [
+      { required: true, message: '请选择煤矿名称', trigger: 'change' },
+    ],
   },
 ];
 
 /** 弹框表单配置 */
-export const formSchema = [
+export const formSchema :FormSchema[] = [
   {
     field: 'goafName',
     label: '工作面名称',
     component: 'Input',
+    required: true,
+    rules: [
+      { required: true, message: '请输入工作面名称', trigger: 'blur' },
+      { max: 100, message: '工作面名称长度不能超过100个字符', trigger: 'blur' },
+    ],
   },
   {
     field: 'queCon',
     label: '问题描述',
     component: 'Input',
+    required: true,
+    rules: [
+      { required: true, message: '请输入问题描述', trigger: 'blur' },
+      { max: 500, message: '问题描述长度不能超过500个字符', trigger: 'blur' },
+    ],
   },
   {
     field: 'startTime',
@@ -160,6 +178,9 @@ export const formSchema = [
       format: 'YYYY-MM-DD HH:mm:ss',
     },
     required: true,
+    rules: [
+      { required: true, message: '请选择开始时间', trigger: 'change' },
+    ],
   },
   {
     field: 'endTime',
@@ -170,11 +191,18 @@ export const formSchema = [
       format: 'YYYY-MM-DD HH:mm:ss',
     },
     required: true,
+    rules: [
+      { required: true, message: '请选择结束时间', trigger: 'change' },
+    ],
   },
   {
     field: 'param',
     label: '参数',
     component: 'Input',
     required: true,
+    rules: [
+      { required: true, message: '请输入参数', trigger: 'blur' },
+      { max: 200, message: '参数长度不能超过200个字符', trigger: 'blur' },
+    ],
   },
 ];