Sfoglia il codice sorgente

SOP接口联调和动态表单组件细节调整

刘洋 1 anno fa
parent
commit
705e083bee

+ 1 - 0
package.json

@@ -28,6 +28,7 @@
     "echarts": "^5.4.2",
     "element-resize-detector": "^1.2.4",
     "lodash": "^4.17.21",
+    "mitt": "^3.0.1",
     "mockjs": "^1.1.0",
     "nprogress": "^0.2.0",
     "pinia": "^2.0.27",

+ 13 - 0
src/api/sop.js

@@ -132,3 +132,16 @@ export const getFlowDetail = (data) =>
     url: '/api/admin/flow/view',
     data,
   });
+
+//sop列表
+export const getSopList = (data) =>
+  request({
+    url: '/api/admin/sop/list',
+    data,
+  });
+//sop填报
+export const getSopFlowView = (params) =>
+  request({
+    url: '/api/admin/flow/view',
+    params,
+  });

+ 2 - 0
src/utils/bus.js

@@ -0,0 +1,2 @@
+import mitt from 'mitt';
+export default mitt();

+ 14 - 5
src/views/sop/components/CHECKBOX.vue

@@ -2,17 +2,26 @@
   <t-checkbox-group
     style="min-height: 32px"
     v-model="value"
-    :name="config.binding"
-    :options="config.options || []"
+    :name="config.formName"
+    :options="options"
     @change="change"
   ></t-checkbox-group>
 </template>
 
 <script setup name="CHECKBOX">
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 const { config, onChange } = defineProps(['config', 'onChange']);
-const value = ref([]);
-const change = () => {};
+const value = ref(config.value || []);
+const change = () => {
+  onChange({ prop: config.formName, value: value.value });
+};
+const options = computed(() => {
+  return !config.options
+    ? []
+    : typeof config.options === 'string'
+    ? JSON.parse(config.options)
+    : config.options;
+});
 </script>
 
 <style></style>

+ 3 - 2
src/views/sop/components/DATE.vue

@@ -4,14 +4,15 @@
     @change="change"
     style="width: 100%"
     value-type="time-stamp"
+    :disabled="!config.writable"
   />
 </template>
 <script setup name="DATE">
 import { ref } from 'vue';
 const { config, onChange } = defineProps(['config', 'onChange']);
-const value = ref('');
+const value = ref(config.value || '');
 const change = () => {
-  onChange({ prop: config.binding, value: value.value });
+  onChange({ prop: config.formName, value: value.value });
 };
 </script>
 <style></style>

+ 0 - 21
src/views/sop/components/LABEL.vue

@@ -1,21 +0,0 @@
-<template>
-  <div class="label">
-    <p v-if="label" class="sub-label">{{ label }}</p>
-    <div class="read-only"></div>
-  </div>
-</template>
-<script setup name="LABEL">
-import { ref } from 'vue';
-const { config, label } = defineProps(['config', 'label']);
-const value = ref('');
-</script>
-<style lang="less" scoped>
-.label {
-  width: 100%;
-  .sub-label {
-    color: #555;
-    font-size: 14px;
-    margin-bottom: 5px;
-  }
-}
-</style>

+ 0 - 19
src/views/sop/components/LABELS.vue

@@ -1,19 +0,0 @@
-<template>
-  <div class="labels">
-    <LABEL
-      :config="item"
-      :label="item.title"
-      :key="item.id"
-      v-for="item in config.children"
-    ></LABEL>
-  </div>
-</template>
-<script setup name="LABEL">
-import LABEL from './LABEL.vue';
-const { config } = defineProps(['config']);
-</script>
-<style lang="less" scoped>
-.labels {
-  width: 100%;
-}
-</style>

+ 17 - 0
src/views/sop/components/NUMBER.vue

@@ -0,0 +1,17 @@
+<template>
+  <t-input-number
+    theme="normal"
+    v-model="value"
+    @input="onInput"
+    :disabled="!config.writable"
+  ></t-input-number>
+</template>
+<script setup name="NUMBER">
+import { ref } from 'vue';
+const { config, onChange } = defineProps(['config', 'onChange']);
+const value = ref(config.value || '');
+const onInput = () => {
+  onChange({ prop: config.formName, value: value.value });
+};
+</script>
+<style></style>

+ 26 - 4
src/views/sop/components/RADIO.vue

@@ -4,17 +4,39 @@
     v-model="value"
     allow-uncheck
     :name="config.binding"
