Răsfoiți Sursa

计划变更报备和上报质量问题UI

刘洋 1 an în urmă
părinte
comite
736bfdc3a7

+ 98 - 0
src/components/common/dynamic-table/index.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="flow-table">
+    <t-table
+      ref="tableRef"
+      row-key="key"
+      :columns="columns"
+      :data="tableData"
+      :editable-row-keys="editableRowKeys"
+      bordered
+      @row-edit="onRowEdit"
+      @row-validate="onRowValidate"
+      @validate="onValidate"
+      size="small"
+    >
+      <template #key="{ row }">
+        <div class="flex items-center key-cell">
+          <Icon
+            name="delete"
+            class="delete-icon"
+            @click="deleteRow(row)"
+          ></Icon>
+          <span class="key-index">{{ row.key }}</span>
+        </div>
+      </template>
+    </t-table>
+    <t-button theme="primary" class="m-t-15px" @click="createOneRow">
+      <template #icon><Icon name="add"></Icon></template>
+      添加
+    </t-button>
+  </div>
+</template>
+
+<script setup name="DynamicTable">
+import { computed, ref, watch } from 'vue';
+import { Icon } from 'tdesign-icons-vue-next';
+const { columns } = defineProps(['columns']);
+const tableRef = ref();
+const tableData = ref([]);
+const resetKeys = () => {
+  tableData.value.forEach((item, index) => {
+    item.key = index + 1 + '';
+  });
+};
+
+const createOneRow = () => {
+  let rowData = columns.reduce((row, item) => {
+    row[item.colKey] = '';
+    return row;
+  }, {});
+  tableData.value.push({ ...rowData });
+  resetKeys();
+};
+
+createOneRow();
+
+const onRowEdit = () => {};
+const onRowValidate = () => {};
+const onValidate = () => {};
+
+const editableRowKeys = computed(() => {
+  return tableData.value.map((item) => item.key);
+});
+const deleteRow = (row) => {
+  let index = tableData.value.findIndex((item) => item.key == row.key);
+  tableData.value.splice(index, 1);
+  resetKeys();
+};
+const validate = (cb) => {
+  tableRef.value.validateTableData().then((result) => {
+    if (!Object.keys(result.result).length) {
+      cb();
+    }
+  });
+};
+
+defineExpose({ validate });
+</script>
+
+<style lang="less" name="TABLE">
+.flow-table {
+  width: 100%;
+  margin-bottom: 25px;
+  .key-cell {
+    .key-index {
+      font-size: 14px;
+    }
+    .delete-icon {
+      font-size: 18px;
+      color: #777;
+      margin-right: 10px;
+      cursor: pointer;
+      &:hover {
+        color: var(--td-brand-color);
+      }
+    }
+  }
+}
+</style>

+ 20 - 22
src/router/modules/sop.js

@@ -38,19 +38,28 @@ export default {
           meta: {
           meta: {
             title: 'SOP流程',
             title: 'SOP流程',
             bind: 'office',
             bind: 'office',
-            // bind: 'OfficeSop',
           },
           },
         },
         },
-        // {
-        //   name: 'SopStep',
-        //   path: '/sop/sop-manage/sop-step',
-        //   component: () => import('@/views/sop/sop-manage/sop-step/index.vue'),
-        //   meta: {
-        //     title: '教务处SOP流程',
-        //     bind: 'office',
-        //     // bind: 'OfficeSop',
-        //   },
-        // },
+        {
+          name: 'PlanChange',
+          path: '/sop/sop-manage/plan-change',
+          component: () =>
+            import('@/views/sop/sop-manage/plan-change/index.vue'),
+          meta: {
+            title: '项目计划变更',
+            bind: 'office',
+          },
+        },
+        {
+          name: 'QualityIssue',
+          path: '/sop/sop-manage/quality-issue',
+          component: () =>
+            import('@/views/sop/sop-manage/quality-issue/index.vue'),
+          meta: {
+            title: '上报质量问题',
+            bind: 'office',
+          },
+        },
         {
         {
           name: 'StudentSop',
           name: 'StudentSop',
           path: '/sop/sop-manage/student-sop',
           path: '/sop/sop-manage/student-sop',
@@ -62,17 +71,6 @@ export default {
             alias: 'cloudMark',
             alias: 'cloudMark',
           },
           },
         },
         },
