Explorar el Código

Merge branch 'release_v1.0.0' of http://git.qmth.com.cn/sop/web into dev_1.0.1

刘洋 hace 1 año
padre
commit
fac8038487

+ 111 - 0
src/components/common/select-free-engineer/index.bat.vue

@@ -0,0 +1,111 @@
+<template>
+  <t-select v-model="selected" clearable v-bind="attrs" @change="onChange">
+    <template v-if="filterable" #panelTopContent>
+      <div style="padding: 10px">
+        <t-input
+          v-model="filterVal"
+          placeholder="请输入关键词搜索"
+          @change="onFilter"
+        />
+      </div>
+    </template>
+    <t-option
+      v-for="item in optionList"
+      :key="item.userId"
+      :value="item.userId"
+      :label="`${item.name}_${item.supplierName || ''}_${item.city || ''}`"
+    />
+  </t-select>
+</template>
+
+<script setup name="SelectFreeEngineer">
+import { onMounted, ref, useAttrs, watch, computed } from 'vue';
+import {
+  personAllocateFreeCoordinatorApi,
+  personAllocateFreeEngineerApi,
+} from '@/api/resource-guard';
+
+let optionList = ref([]);
+let optionFullList = ref([]);
+let selected = ref('');
+let filterVal = ref('');
+
+const attrs = useAttrs();
+
+const emit = defineEmits(['update:modelValue', 'change']);
+const props = defineProps({
+  modelValue: { type: [Number, String, Array], default: '' },
+  filterable: { type: Boolean, default: true },
+  type: { type: String, default: '' },
+  unitId: { type: String, default: '' },
+  crmNo: { type: String, default: '' },
+});
+const isMultiple = computed(() => {
+  const multiple = attrs.multiple;
+  return multiple === '' || multiple;
+});
+
+// bug:第一次复现已选选项时,直接搜索后,选项列表为空时,复现内容展示异常。除非重新选择一个后,在搜索,搜索结果不影响复现内容
+const onFilter = () => {
+  const escapeRegexpString = (value = '') =>
+    String(value).replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
+  const reg = new RegExp(escapeRegexpString(filterVal.value), 'i');
+  optionList.value = optionFullList.value.filter((item) => reg.test(item.name));
+};
+
+const search = async () => {
+  if (!props.type) return;
+  optionFullList.value = [];
+
+  let res = null;
+  if (props.type === 'REGION_COORDINATOR') {
+    res = await personAllocateFreeCoordinatorApi(
+      props.unitId,
+      props.crmNo
+    ).catch(() => {});
+    if (!res) return;
+  } else {
+    res = await personAllocateFreeEngineerApi(props.type, props.crmNo).catch(
+      () => {}
+    );
+    if (!res) return;
+  }
+
+  optionFullList.value = res;
+  onFilter();
+};
+
+const onChange = () => {
+  const selectedData = isMultiple.value
+    ? optionList.value.filter(
+        (item) => selected.value && selected.value.includes(item.userId)
+      )
+    : optionList.value.filter((item) => selected.value === item.userId);
+  emit('update:modelValue', selected.value);
+  emit('change', isMultiple.value ? selectedData : selectedData[0]);
+};
+
+onMounted(() => {
+  search();
+});
+
+watch(
+  () => props.modelValue,
+  (val) => {
+    selected.value = val;
+  },
+  {
+    immediate: true,
+  }
+);
+watch(
+  () => props.type,
+  (val, oldval) => {
+    if (val !== oldval) {
+      search();
+      emit('update:modelValue', null);
+      emit('change', isMultiple.value ? [] : null);
+    }
+  }
+);
+</script>

+ 1 - 1
src/components/common/select-free-engineer/index.vue

@@ -10,7 +10,7 @@
       v-for="item in optionList"
       :key="item.userId"
       :value="item.userId"
-      :label="item.name"
+      :label="`${item.name}_${item.supplierName || ''}_${item.city || ''}`"
     />
   </t-select>
 </template>

+ 2 - 1
src/main.js

@@ -5,7 +5,7 @@ import App from './App.vue';
 import router from './router';
 import store from './store';
 import directives from './directives';
-import { capsule } from '@/utils/tool';
+import { capsule, appUpdateObserver } from '@/utils/tool';
 import { Loading } from 'tdesign-vue-next';
 import 'virtual:svg-icons-register';
 import 'tdesign-vue-next/es/style/index.css';
@@ -28,3 +28,4 @@ app.config.globalProperties.$title = import.meta.env.VITE_APP_TITLE;
 app.mount('#app');
 
 capsule('质控平台', `v${packageJson.version} release`);
+appUpdateObserver();

+ 36 - 0
src/utils/tool.js

@@ -1,5 +1,6 @@
 import { cloneDeep } from 'lodash';
 import { useUserStore } from '@/store';
