Преглед на файлове

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

刘洋 преди 1 година
родител
ревизия
47a12f0b1d

+ 8 - 0
src/api/resource-guard.js

@@ -108,6 +108,14 @@ export const deviceSendStatusUpdateApi = (params) =>
     params,
     paramsSerializer,
   });
+//批量返还
+export const deviceSendReturn = (params) =>
+  request({
+    url: '/api/admin/device/delivery/can_return_info',
+    params,
+    paramsSerializer,
+    loading: true,
+  });
 export const deviceSendExportApi = (data) =>
   request({
     url: '/api/admin/device/delivery/export',

+ 1 - 0
src/config/constants.js

@@ -241,4 +241,5 @@ export const DEVICE_SEND_STATUS = {
   DELIVER: '已发货',
   RECEIVE: '已签收',
   // CANCEL: '作废',
+  RETURN: '已返还',
 };

+ 62 - 5
src/views/resource-guard/device-guard/device-send/index.vue

@@ -47,8 +47,7 @@
           :disabled="!selectedRowKeys.length"
           @click="handleOperate('RECEIVE')"
         >
-          <template #icon><svg-icon name="delete" color="#262626" /></template
-          >批量签收
+          <template #icon><MarkAsUnreadIcon /></template>批量签收
         </t-button>
         <t-button
           v-if="perm.BUTTON_BatchCancel"
@@ -56,8 +55,14 @@
           :disabled="!selectedRowKeys.length"
           @click="handleOperate('CANCEL')"
         >
-          <template #icon><svg-icon name="delete" color="#262626" /></template
-          >批量作废
+          <template #icon><CloseOctagonIcon /></template>批量作废
+        </t-button>
+        <t-button
+          v-if="perm.BUTTON_BatchReturn"
+          variant="outline"
+          @click="handleReturn(selectedRowKeys)"
+        >
+          <template #icon><RollfrontIcon /></template>批量返还
         </t-button>
       </t-space>
     </div>
@@ -155,6 +160,19 @@
             >
               签收
             </t-link>
+            <t-link
+              theme="primary"
+              hover="color"
+              v-if="
+                row.status == 'RECEIVE' &&
+                row.usageType === 'OTHER' &&
+                row.deliveryType === 'OUT' &&
+                perm.LINK_Return
+              "
+              @click="handleReturn([row.id], true)"
+            >
+              返还
+            </t-link>
           </div>
         </template>
       </t-table>
@@ -164,6 +182,12 @@
       :curRow="curRow"
       @success="fetchData"
     ></EditDeviceInfoDialog>
+
+    <DeviceTableDialog
+      :data="waitReturnData"
+      v-model:visible="showWaiReturnDataDialog"
+      @success="returnSuccess"
+    ></DeviceTableDialog>
   </div>
 </template>
 
@@ -172,18 +196,25 @@ import { ref, reactive, computed, onMounted } from 'vue';
 import { omit } from 'lodash';
 
 import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
-import { ErrorCircleFilledIcon } from 'tdesign-icons-vue-next';
+import {
+  ErrorCircleFilledIcon,
+  RollfrontIcon,
+  CloseOctagonIcon,
+  MarkAsUnreadIcon,
+} from 'tdesign-icons-vue-next';
 import useFetchTable from '@/hooks/useFetchTable';
 import {
   deviceSendStatusUpdateApi,
   deviceSendListApi,
   deviceSendExportApi,
+  deviceSendReturn,
 } from '@/api/resource-guard';
 import { DEVICE_SEND_STATUS, DEVICE_USAGE_TYPE } from '@/config/constants';
 import usePermission from '@/hooks/usePermission';
 import EditDeviceInfoDialog from './edit-device-info-dialog.vue';
 import { dictToOptionList } from '@/utils/tool';
 import { downloadImportTemplate } from '@/api/common';
+import DeviceTableDialog from '@/views/sop/sop-manage/device-out-in/add-device-dialog.vue';
 const { perm } = usePermission();
 const tableKey = ref(Date.now() + '');
 const curRow = ref(null);
@@ -449,6 +480,28 @@ const handleEdit = (row) => {
   curRow.value = row;
   showEditDeviceDialog.value = true;
 };
+const waitReturnData = ref([]);
+const showWaiReturnDataDialog = ref(false);
+const handleReturn = (ids = [], single) => {
+  let confirmBody = !ids.length
+    ? `确定要返还当所有记录吗?`
+    : `确定要返还当前${single ? '' : '选中的'}记录吗?`;
+  const confirmDia = DialogPlugin({
+    header: '操作提示',
+    body: confirmBody,
+    confirmBtn: '确定',
+    cancelBtn: '取消',
+    onConfirm: async () => {
+      confirmDia.hide();
+      const res = await deviceSendReturn({
+        idList: ids,
+      }).catch(() => {});
+      if (!res) return;
+      Array.isArray(res) && (waitReturnData.value = res);
+      showWaiReturnDataDialog.value = true;
+    },
+  });
+};
 const handleOperate = (status, ids) => {
   let str =
     status === 'DELIVER'
@@ -497,4 +550,8 @@ const multExport = () => {
     },
   });
 };
