Explorar o código

违规新增,sop新增

zhangjie hai 1 ano
pai
achega
77f99cf789

+ 5 - 0
src/api/sop.js

@@ -47,6 +47,11 @@ export const getViolationList = (data) =>
     url: '/api/admin/tb/violation/query',
     params: data,
   });
+export const saveViolation = (data) =>
+  request({
+    url: '/api/admin/tb/violation/save',
+    data,
+  });
 // 关闭单个违规登记
 export const closeViolation = (id) =>
   request({

+ 1 - 1
src/components/common/dynamic-table/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="flow-table">
+  <div>
     <t-table
       ref="tableRef"
       size="small"

+ 13 - 2
src/utils/tool.js

@@ -405,6 +405,17 @@ export function randomCode(len = 16) {
   return stepNums.join('');
 }
 
-export function deepCopy(obj) {
-  return JSON.parse(JSON.stringify(obj));
+/**
+ * 将目标对象中有的属性值与源对象中的属性值合并
+ * @param {Object} target 目标对象
+ * @param {Object} sources 源对象
+ */
+export function objAssign(target, sources) {
+  let targ = { ...target };
+  for (let k in targ) {
+    targ[k] = Object.prototype.hasOwnProperty.call(sources, k)
+      ? sources[k]
+      : targ[k];
+  }
+  return targ;
 }

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

@@ -80,7 +80,7 @@ const upload = async (files) => {
 
   const res = await uploadFiles(formData, md5).catch(() => {});
   if (res) {
-    return { status: 'success', response: res };
+    return { status: 'success', response: { id: res.id, url: res.previewUrl } };
   } else {
     return { status: 'fail', error: '上传失败' };
   }

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

@@ -5,11 +5,7 @@
     :label-width="isBigTitle || !config.title ? 0 : transLabelWidth"
     class="my-form-item"
   >
-    <div
-      class="top-label flex items-center"
-      :class="{ 'm-t-20px': isBigTitle }"
-      v-if="isBigTitle"
-    >
+    <div v-if="isBigTitle" class="top-label flex items-center">
       <p>{{ config.title }}</p>
     </div>
     <RenderTest></RenderTest>

+ 21 - 5
src/views/sop/sop-manage/office-sop/index.vue

@@ -90,7 +90,7 @@
             <t-link
               theme="primary"
               hover="color"
-              @click="editSopFlowHandle(row, 'add')"
+              @click="editSopFlowHandle(row, 'new')"
             >
               新增SOP
             </t-link>
@@ -148,18 +148,24 @@
       type="new"
       @confirm="fetchData"
     ></PlanChangeDialog>
+    <!-- AddViolationDialog -->
+    <add-violation-dialog
+      v-model:visible="showAddViolationDialog"
+      :sop="curSopData"
+      type="new"
+    ></add-violation-dialog>
   </div>
 </template>
 
 <script setup lang="jsx" name="OfficeSop">
 import { ref, reactive, computed } from 'vue';
 import useFetchTable from '@/hooks/useFetchTable';
-import { useRouter } from 'vue-router';
 import { sopListApi, sopBatchCancelApi } from '@/api/sop';
 import { timestampFilter } from '@/utils/filter';
 import SopStepDialog from '../sop-step/sop-step-dialog.vue';
 import QualityIssueDialog from '../quality-issue/quality-issue-dialog.vue';
 import PlanChangeDialog from '../plan-change/plan-change-dialog.vue';
+import AddViolationDialog from '../../sop-monitor/violation-registration/add-violation-dialog.vue';
 import { useAppStore } from '@/store';
 
 const appStore = useAppStore();
@@ -169,7 +175,6 @@ const selectChange = (value, { selectedRowData }) => {
   selectedRowKeys.value = value;
 };
 
-const router = useRouter();
 const columns = [
   {
     colKey: 'row-select',
@@ -269,8 +274,10 @@ const handleBatchCancel = () => {
   });
 };
 
+const showAddViolationDialog = ref(false);
 const createViolationHandle = (row) => {
-  console.log(row);
+  curSopData.value = row;
+  showAddViolationDialog.value = true;
 };
 
 const showSopStepDialog = ref(false);
@@ -278,7 +285,16 @@ const curSopData = ref({});
 const curSopType = ref('');
 const editSopFlowHandle = (row, type = 'fill') => {
   curSopType.value = type;
-  curSopData.value = row;
+
+  if (type === 'new') {
+    curSopData.value = {
+      ...row,
+      flowDeploymentId:
+        appStore.getFlowDetailByType('OFFICE_SOP_FLOW')?.flowDeploymentId,
+    };
+  } else {
+    curSopData.value = row;
+  }
   showSopStepDialog.value = true;
 };
 

+ 22 - 14
src/views/sop/sop-manage/plan-change/index.vue

@@ -65,8 +65,8 @@
         class="sop-step-body"
         ref="formRef"
         :data="formData"
-        :labelWidth="180"
         :rules="rules"
+        labelAlign="top"
         colon
       >
         <t-row :gutter="[0, 20]">
@@ -109,10 +109,15 @@
             </t-form-item>
           </t-col>
           <t-col :span="12">
-            <t-form-item label="项目信息及计划变更明细">
+            <t-form-item
+              label="项目信息及计划变更明细"
+              name="contentJson"
+              requiredMark
+            >
               <dynamic-table
-                :columns="columns"
                 ref="dTable"
+                v-model="formData.contentJson"
+                :columns="columns"
                 :readonly="readonly"
               ></dynamic-table>
             </t-form-item>
@@ -151,13 +156,12 @@
 </template>
 <script setup name="PlanChange">
 import { ref, computed, reactive } from 'vue';
-import { Input, MessagePlugin } from 'tdesign-vue-next';
-import { useRouter, useRoute } from 'vue-router';
+import { MessagePlugin } from 'tdesign-vue-next';
+import { useRoute } from 'vue-router';
 import dynamicTable from '@/components/common/dynamic-table';
 import { dictToOptionList } from '@/utils/tool';
 import { PLAN_CHANGE_TYPE } from '@/config/constants';
 import { createPlanChange, approvePlanChange } from '@/api/sop';
-import { useAppStore } from '@/store';
 import { omit } from 'lodash';
 
 const dTable = ref();
@@ -192,7 +196,6 @@ const columns = computed(() => [
       type: 'text',
       attrs: {
         clearable: true,
-        disabled: readonly.value,
       },
     },
     rules: [
@@ -206,7 +209,6 @@ const columns = computed(() => [
       type: 'text',
       attrs: {
         clearable: true,
-        disabled: readonly.value,
       },
     },
     rules: [
@@ -225,7 +227,6 @@ const columns = computed(() => [
       type: 'text',
       attrs: {
         clearable: true,
-        disabled: readonly.value,
       },
     },
   },
@@ -238,7 +239,7 @@ const formData = reactive({
   type: 'PLAN',
   flowDeploymentId: '',
   reason: '',
-  contentJson: '',
+  contentJson: [],
   projectExchangeApprove: 'FINISH',
   remark: '',
 });
@@ -263,6 +264,16 @@ const rules = computed(() => {
         trigger: 'change',
       },
     ],
+    contentJson: [
+      {
+        validator: (val) => {
+          if (!val || !val.length)
+            return { result: false, message: '请至少填写一行数据' };
+
+          return { result: true, type: 'success' };
+        },
+      },
+    ],
     remark: [
       {
         validator: (val) => {
@@ -287,12 +298,9 @@ const submitHandle = async () => {
   if (valid !== true) return;
 
   if (IS_NEW_MODE.value) {
-    dTable.value?.validate(() => {});
-
-    let tableData = dTable.value.exportTableData();
     const res = await createPlanChange({
       ...omit(formData, ['projectExchangeApprove', 'remark']),
-      contentJson: JSON.stringify(tableData),
+      contentJson: JSON.stringify(formData.contentJson),
       flowApprove: 'START',
     }).catch(() => {});
     if (!res) return;

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


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


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


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


+ 56 - 23
src/views/sop/sop-manage/sop-step/index.vue

@@ -120,14 +120,14 @@
             >提交</t-button
           >
           <t-button
-            v-if="IS_FILL_MODE"
+            v-if="!IS_EDIT_MODE"
             theme="primary"
             @click="submitHandle('START')"
             >提交</t-button
           >
 
           <t-button
-            v-if="IS_FILL_MODE"
+            v-if="!IS_EDIT_MODE"
             theme="default"
             @click="submitHandle('DRAFT')"
             >保存草稿</t-button
@@ -150,8 +150,10 @@ import {
   sopEditApi,
   sopSaveApi,
   sopApproveApi,
+  sopApplyApi,
+  flowFormPropertiesApi,
 } from '@/api/sop';
-import { deepCopy } from '@/utils/tool';
+import { objCopy } from '@/utils/tool';
 // import bus from '@/utils/bus';
 
 const props = defineProps({
@@ -168,6 +170,9 @@ const props = defineProps({
 });
 const emit = defineEmits(['confirm']);
 
+const IS_NEW_MODE = computed(() => {
+  return props.type === 'new';
+});
 const IS_FILL_MODE = computed(() => {
   return props.type === 'fill';
 });
@@ -215,6 +220,23 @@ const tabs = ref([]);
 const curStep = ref('');
 const flowId = props.sop.flowId;
 
+const initNew = async () => {
+  loading.value = true;
+  const res = await flowFormPropertiesApi({
+    flowDeploymentId: props.sop.flowDeploymentId,
+  });
+  loading.value = false;
+
+  allSteps.value = [res.formProperties];
+  tabs.value = allSteps.value.map((item) => {
+    return {
+      value: item.taskName,
+      label: item.taskName,
+      disabled: false,
+    };
+  });
+  curStep.value = tabs.value.slice(-1)[0].value;
+};
 const initFill = async () => {
   loading.value = true;
   const res = await sopFlowViewApi({ flowId });
@@ -278,7 +300,8 @@ const init = () => {
     // 编辑
     initEdit();
   } else {
-    //还有可能是点击“新增SOP”进来的
+    // 还有可能是点击“新增SOP”进来的
+    initNew();
   }
 };
 init();
@@ -335,21 +358,9 @@ const itemValueChange = ({ prop, value }) => {
 
 const getFormData = () => {
   let data = {};
-  if (IS_FILL_MODE.value) {
-    const curStepData = allSteps.value.find(
-      (item) => item.taskName === curStep.value
-    );
-    data = { ...curStepData };
-    data.formProperty = data.formProperty.map((item) => {
-      return {
-        ...item,
-        value: JSON.stringify({ value: formData.value[item.formName] || null }),
-      };
-    });
-  }
 
   if (IS_EDIT_MODE.value) {
-    data = deepCopy(sopEditDetail.value);
+    data = objCopy(sopEditDetail.value);
     for (const k in data.setupMap) {
       if (Object.hasOwnProperty.call(data.setupMap, k)) {
         const element = data.setupMap[k];
@@ -360,6 +371,18 @@ const getFormData = () => {
         });
       }
     }
+  } else {
+    // 新增 or 填报
+    const curStepData = allSteps.value.find(
+      (item) => item.taskName === curStep.value
+    );
+    data = { ...curStepData };
+    data.formProperty = data.formProperty.map((item) => {
+      return {
+        ...item,
+        value: JSON.stringify({ value: formData.value[item.formName] || null }),
+      };
+    });
   }
 
   return JSON.stringify(data);
@@ -381,12 +404,22 @@ const submitHandle = async (approve = 'START') => {
     if (valid !== true) return;
   }
 
-  const res = await sopApproveApi({
-    taskId: props.sop.taskId,
-    formProperties: getFormData(),
-    approve,
-  }).catch(() => {});
-  if (!res) return;
+  if (IS_FILL_MODE.value) {
+    const res = await sopApproveApi({
+      taskId: props.sop.taskId,
+      formProperties: getFormData(),
+      approve,
+    }).catch(() => {});
+    if (!res) return;
+  } else if (IS_NEW_MODE.value) {
+    const res = await sopApplyApi({
+      crmNo: props.sop.crmNo,
+      flowDeploymentId: props.sop.flowDeploymentId,
+      formProperties: getFormData(),
+      approve,
+    }).catch(() => {});
+    if (!res) return;
+  }
 
   MessagePlugin.success('保存成功');
   emit('confirm');

+ 221 - 0
src/views/sop/sop-monitor/violation-registration/add-violation-dialog.vue

@@ -0,0 +1,221 @@
+<template>
+  <my-drawer
+    class="sop-dialog"
+    :visible="visible"
+    :header="title"
+    size="80%"
+    attach="body"
+    :closeOnOverlayClick="false"
+    :close-btn="true"
+    :footer="false"
+    @close="emit('update:visible', false)"
+  >
+    <div class="sop-step">
+      <t-collapse class="sop-step-mid" defaultExpandAll>
+        <t-collapse-panel disabled>
+          <template #expandIcon></template>
+          <template #header> SOP信息 </template>
+          <t-form colon label-width="72px">
+            <t-row :gutter="[0, 4]">
+              <t-col :span="3">
+                <t-form-item label="服务单元">{{
+                  sop.serviceName
+                }}</t-form-item>
+              </t-col>
+              <t-col :span="3">
+                <t-form-item label="SOP流水号">{{ sop.sopNo }}</t-form-item>
+              </t-col>
+              <t-col :span="3">
+                <t-form-item label="客户类型">{{
+                  sop.customManagerTypeStr
+                }}</t-form-item>
+              </t-col>
+              <t-col :span="3">
+                <t-form-item label="客户名称">{{ sop.customName }}</t-form-item>
+              </t-col>
+              <t-col :span="3">
+                <t-form-item label="项目单号">{{ sop.crmNo }}</t-form-item>
+              </t-col>
+              <t-col :span="3">
+                <t-form-item label="实施产品">{{
+                  sop.productName
+                }}</t-form-item>
+              </t-col>
+              <t-col :span="6">
+                <t-form-item label="项目名称">{{ sop.crmName }}</t-form-item>
+              </t-col>
+            </t-row>
+          </t-form>
+        </t-collapse-panel>
+      </t-collapse>
+
+      <div class="sop-step-list" style="flex-direction: column">
+        <t-form
+          class="sop-step-body"
+          ref="formRef"
+          :data="formData"
+          :rules="rules"
+          labelAlign="top"
+          colon
+        >
+          <t-row :gutter="[0, 20]">
+            <template v-if="!IS_NEW_MODE">
+              <t-col :span="4">
+                <t-form-item label="登记人">
+                  {{ formData.creator }}
+                </t-form-item>
+              </t-col>
+              <t-col :span="4">
+                <t-form-item label="违规登记时间">
+                  {{ timestampFilter(formData.createTime) }}
+                </t-form-item>
+              </t-col>
+            </template>
+
+            <t-col :span="5">
+              <t-form-item label="节点负责人" name="userId">
+                <select-filter-user
+                  v-model="formData.userId"
+                  placeholder="输入关键词搜索选择"
+                  clearable
+                ></select-filter-user>
+              </t-form-item>
+            </t-col>
+            <t-col :span="5" :offset="1">
+              <t-form-item label="违规类型" name="type">
+                <t-select
+                  v-model="formData.type"
+                  :options="dictToOptionList(VIOLATION_TYPE)"
+                >
+                </t-select>
+              </t-form-item>
+            </t-col>
+            <t-col :span="12">
+              <t-form-item label="违规情况说明" name="content">
+                <t-textarea
+                  v-model="formData.content"
+                  :autosize="{ minRows: 3, maxRows: 5 }"
+                  :maxlength="100"
+                ></t-textarea>
+              </t-form-item>
+            </t-col>
+            <t-col :span="12">
+              <t-form-item label="截图说明">
+                <upload-image
+                  :config="{ length: 3 }"
+                  :on-change="imageChange"
+                ></upload-image>
+              </t-form-item>
+            </t-col>
+          </t-row>
+        </t-form>
+        <t-space class="sop-step-footer">
+          <t-button theme="primary" @click="save">提交</t-button>
+          <t-button theme="default" @click="emit('update:visible', false)"
+            >取消</t-button
+          >
+        </t-space>
+      </div>
+    </div>
+  </my-drawer>
+</template>
+
+<script setup name="AddViolationDialog">
+import { ref, computed, reactive, watch } from 'vue';
+import { VIOLATION_TYPE } from '@/config/constants';
+import { dictToOptionList, objAssign } from '@/utils/tool';
+import { timestampFilter } from '@/utils/filter';
+import { saveViolation } from '@/api/sop';
+import UploadImage from '../../components/UPLOAD_IMAGE.vue';
+import { MessagePlugin } from 'tdesign-vue-next';
+
+const emit = defineEmits(['update:visible', 'success']);
+const props = defineProps({
+  visible: Boolean,
+  type: {
+    type: String,
+    default: 'new',
+  },
+  sop: {
+    type: Object,
+    default() {
+      return {};
+    },
+  },
+});
+
+const initFormData = {
+  id: null,
+  creator: '',
+  createTime: null,
+  userId: null,
+  type: '',
+  status: 'NOT_START',
+  content: '',
+  attachmentIds: [],
+  crmNo: '',
+  serviceId: null,
+  sopNo: '',
+};
+
+let formData = reactive(initFormData);
+const formRef = ref(null);
+
+const IS_NEW_MODE = computed(() => {
+  return props.type === 'new';
+});
+const title = computed(() => {
+  return props.type === 'new' ? '新增违规' : '';
+});
+
+const rules = {
+  userId: [
+    {
+      required: true,
+      message: '节点负责人必选',
+      type: 'error',
+      trigger: 'change',
+    },
+  ],
+  type: [
+    { required: true, message: '类型必选', type: 'error', trigger: 'change' },
+  ],
+  content: [
+    {
+      required: true,
+      message: '违规情况说明必填',
+      type: 'error',
+      trigger: 'change',
+    },
+  ],
+};
+
+watch(
+  () => props.visible,
+  (val) => {
+    if (val) {
+      const info = objAssign(
+        { crmNo: '', serviceId: null, sopNo: '' },
+        props.sop
+      );
+      formData = reactive(objAssign(initFormData, info));
+    }
+  }
+);
+
+const imageChange = (data) => {
+  formData.attachmentIds = data.value.map((item) => item.id);
+};
+
+const save = async () => {
+  const valid = await formRef.value.validate();
+  if (valid !== true) return;
+
+  const res = await saveViolation(formData).catch(() => {});
+  if (!res) return;
+
+  MessagePlugin.success('保存成功');
+  emit('update:visible', false);
+  emit('success');
+};
+</script>