+import { DialogPlugin } from 'tdesign-vue-next';
 
 export const extractFileName = (str) => {
   if (/filename=([^;\s]*)/gi.test(str)) {
@@ -513,3 +514,38 @@ export function isEqual(objA, objB) {
       return false;
   }
 }
+
+export const appUpdateObserver = () => {
+  window.addEventListener(
+    'error',
+    (e) => {
+      if (e.target.localName === 'script' || e.target.localName === 'link') {
+        DialogPlugin({
+          header: '提示',
+          body: `检测到系统功能已升级,点击"确定"自动刷新获取更新`,
+          confirmBtn: '确定',
+          cancelBtn: null,
+          onConfirm: async () => {
+            if ('caches' in window) {
+              caches
+                .keys()
+                .then((cacheNames) => {
+                  const arr = cacheNames.map((cacheName) => {
+                    return caches.delete(cacheName);
+                  });
+                  return arr;
+                })
+                .then(() => {
+                  location.reload();
+                });
+            } else {
+              location.reload();
+            }
+          },
+        });
+      }
+      e.preventDefault();
+    },
+    true
+  );
+};

+ 11 - 6
src/views/resource-guard/person-guard/person-allocate/person-deploy-dialog.vue

@@ -2,7 +2,7 @@
   <my-dialog
     :visible="visible"
     header="调配"
-    :width="600"
+    :width="800"
     attach="body"
     :closeOnOverlayClick="false"
     @close="emit('update:visible', false)"
@@ -57,9 +57,15 @@
                 bordered
               >
                 <template #num="{ row }">
-                  <span>{{ row.userIdList.length }}</span>
-                  <span>/</span>
-                  <span>{{ row.quota }}</span>
+                  <div
+                    :class="{
+                      'color-error': row.userIdList.length > row.quota,
+                    }"
+                  >
+                    <span>{{ row.userIdList.length }}</span>
+                    <span>/</span>
+                    <span>{{ row.quota }}</span>
+                  </div>
                 </template>
                 <template #users="{ row }">
                   <select-free-engineer
@@ -68,8 +74,7 @@
                     :unit-id="curRow.serviceUnitId"
                     :crm-no="curRow.crmNo"
                     multiple
-                    :min-collapsed-num="3"
-                    :filterable="false"
+                    :min-collapsed-num="2"
                     :max="row.roleType === 'REGION_COORDINATOR' ? 1 : 0"
                   ></select-free-engineer>
                 </template>

+ 3 - 3
src/views/service-unit/service-unit-manage/unit-manage/index.vue

@@ -61,7 +61,7 @@
             </template>
             <template v-if="perm.LINK_Publish">
               <t-link
-                :disabled="row.status !== 'NEW'"
+                :disabled="row.status !== 'NEW' && row.status !== 'FINISH'"
                 theme="primary"
                 hover="color"
                 @click="handlePublish(row)"
@@ -79,7 +79,7 @@
                 作废
               </t-link>
             </template>
-            <template v-if="perm.LINK_Restart">
+            <!-- <template v-if="perm.LINK_Restart">
               <t-link
                 :disabled="row.status !== 'FINISH'"
                 theme="primary"
@@ -88,7 +88,7 @@
               >
                 重启
               </t-link>
-            </template>
+            </template> -->
             <template v-if="perm.LINK_Close">
               <t-link
                 :disabled="row.status !== 'PUBLISH'"

+ 5 - 2
src/views/sop/components/dynamic-form-item/SIGN.vue

