소스 검색

Merge branch 'master' of http://git.qmth.com.cn/sop/web into dev_v1.0.0

刘洋 1 년 전
부모
커밋
88e42f6ae5

+ 8 - 0
src/api/common.js

@@ -12,3 +12,11 @@ export const uploadFiles = (formData, md5) => {
     },
   });
 };
+
+export const downloadImportTemplate = (params) => {
+   request({
+    url: '/api/admin/common/download_import_template',
+    params: params,
+    download:true,
+  });
+};

+ 6 - 0
src/api/my-workbenches.js

@@ -16,3 +16,9 @@ export const getMyWaits = (data) =>
     url: '/api/admin/flow/task/list',
     params: data,
   });
+
+export const setMyMessagesRead = (id) =>
+    request({
+        url: '/api/sys/message/read',
+        params: { id },
+    });

+ 84 - 0
src/components/common/select-url-user/index.vue

@@ -0,0 +1,84 @@
+<template>
+  <t-select
+    v-model="selected"
+    filterable
+    clearable
+    v-bind="attrs"
+    @change="onChange"
+  >
+    <t-option
+      v-for="item in optionList"
+      :key="item.id"
+      :value="item.id"
+      :label="item.realName"
+    />
+  </t-select>
+</template>
+
+<script setup name="SelectUrlUser">
+import { onMounted, ref, useAttrs, watch, computed } from 'vue';
+import {request} from "@/utils/request";
+import {isEqual} from "@/utils/tool";
+
+let optionList = ref([]);
+let selected = ref('');
+
+const attrs = useAttrs();
+
+const emit = defineEmits(['update:modelValue', 'change']);
+const props = defineProps({
+  modelValue: { type: [Number, String, Array], default: '' },
+  url: { type: String, default: '' },
+  params: { type: Object, default: {} },
+});
+const isMultiple = computed(() => {
+  const multiple = attrs.multiple;
+  return multiple === '' || multiple;
+});
+
+const search = async () => {
+  if (!props.url) return;
+  optionList.value = [];
+
+  const res = await request({ url: props.url, params: props.params }).catch(() => {});
+  // const res = await getRoleUserList(props.type).catch(() => {});
+  if (!res) return;
+
+  optionList.value = res;
+};
+
+const onChange = () => {
+  const selectedData = isMultiple.value
+    ? optionList.value.filter(
+        (item) => selected.value && selected.value.includes(item.id)
+      )
+    : optionList.value.filter((item) => selected.value === item.id);
+  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.params,
+  (val, oldval) => {
+    if (!isEqual(val ,oldval)) {
+      search();
+      emit('update:modelValue', null);
+      emit('change', isMultiple.value ? [] : null);
+    }
+  }
+);
+
+</script>

+ 1 - 1
src/components/common/upload-button/index.vue