-        // {
-        //   name: 'StuSopStep',
-        //   path: '/sop/sop-manage/stu-sop-step',
-        //   component: () =>
-        //     import('@/views/sop/sop-manage/stu-sop-step/index.vue'),
-        //   meta: {
-        //     title: '研究生SOP流程',
-        //     bind: 'cloudMark',
-        //     // bind: 'OfficeSop',
-        //   },
-        // },
         {
         {
           name: 'DeviceOutIn',
           name: 'DeviceOutIn',
           path: '/sop/sop-manage/device-out-in',
           path: '/sop/sop-manage/device-out-in',

+ 14 - 4
src/style/global.less

@@ -65,13 +65,13 @@ body {
   padding-right: 15px;
   padding-right: 15px;
   padding-left: 15px;
   padding-left: 15px;
   .t-form__controls-content {
   .t-form__controls-content {
-    flex-direction: column;
-    align-items: flex-start;
+    // flex-direction: column;
+    // align-items: flex-start;
     .sub-title {
     .sub-title {
       margin-bottom: 10px;
       margin-bottom: 10px;
       p {
       p {
         font-size: 13px;
         font-size: 13px;
-        line-height: 1.4;
+        line-height: 30px;
       }
       }
     }
     }
     .top-label {
     .top-label {
@@ -96,10 +96,20 @@ body {
 }
 }
 .form-group-title {
 .form-group-title {
   font-weight: bold;
   font-weight: bold;
-  color: var(--td-brand-color);
+  // color: var(--td-brand-color);
+  color: @dark-text-color;
+  border-bottom: 1px dashed #ddd;
+  padding-bottom: 5px;
   margin-bottom: 15px;
   margin-bottom: 15px;
   font-size: 14px;
   font-size: 14px;
   &.next-title {
   &.next-title {
     margin-top: 20px;
     margin-top: 20px;
   }
   }
 }
 }
+
+.text-indent {
+  text-indent: 2em;
+}
+.red {
+  color: #e4393d;
+}

+ 6 - 6
src/views/sop/components/FORM_GROUP_TITLE.vue

@@ -7,10 +7,10 @@ const { config } = defineProps(['config']);
 </script>
 </script>
 
 
 <style lang="less" scoped>
 <style lang="less" scoped>
-.form-group-title {
-  width: 100%;
-  color: var(--td-text-color-primary);
-  padding-bottom: 3px;
-  border-bottom: 1px dashed #ddd;
-}
+// .form-group-title {
+//   width: 100%;
+//   color: var(--td-text-color-primary);
+//   padding-bottom: 3px;
+//   border-bottom: 1px dashed #ddd;
+// }
 </style>
 </style>

+ 1 - 1
src/views/sop/components/UPLOAD_IMAGE .vue → src/views/sop/components/UPLOAD_IMAGE.vue

@@ -86,6 +86,6 @@ const upload = async (files) => {
   }
   }
 };
 };
 const change = (fileList) => {
 const change = (fileList) => {
-  onChange({ prop: config.binding, value: fileList });
+  props.onChange({ prop: props.config.binding, value: fileList });
 };
 };
 </script>
 </script>

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

@@ -36,7 +36,7 @@ import FORMGROUPTITLE from './FORM_GROUP_TITLE.vue';
 import RADIO from './RADIO.vue';
 import RADIO from './RADIO.vue';
 import CHECKBOX from './CHECKBOX.vue';
 import CHECKBOX from './CHECKBOX.vue';
 import TEXTAREA from './TEXTAREA.vue';
 import TEXTAREA from './TEXTAREA.vue';
-import UPLOADIMAGE from './UPLOAD_IMAGE .vue';
+import UPLOADIMAGE from './UPLOAD_IMAGE.vue';
 import RADIOWITHINPUT from './RADIO_WITH_INPUT.vue';
 import RADIOWITHINPUT from './RADIO_WITH_INPUT.vue';
 import SIGN from './SIGN.vue';
 import SIGN from './SIGN.vue';
 import DEVICEOUTTABLE from './DEVICE_OUT_TABLE.vue';
 import DEVICEOUTTABLE from './DEVICE_OUT_TABLE.vue';

+ 11 - 7
src/views/sop/sop-manage/office-sop/index.vue

@@ -27,10 +27,10 @@
       >
       >
       </t-table>
       </t-table>
     </div>
     </div>
-    <PlanChangeDialog
+    <!-- <PlanChangeDialog
       v-model:visible="showPlanChangeDialog"
       v-model:visible="showPlanChangeDialog"
       :curRow="curRow"
       :curRow="curRow"
-    ></PlanChangeDialog>
+    ></PlanChangeDialog> -->
   </div>
   </div>
 </template>
 </template>
 
 
@@ -39,9 +39,8 @@ import { ref, reactive } from 'vue';
 import { getTableData } from '@/api/test';
 import { getTableData } from '@/api/test';
 import useFetchTable from '@/hooks/useFetchTable';
 import useFetchTable from '@/hooks/useFetchTable';
 import { useRouter } from 'vue-router';
 import { useRouter } from 'vue-router';
-import PlanChangeDialog from './plan-change-dialog.vue';
-const showPlanChangeDialog = ref(false);
-const curRow = ref(null);
+// import PlanChangeDialog from './plan-change-dialog.vue';
+// const curRow = ref(null);
 const selectedRowKeys = ref([]);
 const selectedRowKeys = ref([]);
 const selectChange = (value, { selectedRowData }) => {
 const selectChange = (value, { selectedRowData }) => {
   selectedRowKeys.value = value;
   selectedRowKeys.value = value;
@@ -98,6 +97,7 @@ const columns = [
             hover="color"
             hover="color"
             onClick={(e) => {
             onClick={(e) => {
               e.stopPropagation();
               e.stopPropagation();
+              qualityIssue();
             }}
             }}
           >
           >
             上报质量问题
             上报质量问题
@@ -146,8 +146,12 @@ const toCurSopFlow = (row) => {
 };
 };
 
 
 const planChange = (row) => {
 const planChange = (row) => {
-  curRow.value = row;
-  showPlanChangeDialog.value = true;
+  // curRow.value = row;
+  // showPlanChangeDialog.value = true;
+  router.push({ name: 'PlanChange' });
+};
+const qualityIssue = (row) => {
+  router.push({ name: 'QualityIssue' });
 };
 };
 </script>
 </script>
 
 

+ 0 - 179
src/views/sop/sop-manage/office-sop/plan-change-dialog.vue

@@ -1,179 +0,0 @@
-<template>
-  <my-dialog
-    :visible="visible"
-    @close="emit('update:visible', false)"
-    header="项目计划变更"
-    :width="980"
-    :closeOnOverlayClick="false"
-  >
-    <t-form ref="formRef" :model="formData" :labelWidth="0">
-      <t-row :gutter="[0, 20]">
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>提交时间</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>客户经理</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>客户类型</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>客户名称</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>项目母单编号</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>项目名称</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>项目经理</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>考试开始时间</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>考试结束时间</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <p>特殊要求及备注</p>
-            </div>
-            <div class="read-only"></div>
-          </t-form-item>
-        </t-col>
-
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <span class="require-icon">*</span>
-              <p>变更原因</p>
-            </div>
-            <div class="sub-title">
-              <p :style="{ color: 'gray' }"> 请简要描述变更原因 </p>
-            </div>
-            <t-input v-model="formData.k"></t-input>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item class="my-form-item">
-            <div class="top-label flex items-center">
-              <span class="require-icon">*</span>
-              <p>变更类型</p>
-            </div>
-            <div class="sub-title">
-              <p :style="{ color: 'gray' }">
-                1.请先选择“变更类型”,系统会给出相应的填写表单;
-              </p>
-              <p :style="{ color: 'gray' }">
-                2.关键信息及计划变更:项目关键信息里填写的项目关键信息或时间计划内容调整;
-              </p>
-              <p :style="{ color: 'gray' }">
-                3.处理时限报备:例如项目关键信息、项目总结报告、验收报告等提交时限有特殊原因需要调整,处理时限是指不在项目关键信息表填写的处理时限约束;
-              </p>
-              <p :style="{ color: 'gray' }">
-                4.项目取消:若项目取消,则只需填写变更原因后提交。
-              </p>
-            </div>
-            <t-radio-group v-model="formData.l">
-              <t-radio value="1">项目信息及计划变更</t-radio>
-              <t-radio value="2">处理时限报备</t-radio>
-              <t-radio value="3">项目取消</t-radio>
-            </t-radio-group>
-          </t-form-item>
-        </t-col>
-      </t-row>
-    </t-form>
-    <template #foot>
-      <t-button theme="default" @click="emit('update:visible', false)"
-        >取消</t-button
-      >
-      <t-button theme="primary" @click="save">保存</t-button>
-    </template>
-  </my-dialog>
-</template>
-<script setup name="PlanChangeDialog">
-import { ref } from 'vue';
-import useClearDialog from '@/hooks/useClearDialog';
-const emit = defineEmits(['update:visible', 'success']);
-const formRef = ref(null);
-
-const props = defineProps({
-  visible: Boolean,
-  curRow: Object,
-});
-const getDetail = async () => {
-  //编辑状态下获取回显数据的接口请求业务,如果curRow里的字段够用,就直接把curRow里的字段赋值给formData
-  alert('获取详情中...');
-};
-const { formData, isEdit } = useClearDialog(
-  {
-    a: '',
-    b: '',
-    c: '',
-    d: '',
-    e: '',
-    f: '',
-    g: '',
-    h: '',
-    i: '',
-    j: '',
-    k: '',
-    l: '',
-    m: [],
-  },
-  props,
-  getDetail
-);
-const save = () => {
-  //ajax...
-  emit('success');
-};
-</script>
-<style lang="less" scoped></style>

+ 166 - 0
src/views/sop/sop-manage/plan-change/index.vue

@@ -0,0 +1,166 @@
+<template>
+  <div class="plan-change">
+    <div class="page-wrap">
+      <t-form ref="formRef" :data="formData" :labelWidth="180">
+        <t-row :gutter="[0, 20]">
+          <t-col :span="12">
+            <div class="form-group-title" style="margin-bottom: 0">
+              项目派单信息(SOP流水单号:20230601001)
+            </div>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="项目单号"
+              >1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="项目名称">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="派单时间">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="客户经理">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="客户类型">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="客户名称">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="考试开始时间">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="考试结束时间">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="实施产品">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="服务单元">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="12">
+            <div class="form-group-title"> SOP项目计划变更说明 </div>
+            <div class="sub-title">
+              <p :style="{ color: 'gray' }">
+                SOP项目计划需要变更时,可手动发起一个或多个申请,质控专员审核后,在SOP流程中进行计划变更调整,完成变更后,申请流程结束并通知到发起申请人
+              </p>
+            </div>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="报备申请人">
+              1111
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item class="my-form-item" label="报备申请时间">
+              1111
+            </t-form-item>
+          </t-col>
+
+          <t-col :span="12">
+            <t-form-item class="my-form-item" label="变更类型">
+              <div>
+                <div class="sub-title">
+                  <p :style="{ color: 'gray' }">
+                    1.关键信息及计划变更:项目关键信息里填写的项目关键信息或时间计划内容调整;
+                  </p>
+                  <p :style="{ color: 'gray' }">
+                    2.项目取消:若项目取消,则只需填写变更原因后提交。
+                  </p>
+                </div>
+                <t-radio-group v-model="formData.b">
+                  <t-radio value="1">项目信息及计划变更</t-radio>
+                  <t-radio value="2">项目取消</t-radio>
+                </t-radio-group>
+              </div>
+            </t-form-item>
+          </t-col>
+          <t-col :span="12">
+            <t-form-item class="my-form-item" label="变更原因">
+              <t-input v-model="formData.a" placeholder="50字以内"></t-input>
+            </t-form-item>
+          </t-col>
+          <t-col :span="12">
+            <t-form-item class="my-form-item" label="项目信息及计划变更明细">
+              <dynamic-table :columns="columns" ref="dTable"></dynamic-table>
+            </t-form-item>
+          </t-col>
+        </t-row>
+        <s-button @cancel="router.back()" @confirm="save"></s-button>
+      </t-form>
+    </div>
+  </div>
+</template>
+<script setup name="PlanChange">
+import { ref, computed } from 'vue';
+import { Input } from 'tdesign-vue-next';
+import { useRouter } from 'vue-router';
+import dynamicTable from '@/components/common/dynamic-table';
+const dTable = ref();
+const router = useRouter();
+const columns = computed(() => [
+  {
+    colKey: 'a',
+    title: '变更字段(全称)',
+    edit: {
+      component: Input,
+      props: {
+        clearable: true,
+      },
+      validateTrigger: 'change',
+      rules: [{ required: true, message: '不能为空' }],
+    },
+  },
+  {
+    colKey: 'b',
+    title: '变更后内容',
+    edit: {
+      component: Input,
+      props: {
+        clearable: true,
+      },
+      validateTrigger: 'change',
+      rules: [{ required: true, message: '不能为空' }],
+    },
+  },
+  {
+    colKey: 'c',
+    title: '备注',
+    edit: {
+      component: Input,
+      props: {
+        clearable: true,
+      },
+    },
+  },
+]);
+const formRef = ref(null);
+const formData = ref({
+  a: '',
+  b: '',
+});
+const save = () => {
+  dTable.value?.validate(() => {});
+};
+</script>
+<style lang="less" scoped></style>

+ 66 - 0
src/views/sop/sop-manage/quality-issue/index.vue

@@ -0,0 +1,66 @@
+<template>
+  <div class="quality-issue">
+    <div class="page-wrap">
+      <t-form ref="formRef" :labelWidth="100">
+        <t-row :gutter="[0, 10]">
+          <t-col :span="12">
+            <div class="form-group-title" style="margin-bottom: 0">
+              SOP信息
+            </div>
+          </t-col>
+          <t-col :span="3">
+            <t-form-item label="服务单元"></t-form-item>
+          </t-col>
+          <t-col :span="3">
+            <t-form-item label="SOP流水号"></t-form-item>
+          </t-col>
+          <t-col :span="3">
+            <t-form-item label="客户类型"></t-form-item>
+          </t-col>
+          <t-col :span="3">
+            <t-form-item label="客户名称"></t-form-item>
+          </t-col>
+          <t-col :span="3">
+            <t-form-item label="项目单号"></t-form-item>
+          </t-col>
+          <t-col :span="3">
+            <t-form-item label="实施产品"></t-form-item>
+          </t-col>
+          <t-col :span="3">
+            <t-form-item label="项目名称"></t-form-item>
+          </t-col>
+        </t-row>
+      </t-form>
+      <t-tabs v-model="curStep" placement="left" class="m-t-20px">
+        <t-tab-panel
+          v-for="item in tabs"
+          :key="item.value"
+          :value="item.value"
+          :label="item.label"
+        >
+          <Step1 v-if="curStep === 'step1'"></Step1>
+          <Step2 v-else-if="curStep === 'step2'"></Step2>
+          <Step3 v-else-if="curStep === 'step3'"></Step3>
+          <Step4 v-else-if="curStep === 'step4'"></Step4>
+        </t-tab-panel>
+      </t-tabs>
+    </div>
+  </div>
+</template>
+
+<script setup name="QualityIssue">
+import { ref } from 'vue';
+import Step1 from './step1.vue';
+import Step2 from './step2.vue';
+import Step3 from './step3.vue';
+import Step4 from './step4.vue';
+const tabs = ref([
+  { value: 'step1', label: '质量问题上报' },
+  { value: 'step2', label: '质量问题初审' },
+  { value: 'step3', label: '甲方复核' },
+  { value: 'step4', label: '乙方复核' },
+]);
+const curStep = ref('step3');
+</script>
+
+<style></style>

+ 68 - 0
src/views/sop/sop-manage/quality-issue/step1.vue

@@ -0,0 +1,68 @@
+<template>
+  <div class="step1 p-20px">
+    <t-form ref="formRef" :data="formData" :labelWidth="110">
+      <t-row :gutter="[0, 20]">
+        <t-col :span="12">
+          <div class="form-group-title" style="margin-bottom: 0">
+            质量问题上报(质量问题编号:1234567)
+          </div>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="填写说明">
+            <div class="p-t-5px">
+              <p class="red"
+                >1.质量问题定义:所指客户在项目中的事故或是不满意体验的反馈,并希望通过向我们反馈得以解决的问题。不包含的范围:</p
+              >
+              <p class="red text-indent"
+                >1)客户反馈但并不希望我们协助解决的;</p
+              >
+              <p class="red text-indent">2)不属于公司服务范围或能力内的。</p>
+              <p class="red">2.多个质量问题,请逐条提交。</p>
+            </div>
+          </t-form-item>
+        </t-col>
+        <t-col :span="6">
+          <t-form-item label="提交人"> 张三 </t-form-item>
+        </t-col>
+        <t-col :span="6">
+          <t-form-item label="提交时间"> 2023-06-23 15:23 </t-form-item>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="问题简要">
+            <t-input v-model="formData.a" placeholder="30字以内"></t-input>
+          </t-form-item>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="问题情况说明">
+            <t-textarea
+              v-model="formData.b"
+              placeholder="300字以内"
+            ></t-textarea>
+          </t-form-item>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="附件说明">
+            <UploadImage
+              :config="{ limit: 3 }"
+              @Change="imgChange"
+            ></UploadImage>
+          </t-form-item>
+        </t-col>
+      </t-row>
+    </t-form>
+  </div>
+</template>
+
+<script setup name="QualityIssueStep1">
+import { ref } from 'vue';
+import UploadImage from '@/views/sop/components/UPLOAD_IMAGE.vue';
+const formData = ref({
+  a: '',
+  b: '',
+});
+const imgChange = () => {
+  alert(1);
+};
+</script>
+
+<style></style>

+ 129 - 0
src/views/sop/sop-manage/quality-issue/step2.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="step2 p-20px">
+    <t-form ref="formRef" :data="formData" :labelWidth="200">
+      <t-row :gutter="[0, 20]">
+        <t-col :span="12">
+          <div class="form-group-title" style="margin-bottom: 0">
+            初审意见
+          </div>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="核实情况简要">
+            <t-input v-model="formData.a" placeholder="30字以内"></t-input>
+          </t-form-item>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="核实情况备注">
+            <t-textarea
+              v-model="formData.b"
+              placeholder="300字以内"
+            ></t-textarea>
+          </t-form-item>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="质量问题类型">
+            <div class="p-t-5px">
+              <p class="red"
+                >1、修正类:未执行SOP或未按要求正确操作,需要修正的问题;</p
+              >
+              <p class="red"
+                >2、优化类:SOP未定义或无明确要求和流程的,需要进行流程优化的问题;</p
+              >
+              <p class="red">3、不是问题:不是质量问题。</p>
+              <t-radio-group
+                v-model="formData.c"
+                allow-uncheck
+                :options="[
+                  { value: '1', label: '修正类' },
+                  { value: '2', label: '优化类' },
+                  { value: '3', label: '不是问题' },
+                ]"
+              ></t-radio-group>
+            </div>
+          </t-form-item>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="问题归因">
+            <div class="p-t-5px">
+              <p class="red"
+                >1.执行类:在项目关键信息明确的前提下,人员疏忽、失误、处理不当、项目风险预估不足、预估到风险却未及时采取合理措施,或未按流程严格执行到位等;</p
+              >
+              <p class="red"
+                >2.管理协调类:项目管理未处理好内外项目关键信息沟通协调、计划延期完成等。</p
+              >
+              <p class="red"
+                >3.产品缺陷类:产品设计缺陷、BUG,新应用场景需求等;</p
+              >
+              <p class="red"
+                >4.产品运维类:例如服务器高并发崩溃、网络阻塞等;</p
+              >
+              <p class="red"
+                >5.流程制度类:流程制度本身缺失、设计不合理导致的问题。</p
+              >
+              <t-radio-group
+                v-model="formData.d"
+                allow-uncheck
+                :options="[
+                  { value: '1', label: '修正类' },
+                  { value: '2', label: '优化类' },
+                  { value: '3', label: '不是问题' },
+                  { value: '4', label: '不是问题' },
+                  { value: '5', label: '不是问题' },
+                  { value: '6', label: '其他' },
+                ]"
+              ></t-radio-group>
+            </div>
+          </t-form-item>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="影响度">
+            <t-radio-group
+              v-model="formData.e"
+              allow-uncheck
+              :options="[
+                { value: 'A', label: 'A级' },
+                { value: 'B', label: 'B级' },
+                { value: 'C', label: 'C级' },
+                { value: 'D', label: 'D级' },
+              ]"
+            ></t-radio-group>
+          </t-form-item>
+        </t-col>
+        <t-col :span="6">
+          <t-form-item label="责任人(可多选)">
+            <t-select v-model="formData.f" :options="[]" multiple />
+          </t-form-item>
+        </t-col>
+        <t-col :span="6"> </t-col>
+        <t-col :span="6">
+          <t-form-item label="甲方复审人员(可多选)">
+            <t-select v-model="formData.g" :options="[]" multiple />
+          </t-form-item>
+        </t-col>
+        <t-col :span="6">
+          <t-form-item label="乙方复审人员(可多选)">
+            <t-select v-model="formData.h" :options="[]" multiple />
+          </t-form-item>
+        </t-col>
+      </t-row>
+    </t-form>
+  </div>
+</template>
+
+<script setup name="QualityIssueStep2">
+import { ref } from 'vue';
+const formData = ref({
+  a: '',
+  b: '',
+  c: '',
+  d: '',
+  e: '',
+  f: [],
+  g: [],
+});
+const imgChange = () => {
+  alert(1);
+};
+</script>
+
+<style></style>