@@ -44,7 +44,7 @@ const getSignResultImg = () => {
     // getAttachmentListByKey('1').then((res) => {
     if (Array.isArray(res)) {
       if (res && res[0]?.url) {
-        valueData.value = res[0].url;
+        valueData.value = res[res.length - 1].url;
         emitChange();
         pause();
       }
@@ -62,7 +62,10 @@ onBeforeUnmount(() => {
   pause();
 });
 onMounted(() => {
-  key.value = userStore.user?.id + '_' + new Date().getTime();
+  key.value =
+    location.host === 'sop.qmth.com.cn'
+      ? userStore.user?.id + '_' + new Date().getTime()
+      : '1';
   valueData.value = props.modelValue;
 });
 const emitChange = () => {

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

@@ -38,7 +38,13 @@
     </div>
     <SearchForm :fields="fields" :params="params">
       <template #service="{ item, params }">
-        <select-service-unit v-model="params[item.prop]"></select-service-unit>
+        <select-service-unit
+          v-model="params[item.prop]"
+          :filterParams="{
+            statusList: ['PUBLISH', 'FINISH'],
+            type: 'OFFICE',
+          }"
+        ></select-service-unit>
       </template>
       <template #buttons>
         <t-space :size="16">

+ 7 - 1
src/views/sop/sop-manage/student-sop/index.vue

@@ -38,7 +38,13 @@
     </div>
     <SearchForm :fields="fields" :params="params">
       <template #service="{ item, params }">
-        <select-service-unit v-model="params[item.prop]"></select-service-unit>
+        <select-service-unit
+          v-model="params[item.prop]"
+          :filterParams="{
+            statusList: ['PUBLISH', 'FINISH'],
+            type: 'CLOUD_MARK',
+          }"
+        ></select-service-unit>
       </template>
       <template #buttons>
         <t-space :size="16">

+ 51 - 9
src/views/system/config-manage/checkin-manage/edit-checkin-dialog.vue

@@ -26,8 +26,27 @@
             </select-role>
           </t-form-item>
         </t-col>
-        <t-col :span="6">
-          <t-form-item label="适用供应商" name="supplierId">
+        <t-col :span="12">
+          <t-form-item
+            v-if="!isEdit"
+            label="适用供应商"
+            name="supplierIdList"
+            required-mark
+          >
+            <select-supplier
+              v-model="formData.supplierIdList"
+              multiple
+              :min-collapsed-num="2"
+              type="HUMAN"
+            >
+            </select-supplier>
+          </t-form-item>
+          <t-form-item
+            v-else
+            label="适用供应商"
+            name="supplierId"
+            required-mark
+          >
             <select-supplier v-model="formData.supplierId" type="HUMAN">
             </select-supplier>
           </t-form-item>
@@ -39,7 +58,7 @@
               format="HH:mm"
               class="width-full"
               clearable
-              @change="signTimeChange"
+              @change="signInTimeChange"
             />
           </t-form-item>
         </t-col>
@@ -56,7 +75,7 @@
               format="HH:mm"
               class="width-full"
               clearable
-              @change="signTimeChange"
+              @change="signOutTimeChange"
             />
           </t-form-item>
         </t-col>
@@ -135,8 +154,8 @@ const { formData, isEdit } = useClearDialog(
     serviceId: '',
     dingRoleIds: [],
     supplierId: '',
-    signInTime: ['06:00', '10:00'],
-    signOutTime: ['16:00', '23:59'],
+    signInTime: ['', ''],
+    signOutTime: ['', ''],
     approveRoleIds: [],
     faceOpen: false,
     reissueCardCount: 2,
@@ -197,6 +216,18 @@ const rules = {
       trigger: 'change',
     },
   ],
+  supplierIdList: [
+    {
+      validator: () => {
+        if (!formData.supplierIdList || !formData.supplierIdList.length)
+          return { result: false, message: '适用供应商必选' };
+
+        return { result: true, type: 'success' };
+      },
+      type: 'error',
+      trigger: 'change',
+    },
+  ],
   // signInTime: [
   //   {
   //     required: true,
@@ -252,7 +283,13 @@ const rules = {
   ],
 };
 
-const signTimeChange = () => {
+const signInTimeChange = () => {
+  formRef.value.validate({ fields: ['signInOutTime'] });
+  formData.signOutTime = formData.signInTime
+    ? [formData.signInTime[1], formData.signInTime[0]]
+    : ['', ''];
+};
+const signOutTimeChange = () => {
   formRef.value.validate({ fields: ['signInOutTime'] });
 };
 const save = async () => {
@@ -263,8 +300,13 @@ const save = async () => {
   for (let key in formData) {
     data[key] = formData[key];
   }
-  data.signInTime = formData.signInTime ? formData.signInTime.join('~') : '';
-  data.signOutTime = formData.signInTime ? formData.signOutTime.join('~') : '';
+  const checkTime = (data) => data && !data.some((item) => !item);
+  data.signInTime = checkTime(formData.signInTime)
+    ? formData.signInTime.join('~')
+    : '';
+  data.signOutTime = checkTime(formData.signOutTime)
+    ? formData.signOutTime.join('~')
+    : '';
   data.dingObjs = [
     ...formData.dingRoleIds.map((item) => {
       return { roleId: item, type: 'DING' };

+ 1 - 0
src/views/system/config-manage/checkin-manage/index.vue

@@ -97,6 +97,7 @@
               v-if="perm.LINK_Delete"
               theme="primary"
               hover="color"
+              :disabled="row.status !== 'NEW'"
               @click="handleDelete(row)"
             >
               删除

+ 3 - 3
src/views/system/config-manage/service-level-manage/index.vue

@@ -55,7 +55,7 @@
               hover="color"
               @click="handleDelete(row)"
             >
-              删除
+              作废
             </t-link>
           </div>
         </template>
@@ -122,7 +122,7 @@ const handleEdit = (row) => {
 const handleDelete = (row) => {
   const confirmDia = DialogPlugin({
     header: '操作提示',
-    body: `确定要删除当前记录吗`,
+    body: `确定要作废当前记录吗`,
     confirmBtn: '确定',
     cancelBtn: '取消',
     theme: 'warning',
@@ -130,7 +130,7 @@ const handleDelete = (row) => {
       confirmDia.hide();
       const res = await serviceLevelDeleteApi(row.id).catch(() => {});
       if (!res) return;
-      MessagePlugin.success('删除成功');
+      MessagePlugin.success('作废成功');
       fetchData();
     },
   });