-    :options="config.options || []"
+    :options="options"
     @change="change"
     :disabled="config.disabled"
   ></t-radio-group>
 </template>
 
 <script setup name="RADIO">
-import { ref } from 'vue';
+import { ref, computed, onMounted } from 'vue';
+const emit = defineEmits(['ruleDispatch', 'initFormKey']);
 const { config, onChange } = defineProps(['config', 'onChange']);
-const value = ref('');
-const change = () => {};
+// onMounted(() => {
+//   let ruleItem = config.required
+//     ? {
+//         required: true,
+//         message: `${config.title}不能为空`,
+//         type: 'error',
+//         trigger: 'change',
+//       }
+//     : null;
+//   ruleItem && emit('ruleDispatch', [{ [config.formName]: [ruleItem] }]);
+//   emit('initFormKey', config.formName);
+// });
+const value = ref(config.value || '');
+const change = () => {
+  onChange({ prop: config.formName, value: value.value });
+};
+const options = computed(() => {
+  return !config.options
+    ? []
+    : typeof config.options === 'string'
+    ? JSON.parse(config.options)
+    : config.options;
+});
 </script>
 
 <style></style>

+ 4 - 2
src/views/sop/components/RADIO_WITH_INPUT.vue

@@ -20,9 +20,11 @@
 <script setup name="RADIOWITHINPUT">
 import { ref } from 'vue';
 const { config, onChange } = defineProps(['config', 'onChange']);
-const value = ref('');
+const value = ref(config.value || '');
 const inputValue = ref('');
-const change = () => {};
+const change = () => {
+  onChange({ prop: config.formName, value: value.value });
+};
 </script>
 
 <style></style>

+ 11 - 3
src/views/sop/components/SELECT.vue

@@ -1,15 +1,23 @@
 <template>
-  <t-select v-model="value" @change="change"></t-select>
+  <t-select
+    v-model="value"
+    @change="change"
+    :options="options"
+    :multiple="config.code === 'MULTIPLE_SELECT'"
+    :disabled="!config.writable"
+  ></t-select>
 </template>
 <script setup name="SELECT">
 import { ref, onMounted } from 'vue';
 import { useRequest } from 'vue-request';
 import { request } from '@/utils/request.js';
 const { config, onChange } = defineProps(['config', 'onChange']);