+ 40 - 0
src/views/sop/sop-manage/quality-issue/step3.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="step3 p-20px">
+    <t-form ref="formRef" :data="formData" :labelWidth="90">
+      <t-row :gutter="[0, 20]">
+        <t-col :span="12">
+          <div class="form-group-title" style="margin-bottom: 0">
+            甲方复核
+          </div>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="审核意见">
+            <t-radio-group
+              v-model="formData.a"
+              allow-uncheck
+              :options="[
+                { value: '1', label: '同意' },
+                { value: '2', label: '不同意' },
+              ]"
+            ></t-radio-group>
+          </t-form-item>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="备注">
+            <t-textarea v-model="formData.b"></t-textarea>
+          </t-form-item>
+        </t-col>
+      </t-row>
+    </t-form>
+  </div>
+</template>
+
+<script setup name="QualityIssueStep3">
+import { ref } from 'vue';
+const formData = ref({
+  a: '',
+  b: '',
+});
+</script>
+
+<style></style>

+ 40 - 0
src/views/sop/sop-manage/quality-issue/step4.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="step4 p-20px">
+    <t-form ref="formRef" :data="formData" :labelWidth="90">
+      <t-row :gutter="[0, 20]">
+        <t-col :span="12">
+          <div class="form-group-title" style="margin-bottom: 0">
+            甲方复核
+          </div>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="审核意见">
+            <t-radio-group
+              v-model="formData.a"
+              allow-uncheck
+              :options="[
+                { value: '1', label: '同意' },
+                { value: '2', label: '不同意' },
+              ]"
+            ></t-radio-group>
+          </t-form-item>
+        </t-col>
+        <t-col :span="12">
+          <t-form-item label="备注">
+            <t-textarea v-model="formData.b"></t-textarea>
+          </t-form-item>
+        </t-col>
+      </t-row>
+    </t-form>
+  </div>
+</template>
+
+<script setup name="QualityIssueStep4">
+import { ref } from 'vue';
+const formData = ref({
+  a: '',
+  b: '',
+});
+</script>
+
+<style></style>