@@ -50,7 +50,7 @@ const props = defineProps({
   headers: {
     type: Object,
     default() {
-      return {};
+      return {'Content-Type': 'multipart/form-data'};
     },
   },
   buttonProps: {

+ 48 - 0
src/utils/tool.js

@@ -461,3 +461,51 @@ export function timeNumberToText(timeNumber) {
 
   return [day, hour, minute, second].filter((item) => !!item).join('');
 }
+
+//判断两个对象是否相等
+export function isEqual(objA,objB){
+  //相等
+  if(objA === objB) return objA !== 0 || 1/objA === 1/objB;
+  //空判断
+  if(objA == null || objB == null) return objA === objB;
+  //类型判断
+  if(Object.prototype.toString.call(objA) !== Object.prototype.toString.call(objB)) return false;
+
+  switch(Object.prototype.toString.call(objA)){
+    case '[object RegExp]':
+    case '[object String]':
+      //字符串转换比较
+      return '' + objA ==='' + objB;
+    case '[object Number]':
+      //数字转换比较,判断是否为NaN
+      if(+objA !== +objA){
+        return +objB !== +objB;
+      }
+
+      return +objA === 0?1/ +objA === 1/objB : +objA === +objB;
+    case '[object Date]':
+    case '[object Boolean]':
+      return +objA === +objB;
+    case '[object Array]':
+      //判断数组
+      for(let i = 0; i < objA.length; i++){
+        if (!isEqual(objA[i],objB[i])) return false;
+      }
+      return true;
+    case '[object Object]':
+      //判断对象
+      let keys = Object.keys(objA);
+      for(let i = 0; i < keys.length; i++){
+        if (!isEqual(objA[keys[i]],objB[keys[i]])) return false;
+      }
+
+      keys = Object.keys(objB);
+      for(let i = 0; i < keys.length; i++){
+        if (!isEqual(objA[keys[i]],objB[keys[i]])) return false;
+      }
+
+      return true;
+    default :
+      return false;
+  }
+}

+ 11 - 5
src/views/my-workbenches/workbenches/message-reminder/index.vue

@@ -28,6 +28,7 @@
         :tableData="tableData"
         :pagination="pagination"
         :onChange="onChange"
+        @open="open"
       ></MessageList>
     </div>
   </div>
@@ -36,12 +37,19 @@
 <script setup name="MessageReminder">
 import { reactive, computed, ref } from 'vue';
 import useFetchTable from '@/hooks/useFetchTable';
-import { getMyMessages } from '@/api/my-workbenches';
+import {getMyMessages, setMyMessagesRead} from '@/api/my-workbenches';
 import { MESSAGE_TYPE } from '@/config/constants';
 import { dictToOptionList } from '@/utils/tool';
 import MessageList from './message-list.vue';
 import { useWorkStore } from '@/store';
+import NoticeList from "@/views/my-workbenches/workbenches/notice/notice-list.vue";
 
+const open = (notice) => {
+    setMyMessagesRead(notice.id).then(()=> {
+      search()
+      workStore.updateWorkCounts();
+    })
+};
 const workStore = useWorkStore();
 const tabs = [
   {
@@ -66,10 +74,8 @@ const params = reactive({
 });
 
 const transParams = computed(() => {
-  let types = params.types.join(',');
-  if (!types.length && params.status === 'undefined') {
-    types = Object.keys(MESSAGE_TYPE).join();
-  }
+  // let types = params.types.join(',');
+  let types = Object.keys(MESSAGE_TYPE).join();
   let status = eval(params.status);
   return { ...params, types, status };
 });

+ 6 - 0
src/views/my-workbenches/workbenches/message-reminder/message-list.vue

@@ -3,6 +3,7 @@
     <div class="message-item" v-for="item in tableData" :key="item.id">
       <div class="m-head">
         <div class="m-title">
+          <t-link hover="color" @click="open(item)">
           <span>{{ messageTypeFilter(item.messageType) }}</span>
           <t-tag
             theme="danger"
@@ -11,6 +12,7 @@
             v-if="!item.readStatus"
             >未读</t-tag
           >
+          </t-link>
         </div>
         <div class="m-time">{{ timestampFilter(item.sendTime, 'mm') }}</div>
       </div>
@@ -54,4 +56,8 @@ const { tableData, pagination, onChange } = defineProps([
   'pagination',
   'onChange',
 ]);
+const emit = defineEmits(['open']);
+const open = (row) => {
+  emit('open', row);
+};
 </script>

+ 5 - 1
src/views/my-workbenches/workbenches/notice/index.vue

@@ -57,7 +57,7 @@
 <script setup name="Notice">
 import { reactive, computed, ref } from 'vue';
 import useFetchTable from '@/hooks/useFetchTable';
-import { getMyMessages } from '@/api/my-workbenches';
+import {getMyMessages, setMyMessagesRead} from '@/api/my-workbenches';
 import NoticeList from './notice-list.vue';
 import { omit } from 'lodash';
 import { dateFormat } from '@/utils/tool';
@@ -70,6 +70,10 @@ const visible = ref(false);
 const open = (notice) => {
   curNotice.value = notice;
   visible.value = true;
+  setMyMessagesRead(curNotice.value.id).then(()=> {
+    search()
+    workStore.updateWorkCounts();
+  })
 };
 
 const tabs = [

+ 4 - 3
src/views/project-quality/project-quality-manage/issues-feedback/index.vue

@@ -34,7 +34,7 @@
         :selected-row-keys="selectedRowKeys"
         @select-change="selectChange"
       >
-        <template #type="{ col, row }">
+        <template #custom-type="{ col, row }">
           {{ customerTypeFilter(row[col.colKey]) }}
         </template>
         <template #flow-status="{ col, row }">
@@ -234,9 +234,10 @@ const columns = [
   },
   { colKey: 'problemNo', title: '质量问题编号', width: 200 },
   { colKey: 'crmNo', title: '项目单号', width: 200 },
-  { colKey: 'customType', title: '客户类型', cell: 'type', width: 120 },
+  { colKey: 'customType', title: '客户类型', cell: 'custom-type', width: 120 },
   { colKey: 'custom', title: '客户名称', width: 140 },
-  { colKey: 'productName', title: '实施产品' },
+  // { colKey: 'productName', title: '实施产品' },
+  { colKey: 'sopTypeStr', title: '实施产品' },
   { colKey: 'summary', title: '问题简要' },
   { colKey: 'userNames', title: '责任人', width: 140 },
   { colKey: 'type', title: '问题类型', cell: 'issues-type', width: 120 },

+ 4 - 3
src/views/project-quality/project-quality-manage/issues-query/index.vue

@@ -22,7 +22,7 @@
           current: pagination.pageNumber,
         }"
       >
-        <template #type="{ col, row }">
+        <template #custom-type="{ col, row }">
           {{ customerTypeFilter(row[col.colKey]) }}
         </template>
         <template #issues-type="{ col, row }">
@@ -175,9 +175,10 @@ const computedParams = computed(() => {
 const columns = [
   { colKey: 'problemNo', title: '质量问题编号' },
   { colKey: 'crmNo', title: '项目单号' },
-  { colKey: 'customType', title: '客户类型', cell: 'type', width: 100 },
+  { colKey: 'customType', title: '客户类型', cell: 'custom-type', width: 100 },
   { colKey: 'custom', title: '客户名称' },
-  { colKey: 'sopType', title: '实施产品' },
+  // { colKey: 'sopType', title: '实施产品' },
+  { colKey: 'sopTypeStr', title: '实施产品' },
   { colKey: 'summary', title: '问题简要' },
   { colKey: 'userName', title: '责任人' },
   { colKey: 'type', title: '问题类型', cell: 'issues-type', width: 120 },

+ 11 - 5
src/views/sop/sop-monitor/violation-registration/add-violation-dialog.vue

@@ -61,11 +61,17 @@
           <t-row :gutter="[0, 20]">
             <t-col :span="5">
               <t-form-item label="节点负责人" name="userId">
-                <select-filter-user
-                  v-model="formData.userId"
-                  placeholder="输入关键词搜索选择"
-                  clearable
-                ></select-filter-user>
+<!--                <select-filter-user-->
+<!--                  v-model="formData.userId"-->
+<!--                  placeholder="输入关键词搜索选择"-->
+<!--                  clearable-->
+<!--                ></select-filter-user>-->
+                <select-url-user
+                    v-model="formData.userId"
+                    url="/api/admin/sop/user/list"
+                    :params="{sop_info_id:sop.id}"
+                    clearable
+                ></select-url-user>
               </t-form-item>
             </t-col>
             <t-col :span="5" :offset="1">

+ 2 - 2
src/views/sop/sop-monitor/violation-registration/flow-dialog.vue

@@ -26,7 +26,7 @@
               </t-col>
               <t-col :span="3">
                 <t-form-item label="客户类型">{{
-                  curRow.customType
+                    customerTypeFilter(curRow.customType)
                 }}</t-form-item>
               </t-col>
               <t-col :span="3">
@@ -149,7 +149,7 @@ import { MessagePlugin } from 'tdesign-vue-next';
 import { ref, computed } from 'vue';
 import { useUserStore } from '@/store';
 import { violationDetailList, flowViolation } from '@/api/sop';
-import { timestampFilter, violationTypeFilter } from '@/utils/filter';
+import {customerTypeFilter, timestampFilter, violationTypeFilter} from '@/utils/filter';
 
 const userStore = useUserStore();
 

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

@@ -59,7 +59,7 @@
         v-loading="tableLoading"
       >
         <template #face-open="{ item, row }">
-          {{ row[columns.colKey] ? '是' : '否' }}
+          {{ row.faceOpen ? '是' : '否' }}
         </template>
         <template #ding-objs="{ row }">
           {{

+ 6 - 0
src/views/system/config-manage/customer-manage/index.vue

@@ -9,12 +9,17 @@
         <upload-button
           v-if="perm.BUTTON_BatchImport"
           upload-url="/api/sys/custom/import"
+          :format="['xlsx']"
         >
           <t-button variant="outline">
             <template #icon><svg-icon name="import" color="#262626" /></template
             >批量导入
           </t-button>
         </upload-button>
+        <t-button v-if="perm.BUTTON_BatchImport"  variant="outline" @click="downloadImportTemplate({type:'TEMPLATE_CUSTOM'})">
+          <template #icon><svg-icon name="export" color="#262626" /></template
+          >下载模版
+        </t-button>
       </t-space>
     </div>
     <SearchForm :fields="fields" :params="params">
@@ -98,6 +103,7 @@ import { CUSTOMER_TYPE } from '@/config/constants';
 import { dictToOptionList } from '@/utils/tool';
 import { customerTypeFilter, timestampFilter } from '@/utils/filter';
 import usePermission from '@/hooks/usePermission';
+import {downloadImportTemplate} from "@/api/common";
 const { perm } = usePermission();
 
 const showEditCustomerDialog = ref(false);

+ 13 - 5
src/views/system/config-manage/device-manage/index.vue

@@ -16,7 +16,7 @@
           批量作废
         </t-button>
         <t-button
-          v-if="perm.BUTTON_enable"
+          v-if="perm.BUTTON_BatchEnable"
           variant="outline"
           :disabled="!selectedRowKeys.length"
           @click="handleEnable(selectedRowKeys, true)"
@@ -25,7 +25,7 @@
           批量启用
         </t-button>
         <t-button
-          v-if="perm.BUTTON_disable"
+          v-if="perm.BUTTON_BatchUnEnable"
           variant="outline"
           :disabled="!selectedRowKeys.length"
           @click="handleEnable(selectedRowKeys, false)"
@@ -34,14 +34,19 @@
           批量禁用
         </t-button>
         <upload-button
-          v-if="perm.BUTTON_import"
+          v-if="perm.BUTTON_BatchImport"
           upload-url="/api/sys/device/import"
+          :format="['xlsx']"
         >
           <t-button variant="outline">
             <template #icon><svg-icon name="import" color="#262626" /></template
             >批量导入
           </t-button>
         </upload-button>
+        <t-button v-if="perm.BUTTON_BatchImport"  variant="outline" @click="downloadImportTemplate({type:'TEMPLATE_DEVICE'})">
+          <template #icon><svg-icon name="export" color="#262626" /></template
+          >下载模版
+        </t-button>
       </t-space>
     </div>
     <SearchForm :fields="fields" :params="params">
@@ -133,7 +138,7 @@
 </template>
 
 <script setup name="DeviceManage">
-import { ref, reactive, onMounted } from 'vue';
+import {ref, reactive, onMounted, computed} from 'vue';
 import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
 import useFetchTable from '@/hooks/useFetchTable';
 import EditDeviceDialog from './edit-device-dialog.vue';
@@ -151,6 +156,8 @@ import {
 import { dictToOptionList } from '@/utils/tool';
 import { ABLE_TYPE, RUNNING_STATUS, INOUT_TYPE } from '@/config/constants';
 import usePermission from '@/hooks/usePermission';
+import {omit} from "lodash";
+import {downloadImportTemplate} from "@/api/common";
 const { perm } = usePermission();
 
 const showEditDeviceDialog = ref(false);
@@ -306,7 +313,8 @@ const {
   fetchData,
   search,
   onChange,
-} = useFetchTable(deviceQueryApi);
+} = useFetchTable(deviceQueryApi,{ params: params });
+
 
 const selectChange = (value) => {
   selectedRowKeys.value = value;

+ 19 - 2
src/views/system/notice-log/notice-manage/edit-notice-dialog.vue

@@ -28,12 +28,20 @@
             </t-select>
           </t-form-item>
         </t-col>
-        <t-col :span="4">
+        <t-col :span="4" v-if="formData.type==='SERVICE'">
           <t-form-item label="服务单元" name="serviceId">
             <select-service-unit v-model="formData.serviceId">
             </select-service-unit>
           </t-form-item>
         </t-col>
+        <t-col :span="4" v-if="formData.type==='SUPPLIER'">
+          <t-form-item label="人力供应商" name="supplierId">
+            <select-supplier v-model="formData.supplierId" type="HUMAN">
+            </select-supplier>
+          </t-form-item>
+        </t-col>
+      </t-row>
+        <t-row>
         <t-col :span="8">
           <t-form-item label="通知名称" name="title">
             <t-input v-model="formData.title" clearable />
@@ -67,7 +75,7 @@
           >返回</t-button
         >
         <div>
-          <t-button theme="default" @click="save">保存草稿</t-button>
+          <t-button theme="default" @click="save(false)">保存草稿</t-button>
           <t-button theme="primary" ghost @click="onPreview">预览</t-button>
           <t-button theme="primary" @click="save(true)">保存并发布</t-button>
         </div>
@@ -99,6 +107,7 @@ const { formData, isEdit } = useClearDialog(
     id: null,
     type: '',
     serviceId: '',
+    supplierId: '',
     title: '',
     content: '',
     status: 'UN_PUBLISH',
@@ -132,6 +141,14 @@ const rules = {
       trigger: 'change',
     },
   ],
+  supplierId: [
+    {
+      required: true,
+      message: '人力供应商必选',
+      type: 'error',
+      trigger: 'change',
+    },
+  ],
   content: [
     { required: true, message: '正文必填', type: 'error', trigger: 'change' },
     {