+const returnSuccess = () => {
+  showWaiReturnDataDialog.value = false;
+  fetchData();
+};
 </script>

+ 31 - 51
src/views/sop/sop-manage/device-out-in/add-device-dialog.vue

@@ -1,50 +1,20 @@
+<!-- SOP管理=》设备入库登记页面,已不再需要该文件,因为去掉了新增按钮 -->
+<!-- 此文件改为 资源保障=》设备发货管理 页面所用,逻辑也有少量更改 -->
+
 <template>
   <my-dialog
     :visible="visible"
     @close="emit('update:visible', false)"
-    header="新增设备出入库信息"
+    header="待返还设备"
     :width="1200"
     :closeOnOverlayClick="false"
   >
-    <t-form ref="formRef" :data="formData" :rules="rules" :labelWidth="120">
+    <t-form ref="formRef" :data="formData" :rules="rules" :labelWidth="0">
       <t-row :gutter="[0, 20]">
-        <t-col :span="6">
-          <t-form-item label="设备出入库选择">
-            <t-radio-group v-model="formData.inOutType" style="width: 100%">
-              <t-radio value="OUT">出库</t-radio>
-              <t-radio value="IN">入库</t-radio>
-            </t-radio-group>
-          </t-form-item>
-        </t-col>
-        <t-col :span="6">
-          <t-form-item label="设备出入库时间" name="inOutTime">
-            <t-date-picker
-              v-model="formData.inOutTime"
-              style="width: 100%"
-              value-type="time-stamp"
-              enable-time-picker
-              format="YYYY/MM/DD HH:mm"
-              :time-picker-props="{ format: 'HH:mm' }"
-            />
-          </t-form-item>
-        </t-col>
-        <t-col :span="12">
-          <t-form-item label="设备出入库登记" name="pushSupplier">
-            <t-radio-group
-              v-model="formData.pushSupplier"
-              name="pushSupplier"
-              :options="[
-                { value: true, label: '是' },
-                { value: false, label: '否' },
-              ]"
-            ></t-radio-group>
-            <p class="tip"></p>
-          </t-form-item>
-        </t-col>
         <t-col :span="12">
-          <t-form-item label="设备出入库登记" name="deviceInOutFormList">
+          <t-form-item label="" name="deviceInOutFormList">
             <device-table
-              v-model="formData.deviceInOutFormList"
+              :data="formData.deviceInOutFormList"
               :config="deviceConfig"
               :key="formData.inOutType"
             ></device-table>
@@ -56,7 +26,7 @@
       <t-button theme="default" @click="emit('update:visible', false)"
         >取消</t-button
       >
-      <t-button theme="primary" @click="save">保存</t-button>
+      <t-button theme="primary" @click="save">确定</t-button>
     </template>
   </my-dialog>
 </template>