-const value = ref('');
+const value = ref(
+  config.code === 'MULTIPLE_SELECT' ? config.value || [] : config.value || ''
+);
 const options = ref([]);
 const change = () => {
-  onChange({ prop: config.binding, value: value.value });
+  onChange({ prop: config.formName, value: value.value });
 };
 const getOptionsApi = () => {
   request({

+ 27 - 1
src/views/sop/components/TABLE.vue

@@ -35,7 +35,10 @@
 import { computed, ref, watch } from 'vue';
 import { Input, DatePicker } from 'tdesign-vue-next';
 import { Icon } from 'tdesign-icons-vue-next';
+import bus from '@/utils/bus';
 const { config, onChange } = defineProps(['config', 'onChange']);
+const tableRef = ref();
+
 let tablePropList = config.tablePropList || [];
 const createEditOption = (code) => {
   let obj = {};
@@ -50,12 +53,14 @@ const createEditOption = (code) => {
       },
       rules: [{ required: true, message: '不能为空' }],
       showEditIcon: false,
+      validateTrigger: 'blur',
     };
   } else if (code === 'DATE') {
     obj = {
       component: DatePicker,
       rules: [{ required: true, message: '不能为空' }],
       showEditIcon: false,
+      validateTrigger: 'blur',
       props: {
         style: { width: '100%' },
       },
@@ -100,7 +105,12 @@ const createOneRow = () => {
 
 createOneRow();
 
-const onRowEdit = () => {};
+const onRowEdit = (params) => {
+  const { row, editedRow } = params;
+  for (let k in editedRow) {
+    row[k] = editedRow[k];
+  }
+};
 const onRowValidate = () => {};
 const onValidate = () => {};
 
@@ -112,6 +122,22 @@ const deleteRow = (row) => {
   tableData.value.splice(index, 1);
   resetKeys();
 };
+
+const validate = () => {
+  tableRef.value.validateTableData().then((result) => {
+    if (!Object.keys(result.result).length) {
+      onChange({ prop: config.formName, value: tableData.value });
+      bus.emit('tablePassed');
+    } else {
+      console.log('表格验证未通过', result.result);
+      bus.emit('tableFailed');
+      onChange({ prop: config.formName, value: '' });
+    }
+  });
+};
+bus.on('checkTable', () => {
+  validate();
+});
 </script>
 
 <style lang="less" name="TABLE">

+ 7 - 5
src/views/sop/components/TEXT.vue

@@ -1,14 +1,16 @@
 <template>
-  <t-input v-model="value" @input="onInput" v-if="!config.readable"></t-input>
-  <LABEL :config="config" v-else></LABEL>
+  <t-input
+    v-model="value"
+    @input="onInput"
+    :disabled="!config.writable"
+  ></t-input>
 </template>
 <script setup name="TEXT">
 import { ref } from 'vue';
-import LABEL from './LABEL';
 const { config, onChange } = defineProps(['config', 'onChange']);
-const value = ref('');
+const value = ref(config.value || '');
 const onInput = () => {
-  onChange({ prop: config.binding, value: value.value });
+  onChange({ prop: config.formName, value: value.value });
 };
 </script>
 <style></style>

+ 4 - 2
src/views/sop/components/TEXTAREA.vue

@@ -4,7 +4,9 @@
 <script setup name="TEXTAREA">
 import { ref } from 'vue';
 const { config, onChange } = defineProps(['config', 'onChange']);
-const value = ref('');
-const change = () => {};
+const value = ref(config.value || '');
+const change = () => {
+  onChange({ prop: config.formName, value: value.value });
+};
 </script>
 <style></style>

+ 14 - 5
src/views/sop/components/UPLOAD_IMAGE.vue

@@ -3,13 +3,13 @@
     ref="uploadRef3"
     v-model="files"
     :theme="theme"
-    :tips="`最多只能上传 ${config.limit || 3} 张图片`"
+    :tips="`最多只能上传 ${config.length || 3} 张图片`"
     accept="image/*"
     :abridge-name="[6, 6]"
     :auto-upload="true"
     :upload-all-files-in-one-request="false"
     multiple
-    :max="config.limit || 3"
+    :max="config.length || 3"
     :before-upload="handleBeforeUpload"
     :request-method="upload"
     @fail="handleFail"
@@ -18,7 +18,7 @@
   </t-upload>
 </template>
 <script setup name="UploadImage">
-import { ref } from 'vue';
+import { ref, watch } from 'vue';
 import { MessagePlugin } from 'tdesign-vue-next';
 import { uploadFiles } from '@/api/common';
 import { getFileMD5 } from '@/utils/crypto';
@@ -85,7 +85,16 @@ const upload = async (files) => {
     return { status: 'fail', error: '上传失败' };
   }
 };
-const change = (fileList) => {
-  props.onChange({ prop: props.config.binding, value: fileList });
+watch(files.value, () => {
+  onChange({
+    prop: props.config.formName,
+    value: files.value.map((item) => item.response),
+  });
+});
+const change = () => {
+  props.onChange({
+    prop: props.config.formName,
+    value: files.value.map((item) => item.response),
+  });
 };
 </script>

+ 13 - 8
src/views/sop/components/my-form-item.vue

@@ -1,7 +1,7 @@
 <template>
   <t-form-item
     :label="isBigTitle ? '' : config.title"
-    :name="config.binding"
+    :name="config.formName"
     :label-width="isBigTitle || !config.title ? 0 : transLabelWidth"
     class="my-form-item"
   >
@@ -41,9 +41,12 @@ import RADIOWITHINPUT from './RADIO_WITH_INPUT.vue';
 import SIGN from './SIGN.vue';
 import DEVICEOUTTABLE from './DEVICE_OUT_TABLE.vue';
 import DEVICEINTABLE from './DEVICE_IN_TABLE.vue';
+import NUMBER from './NUMBER.vue';
 const { config, labelWidth } = defineProps(['config', 'labelWidth']);
+const emit = defineEmits(['change']);
 const onChange = (obj) => {
   console.log('obj', obj);
+  emit('change', obj);
 };
 const bigTitles = ref(['FORM_GROUP_TITLE', 'ONLY_TITLE']);
 const isBigTitle = computed(() => {
@@ -64,12 +67,12 @@ const RenderTest = defineComponent({
     switch (config.code) {
       case 'TEXT':
         return <TEXT config={config} onChange={onChange}></TEXT>;
-      case 'SELECT':
+      case 'SINGLE_SELECT':
+        return <SELECT config={config} onChange={onChange}></SELECT>;
+      case 'MULTIPLE_SELECT':
         return <SELECT config={config} onChange={onChange}></SELECT>;
       case 'DATE':
         return <DATE config={config} onChange={onChange}></DATE>;
-      case 'FILE':
-        return <UPLOAD config={config} onChange={onChange}></UPLOAD>;
       case 'TABLE':
         return <TABLE config={config} onChange={onChange}></TABLE>;
       case 'FORM_GROUP_TITLE':
@@ -80,19 +83,21 @@ const RenderTest = defineComponent({
         return <CHECKBOX config={config} onChange={onChange}></CHECKBOX>;
       case 'TEXTAREA':
         return <TEXTAREA config={config} onChange={onChange}></TEXTAREA>;
-      case 'UPLOAD_IMAGE':
+      case 'FILE':
         return <UPLOADIMAGE config={config} onChange={onChange}></UPLOADIMAGE>;
       case 'RADIO_WITH_INPUT':
         return (
           <RADIOWITHINPUT config={config} onChange={onChange}></RADIOWITHINPUT>
         );
       case 'DEVICE_OUT_TABLE':
-        return <DEVICEOUTTABLE></DEVICEOUTTABLE>;
+        return <DEVICEOUTTABLE onChange={onChange}></DEVICEOUTTABLE>;
       case 'DEVICE_IN_TABLE':
-        return <DEVICEINTABLE></DEVICEINTABLE>;
+        return <DEVICEINTABLE onChange={onChange}></DEVICEINTABLE>;
+      case 'NUMBER':
+        return <NUMBER config={config} onChange={onChange}></NUMBER>;
       case 'SIGN':
         return <SIGN></SIGN>;
-      case 'ONLY_TITLE':
+      case 'ONLE_TITLE':
         return <div></div>;
       case '':
         return <div></div>;

+ 41 - 15
src/views/sop/sop-manage/office-sop/index.vue

@@ -1,13 +1,19 @@
 <template>
   <div class="office-sop flex flex-col h-full">
-    <SearchForm :fields="fields" :params="params"></SearchForm>
+    <div class="page-action">
+      <t-space size="small">
+        <t-button theme="danger" :disabled="!selectedRowKeys.length">
+          作废
+        </t-button>
+      </t-space>
+    </div>
+    <SearchForm :fields="fields" :params="params">
+      <template #service="{ item, params }">
+        <select-service-unit v-model="params[item.prop]"></select-service-unit>
+      </template>
+    </SearchForm>
 
     <div class="flex-1 page-wrap">
-      <div class="btn-group">
-        <t-button theme="success" :disabled="!selectedRowKeys.length"
-          >批量作废</t-button
-        >
-      </div>
       <t-table
         size="small"
         row-key="id"
@@ -35,10 +41,10 @@
 </template>
 
 <script setup lang="jsx" name="OfficeSop">
-import { ref, reactive } from 'vue';
-import { getTableData } from '@/api/test';
+import { ref, reactive, computed } from 'vue';
 import useFetchTable from '@/hooks/useFetchTable';
 import { useRouter } from 'vue-router';
+import { getSopList } from '@/api/sop';
 // import PlanChangeDialog from './plan-change-dialog.vue';
 // const curRow = ref(null);
 const selectedRowKeys = ref([]);
@@ -54,7 +60,24 @@ const columns = [
     width: 50,
     fixed: 'left',
   },
-  { colKey: 'a', title: '服务单元' },
+  { colKey: 'serviceName', title: '服务单元' },
+  { colKey: 'sopNo', title: 'SOP流水号' },
+  { colKey: 'crmNo', title: '项目单号' },
+  { colKey: 'beginTime', title: '派单时间' },
+  { colKey: 'customManagerName', title: '客户经理' },
+  { colKey: 'customManagerTypeStr', title: '客户类型' },
+  { colKey: 'customName', title: '客户名称' },
+  { colKey: 'crmName', title: '项目名称' },
+  { colKey: 'productName', title: '实施产品' },
+  { colKey: 'examStartTime', title: '考试开始时间' },
+  { colKey: 'examEndTime', title: '考试结束时间' },
+  //...
+  { colKey: 'flowCreateName', title: '提交人' },
+  { colKey: 'flowCreateTime', title: '提交时间' },
+  { colKey: 'flowUpdateTime', title: '更新时间' },
+  { colKey: 'status', title: '流程状态' },
+  { colKey: 'taskName', title: '流程节点' },
+  { colKey: 'pendApproveName', title: '当前节点负责人' },
   {
     title: '管理',
     colKey: 'operate',
@@ -119,30 +142,33 @@ const columns = [
 ];
 const fields = ref([
   {
-    prop: 'a',
+    prop: 'serviceId',
     label: '服务单元',
     type: 'select',
     labelWidth: 80,
-    colSpan: 5,
+    colSpan: 6,
+    cell: 'service',
   },
 ]);
 const params = reactive({
-  a: '',
+  serviceId: '',
+});
+const transParams = computed(() => {
+  return { ...params, type: 'OFFICE_SOP_FLOW' };
 });
-
 const {
   loading: tableLoading,
   pagination,
   tableData,
   fetchData,
   onChange,
-} = useFetchTable(getTableData);
+} = useFetchTable(getSopList, { params: transParams });
 
 const createSopFlow = (row) => {
   router.push({ name: 'SopStep' });
 };
 const toCurSopFlow = (row) => {
-  router.push({ name: 'SopStep' });
+  router.push({ name: 'SopStep', query: { type: 'fill', flowId: row.flowId } });
 };
 
 const planChange = (row) => {

+ 154 - 60
src/views/sop/sop-manage/sop-step/index.vue

@@ -2,50 +2,84 @@
   <div class="sop-step">
     <div class="page-wrap">
       <p class="split-line"></p>
-      <t-tabs v-model="curStep" placement="left" class="m-t-20px">
-        <t-tab-panel
-          v-for="item in tabs"
-          :key="item.taskKey"
-          :value="item.value"
-          :label="item.label"
-        >
-          <t-form
-            ref="form"
-            colon
-            :label-width="labelWidth"
-            class="cur-step-view"
+      <div class="p-t-10px p-b-30px" style="background-color: #fff">
+        <t-tabs v-model="curStep" placement="left" class="m-t-10px">
+          <t-tab-panel
+            v-for="item in tabs"
+            :key="item.taskKey"
+            :value="item.value"
+            :label="item.label"
           >
-            <t-row :gutter="[0, 10]">
-              <t-col
-                :span="
-                  fullWidthCodes.includes(config.code) ? 12 : config.span || 6
-                "
-                v-for="config in curFormConfig"
-                :key="config.id"
-              >
-                <MyFormItem
-                  :config="config"
-                  :labelWidth="labelWidth"
-                ></MyFormItem>
-              </t-col>
-            </t-row>
-          </t-form>
-          <s-buttons
-            confirmText="提交"
-            @cancel="router.back()"
-            class="m-t-50px"
-          ></s-buttons>
-        </t-tab-panel>
-      </t-tabs>
+            <t-form
+              ref="form"
+              colon
+              :label-width="labelWidth"
+              class="cur-step-view"
+              :rules="rules"
+              :data="formData"
+            >
+              <t-row :gutter="[0, 20]">
+                <t-col
+                  :span="
+                    fullWidthCodes.includes(config.code)
+                      ? 12
+                      : config.span < 6
+                      ? 6
+                      : config.span || 6
+                  "
+                  v-for="config in curFormConfig"
+                  :key="config.id"
+                >
+                  <MyFormItem
+                    :config="config"
+                    :labelWidth="labelWidth"
+                    @change="itemValueChange"
+                  ></MyFormItem>
+                </t-col>
+              </t-row>
+            </t-form>
+            <s-buttons
+              confirmText="提交"
+              @cancel="router.back()"
+              @confirm="submitHandle"
+              class="m-t-50px"
+            ></s-buttons>
+          </t-tab-panel>
+        </t-tabs>
+      </div>
     </div>
   </div>
 </template>
 
 <script setup name="SopStep">
-import { ref, computed } from 'vue';
+import { ref, computed, watch } from 'vue';
 import MyFormItem from '../../components/my-form-item.vue';
-import { useRouter } from 'vue-router';
-import testData from '../test';
+import { useRouter, useRoute } from 'vue-router';
+import { getSopFlowView } from '@/api/sop';
+import bus from '@/utils/bus';
+// import testData from '../test';
+const needValueCodes = [
+  'NUMBER', //新增
+  'TEXT',
+  'DATE',
+  'SELECT',
+  'CHECKBOX',
+  'TEXTAREA',
+  'TABLE',
+  'RADIO',
+  'RADIO_WITH_INPUT',
+  'SIGN',
+  'DEVICE_OUT_TABLE',
+  'DEVICE_IN_TABLE',
+  'FILE',
+];
+const rules = ref({
+  // scan_net_radio_2: [{ required: true, type: 'error', message: '大大是的' }],
+});
+const formData = ref({});
+const formProperty = ref([]);
+const loading = ref(false);
+const route = useRoute();
 const router = useRouter();
 const labelWidth = ref(230);
 const fullWidthCodes = ref([
@@ -56,34 +90,94 @@ const fullWidthCodes = ref([
   'DEVICE_OUT_TABLE',
   'DEVICE_IN_TABLE',
 ]);
-const form = ref();
+const form = ref(null);
+const allSteps = ref([]);
+const tabs = ref([]);
+const curStep = ref('');
+const flowId = route.query.flowId;
+const initFill = () => {
+  loading.value = true;
+  getSopFlowView({ flowId }).then((res) => {
+    loading.value = false;
+    curStep.value = res.currFlowTaskResult.taskName;
+    res.flowTaskHistoryList = res.flowTaskHistoryList || [];
+    res.flowTaskHistoryList.forEach((item) => {
+      item.formProperty.forEach((v) => {
+        v.writable = false;
+      });
+    });
+    allSteps.value = [...res.flowTaskHistoryList, res.currFlowTaskResult];
+    tabs.value = [
+      ...res.flowTaskHistoryList.map((item) => {
+        return { value: item.taskName, label: item.taskName };
+      }),
+      {
+        value: res.currFlowTaskResult.taskName,
+        label: res.currFlowTaskResult.taskName,
+      },
+    ];
+  });
+};
+const init = () => {
+  if (route.query.type === 'fill') {
+    //type为fill是点击“填报”进来的
+    initFill();
+  } else {
+    //还有可能是点击“新增SOP”进来的
+  }
+};
+init();
 
-// const curStep = ref('f_usertask_jwc_project');
-const curStep = ref('设备入库登记');
-let allSteps = ref([
-  ...testData.flowTaskHistoryList,
-  testData.currFlowTaskResult,
-]);
-let tabsArr = [
-  ...testData.flowTaskHistoryList.map((item) => {
-    return { value: item.taskName, label: item.taskName };
-  }),
-  {
-    value: testData.currFlowTaskResult.taskName,
-    label: testData.currFlowTaskResult.taskName,
-  },
-];
-let tabs = ref(tabsArr);
 const curFormConfig = computed(() => {
-  return allSteps.value.find((item) => item.taskName === curStep.value)
-    .formProperty;
+  return (
+    allSteps.value.find((item) => item.taskName === curStep.value)
+      ?.formProperty || []
+  );
 });
-const submitHandle = () => {
-  form.value.validate().then(async (result) => {
-    alert(result);
-    if (result === true) {
+watch(curFormConfig, (val) => {
+  formData.value = val.reduce((obj, item) => {
+    if (needValueCodes.includes(item.code)) {
+      obj[item.formName] = '';
+    }
+    return obj;
+  }, {});
+  rules.value = val.reduce((obj, item) => {
+    let ruleItem =
+      item.required && needValueCodes.includes(item.code)
+        ? {
+            required: true,
+            message: `${item.title}不能为空`,
+            type: 'error',
+            trigger: 'change',
+          }
+        : null;
+    if (ruleItem) {
+      obj[item.formName] = [ruleItem];
     }
+    return obj;
+  }, {});
+});
+const submitHandle = () => {
+  bus.emit('checkTable');
+  let tablePassed = false;
+  bus.on('tablePassed', () => {
+    tablePassed = true;
+    console.log('table通过了');
   });
+  bus.on('tableFailed', () => {
+    tablePassed = false;
+    console.log('table不通过');
+  });
+  setTimeout(() => {
+    form.value[0].validate().then(async (result) => {
+      if (result === true && tablePassed) {
+        //提交表单接口执行
+      }
+    });
+  }, 10);
+};
+const itemValueChange = ({ prop, value }) => {
+  formData.value[prop] = value;
 };
 const back = () => {
   history.back();

+ 1 - 0
src/views/user/auth-manage/user-manage/add-user-dialog.vue

@@ -20,6 +20,7 @@
             v-for="(val, key) in GENDER_TYPE"
             :value="key"
             :label="val"
+            :key="val"
           ></t-radio>
         </t-radio-group>
       </t-form-item>