+ 0 - 89
src/views/sop/sop-manage/sop-step/old.vue

@@ -1,89 +0,0 @@
-<template>
-  <div class="sop-flow">
-    <div class="page-wrap">
-      <t-form colon :label-width="0">
-        <t-row :gutter="[0, 20]">
-          <t-col :span="6" v-for="config in baseConfig" :key="config.id">
-            <MyFormItem :config="config"></MyFormItem>
-          </t-col>
-        </t-row>
-      </t-form>
-      <p class="split-line"></p>
-      <t-tabs v-model="curStep" theme="card" 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="0" class="cur-step-view">
-            <t-row :gutter="[0, 20]">
-              <t-col
-                :span="
-                  fullWidthCodes.includes(config.code) ? 12 : config.span || 6
-                "
-                v-for="config in curFormConfig"
-                :key="config.id"
-              >
-                <MyFormItem :config="config"></MyFormItem>
-              </t-col>
-            </t-row>
-          </t-form>
-        </t-tab-panel>
-      </t-tabs>
-    </div>
-  </div>
-</template>
-
-<script setup name="SopStep">
-import { ref, computed } from 'vue';
-import MyFormItem from '../../components/my-form-item.vue';
-import testData from '../test';
-
-const fullWidthCodes = ref(['TABLE', 'PROJECT_HEADER']);
-const form = ref();
-const allConfigMap = ref(testData.setupMap);
-let baseConfig = ref(testData.setupMap.f_usertask_jwc_start.formProperty);
-
-// const curStep = ref('f_usertask_jwc_project');
-const curStep = ref('评卷校验及收尾');
-const curFormConfig = computed(() => {
-  return allConfigMap.value[curStep.value].formProperty;
-});
-let tabs = ref(
-  Object.values(testData.setupMap)
-    .slice(1)
-    .map((item) => {
-      return {
-        value: item.taskKey,
-        label: item.taskName,
-      };
-    })
-);
-
-const submitHandle = () => {
-  form.value.validate().then(async (result) => {
-    alert(result);
-    if (result === true) {
-    }
-  });
-};
-const back = () => {
-  history.back();
-};
-</script>
-
-<style lang="less" scoped>
-.sop-flow {
-  .page-wrap {
-    padding-right: 20%;
-    .split-line {
-      border-bottom: 1px dashed #ddd;
-      margin: 30px 0 10px;
-    }
-    .cur-step-view {
-      padding: 20px 0;
-    }
-  }
-}
-</style>