@@ -72,9 +42,10 @@ const formRef = ref(null);
 
 const props = defineProps({
   visible: Boolean,
+  data: Array,
 });
 const formData = reactive({
-  inOutType: 'OUT',
+  inOutType: 'IN',
   inOutTime: '',
   deviceInOutFormList: [],
   pushSupplier: true,
@@ -82,7 +53,7 @@ const formData = reactive({
 
 const deviceConfig = computed(() => {
   return {
-    code: formData.inOutType === 'OUT' ? 'DEVICE_OUT_TABLE' : 'DEVICE_IN_TABLE',
+    code: 'DEVICE_IN_TABLE',
     writable: true,
   };
 });
@@ -90,34 +61,42 @@ const deviceConfig = computed(() => {
 watch(
   () => props.visible,
   () => {
-    formData.inOutType = 'OUT';
     formData.inOutTime = Date.now();
-    formData.deviceInOutFormList = [];
   }
 );
 watch(
-  () => formData.inOutType,
+  () => props.data,
   () => {
-    formData.deviceInOutFormList = [];
+    formData.deviceInOutFormList = props.data || [];
   }
 );
+
 const rules = {
-  inOutTime: [
+  deviceInOutFormList: [
     {
       required: true,
-      message: '设备出入库时间必选',
+      validator: (val) => {
+        if (!val.length) {
+          return {
+            result: false,
+            message: '请填写至少一条记录',
+          };
+        }
+        return {
+          result: true,
+          type: 'success',
+        };
+      },
       type: 'error',
       trigger: 'change',
     },
-  ],
-  deviceInOutFormList: [
     {
-      required: true,
       validator: (val) => {
-        if (!val.length) {
+        if (val.some((item) => !item.basePhotoPath && !item.expressNo)) {
           return {
             result: false,
-            message: '请填写至少一条记录',
+            message:
+              '每一条数据的快递单拍照和快递单号至少有一项必填,请核对并编辑',
           };
         }
         return {
@@ -132,6 +111,7 @@ const rules = {
 };
 
 const save = async () => {
+  formData.inOutTime = Date.now();
   const valid = await formRef.value.validate();
   if (valid !== true) return;
 

+ 80 - 46
src/views/sop/sop-manage/device-out-in/device-table/edit-column-dialog.vue

@@ -21,6 +21,7 @@
           :options="deviceOptions"
           @change="deviceChange"
           filterable
+          disabled
         >
         </t-select>
       </t-form-item>
@@ -59,6 +60,9 @@
           @change="imgChange"
         ></my-upload>
       </t-form-item>
+      <t-form-item label="快递单号" name="expressNo">
+        <t-input v-model="formData.expressNo" />
+      </t-form-item>
     </t-form>
     <template #foot>
       <t-button theme="default" @click="emit('update:visible', false)"
@@ -69,7 +73,7 @@
   </my-dialog>
 </template>
 <script setup name="EditColumnDialog">
-import { ref, computed, reactive } from 'vue';
+import { ref, computed, reactive, watch } from 'vue';
 import { RUNNING_STATUS } from '@/config/constants';
 import { dictToOptionList } from '@/utils/tool';
 import { deviceCanOut, deviceCanIn } from '@/api/sop';
@@ -96,58 +100,88 @@ const formData = reactive({
   address: '',
   addressArr: ['', '', ''],
   basePhotoPath: '',
+  expressNo: '',
+  deviceBrand: '',
+});
+watch([() => formData.basePhotoPath, () => formData.expressNo], () => {
+  formRef.value.validate();
 });
-
 const title = computed(() => {
   return (props.curRow?.key ? '编辑' : '新增') + '记录';
 });
 
-const rules = {
-  serialNo: [
-    {
-      required: true,
-      message: '不能为空',
-      type: 'error',
-      trigger: 'change',
-    },
-  ],
-  deviceStatus: [
-    {
-      required: true,
-      message: '不能为空',
-      type: 'error',
-      trigger: 'change',
-    },
-  ],
-  // scanCount: [
-  //   {
-  //     required: true,
-  //     message: '不能为空',
-  //     type: 'error',
-  //     trigger: 'change',
-  //   },
-  // ],
-  addressArr: [
-    {
-      validator: () => {
-        if (formData.addressArr.some((item) => !item))
-          return { result: false, message: '发往地必选' };
+const rules = computed(() => {
+  return {
+    serialNo: [
+      {
+        required: true,
+        message: '不能为空',
+        type: 'error',
+        trigger: 'change',
+      },
+    ],
+    deviceStatus: [
+      {
+        required: true,
+        message: '不能为空',
+        type: 'error',
+        trigger: 'change',
+      },
+    ],
+    // scanCount: [
+    //   {
+    //     required: true,
+    //     message: '不能为空',
+    //     type: 'error',
+    //     trigger: 'change',
+    //   },
+    // ],
+    addressArr: [
+      {
+        validator: () => {
+          if (formData.addressArr.some((item) => !item))
+            return { result: false, message: '发往地必选' };
 
-        return { result: true, type: 'success' };
+          return { result: true, type: 'success' };
+        },
+        type: 'error',
+        trigger: 'change',
       },
-      type: 'error',
-      trigger: 'change',
-    },
-  ],
-  basePhotoPath: [
-    {
-      required: true,
-      message: '请上传图片',
-      type: 'error',
-      trigger: 'change',
-    },
-  ],
-};
+    ],
+    basePhotoPath: [
+      {
+        // required: true,
+        validator: () => {
+          if (!formData.basePhotoPath && !formData.expressNo)
+            return {
+              result: false,
+              message: '快递单拍照和快递单号至少有一项必填',
+            };
+
+          return { result: true, type: 'success' };
+        },
+        type: 'error',
+        trigger: 'change',
+      },
+    ],
+    expressNo: [
+      {
+        // required: true,
+        validator: () => {
+          if (!formData.basePhotoPath && !formData.expressNo)
+            return {
+              result: false,
+              message: '快递单拍照和快递单号至少有一项必填',
+            };
+
+          return { result: true, type: 'success' };
+        },
+        type: 'error',
+        trigger: 'change',
+      },
+    ],
+  };
+});
 
 const getDeviceOptions = async () => {
   const func = props.isOutType ? deviceCanOut : deviceCanIn;

+ 13 - 14
src/views/sop/sop-manage/device-out-in/device-table/index.vue

@@ -1,3 +1,5 @@
+<!-- SOP管理=》设备入库登记页面,已不再需要该文件,因为去掉了新增按钮 -->
+<!-- 此文件改为 资源保障=》设备发货管理 页面所用,逻辑也有少量更改 -->
 <template>
   <div>
     <t-table
@@ -15,7 +17,7 @@
             编辑
           </t-link>
           <t-link theme="primary" hover="color" @click="deleteRow(rowIndex)">
-            
+            
           </t-link>
         </div>
       </template>
@@ -36,7 +38,7 @@
         ></image-view>
       </template>
     </t-table>
-    <t-button
+    <!-- <t-button
       v-if="!readonly"
       class="m-t-10px"
       theme="primary"
@@ -44,7 +46,7 @@
     >
       <template #icon><svg-icon name="add-circle" color="#fff" /></template>
       添加
-    </t-button>
+    </t-button> -->
 
     <!-- EditColumnDialog -->
     <edit-column-dialog
@@ -63,9 +65,8 @@ import EditColumnDialog from './edit-column-dialog.vue';
 
 const props = defineProps({
   config: { type: Object },
-  modelValue: { type: Array },
+  data: { type: Array },
 });
-const emit = defineEmits(['update:modelValue', 'change']);
 
 const tableData = ref([]);
 const showEditColumnDialog = ref(false);
@@ -85,11 +86,6 @@ const columns = [
     align: 'center',
     width: 50,
   },
-  {
-    title: '设备编号',
-    colKey: 'deviceNo',
-    width: 120,
-  },
   {
     title: '设备序列号',
     colKey: 'serialNo',
@@ -122,6 +118,11 @@ const columns = [
     colKey: 'basePhotoPath',
     width: 98,
   },
+  {
+    title: '快递单号',
+    colKey: 'expressNo',
+    width: 150,
+  },
   {
     title: '操作',
     colKey: 'operate',
@@ -172,16 +173,14 @@ const columnConfirm = (data) => {
 
 const emitChange = () => {
   resetKeys();
-  emit('update:modelValue', tableData.value);
-  emit('change', tableData.value);
 };
 
 watch(
-  () => props.modelValue,
+  () => props.data,
   (val, oldval) => {
-    if (val === oldval) return;
     const vals = val || [];
     tableData.value = vals;
+    resetKeys();
   },
   {
     immediate: true,

+ 190 - 0
src/views/sop/sop-manage/device-out-in/device-table/index_old.vue

@@ -0,0 +1,190 @@
+<template>
+  <div>
+    <t-table
+      class="dynamic-table"
+      ref="tableRef"
+      row-key="key"
+      :columns="columns"
+      :data="tableData"
+      bordered
+      size="small"
+    >
+      <template v-if="!readonly" #operate="{ row, rowIndex }">
+        <div class="table-operations">
+          <t-link theme="primary" hover="color" @click="handleEdit(row)">
+            编辑
+          </t-link>
+          <t-link theme="primary" hover="color" @click="deleteRow(rowIndex)">
+            删除
+          </t-link>
+        </div>
+      </template>
+      <template #deviceStatus="{ row }">
+        <status-tag :value="row.deviceStatus" type="runningStatus"></status-tag>
+      </template>
+      <template #basePhotoPath="{ row }">
+        <!-- <t-image
+            v-if="row.basePhotoPath"
+            :src="row.basePhotoPath"
+            :style="{ width: '80px', height: '80px' }"
+          /> -->
+        <image-view
+          v-if="row.basePhotoPath"
+          :width="80"
+          :height="80"
+          :images="[row.basePhotoPath]"
+        ></image-view>
+      </template>
+    </t-table>
+    <t-button
+      v-if="!readonly"
+      class="m-t-10px"
+      theme="primary"
+      @click="handleAdd"
+    >
+      <template #icon><svg-icon name="add-circle" color="#fff" /></template>
+      添加
+    </t-button>
+
+    <!-- EditColumnDialog -->
+    <edit-column-dialog
+      v-if="!readonly"
+      v-model:visible="showEditColumnDialog"
+      :curRow="curRow"
+      :isOutType="isOutType"
+      @success="columnConfirm"
+      :tableData="tableData"
+    ></edit-column-dialog>
+  </div>
+</template>
+<script setup name="DEVICEOUTTABLE">
+import { computed, ref, watch } from 'vue';
+import EditColumnDialog from './edit-column-dialog.vue';
+
+const props = defineProps({
+  config: { type: Object },
+  modelValue: { type: Array },
+});
+const emit = defineEmits(['update:modelValue', 'change']);
+
+const tableData = ref([]);
+const showEditColumnDialog = ref(false);
+const curRow = ref(null);
+
+const isOutType = computed(() => {
+  return props.config.code === 'DEVICE_OUT_TABLE';
+});
+const readonly = computed(() => {
+  return !props.config.writable;
+});
+
+const columns = [
+  {
+    title: '序号',
+    colKey: 'key',
+    align: 'center',
+    width: 50,
+  },
+  {
+    title: '设备编号',
+    colKey: 'deviceNo',
+    width: 120,
+  },
+  {
+    title: '设备序列号',
+    colKey: 'serialNo',
+    width: 120,
+  },
+  {
+    title: '供应商',
+    colKey: 'supplierName',
+  },
+  {
+    title: '运行状态',
+    colKey: 'deviceStatus',
+    width: 105,
+  },
+  {
+    title: '总扫描量',
+    colKey: 'scanCount',
+    width: 80,
+  },
+  {
+    title: '当前所在地',
+    colKey: 'location',
+  },
+  {
+    title: '发往地',
+    colKey: 'address',
+  },
+  {
+    title: '快递单拍照',
+    colKey: 'basePhotoPath',
+    width: 98,
+  },
+  {
+    title: '操作',
+    colKey: 'operate',
+    width: 90,
+    align: 'center',
+  },
+];
+const resetKeys = () => {
+  tableData.value.forEach((item, index) => {
+    item.key = index + 1 + '';
+  });
+};
+
+const handleAdd = () => {
+  curRow.value = {
+    key: '',
+    deviceNo: '',
+    supplierName: '',
+    deviceStatus: '',
+    deviceBrand: '',
+    scanCount: '',
+    location: '',
+    address: '',
+    addressArr: ['', '', ''],
+    basePhotoPath: '',
+  };
+  showEditColumnDialog.value = true;
+};
+const handleEdit = (row) => {
+  curRow.value = row;
+  showEditColumnDialog.value = true;
+};
+const deleteRow = (row) => {
+  let index = tableData.value.findIndex((item) => item.key == row.key);
+  tableData.value.splice(index, 1);
+  emitChange();
+};
+
+const columnConfirm = (data) => {
+  if (data.key) {
+    const pos = tableData.value.findIndex((item) => item.key === data.key);
+    tableData.value.splice(pos, 1, data);
+  } else {
+    tableData.value.push(data);
+  }
+  emitChange();
+};
+
+const emitChange = () => {
+  resetKeys();
+  emit('update:modelValue', tableData.value);
+  emit('change', tableData.value);
+};
+
+watch(
+  () => props.modelValue,
+  (val, oldval) => {
+    if (val === oldval) return;
+    const vals = val || [];
+    tableData.value = vals;
+  },
+  {
+    immediate: true,
+  }
+);
+</script>

+ 2 - 2
src/views/sop/sop-manage/device-out-in/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="office-sop flex flex-col h-full">
-    <div class="page-action">
+    <!-- <div class="page-action">
       <t-space size="small">
         <t-button
           v-if="perm.BUTTON_Add"
@@ -11,7 +11,7 @@
           新增</t-button
         >
       </t-space>
-    </div>
+    </div> -->
     <SearchForm :fields="fields" :params="params" :search="search">
       <template #service="{ item, params }">
         <select-service-unit v-model="params[item.prop]"></select-service-unit>