Kaynağa Gözat

设备发货管理,用户绑定供应商

刘洋 1 yıl önce
ebeveyn
işleme
b7ac398113

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

@@ -87,3 +87,30 @@ export const registrationQueryListApi = (data) =>
     url: '/api/admin/device/in/out/source_page',
     params: data,
   });
+
+//设备发货列表
+export const deviceSendListApi = (data) =>
+  request({
+    url: '/api/admin/device/delivery/page',
+    params: data,
+  });
+
+//修改设备发货单条
+export const editDeviceSendApi = (data) =>
+  request({
+    url: '/api/admin/device/delivery/save',
+    data,
+    loading: true,
+  });
+export const deviceSendStatusUpdateApi = (params) =>
+  request({
+    url: '/api/admin/device/delivery/status/update',
+    params,
+    paramsSerializer,
+  });
+export const deviceSendExportApi = (data) =>
+  request({
+    url: '/api/admin/device/delivery/export',
+    params: data,
+    download: true,
+  });

+ 6 - 0
src/api/user.js

@@ -165,6 +165,12 @@ export const getAttachmentListByKey = (key) =>
   });
 //根据用户id查询可绑定的供应商列表options
 export const getSupplierByUser = (params) =>
+  request({
+    url: '/api/sys/supplier/list/by/user_role',
+    params,
+  });
+//用户绑定过的供应商
+export const getUserBindSupplier = (params) =>
   request({
     url: '/api/admin/user/query/supplier',
     params,

+ 57 - 22
src/views/resource-guard/device-guard/device-send/edit-device-info-dialog.vue

@@ -8,30 +8,61 @@
     @close="emit('update:visible', false)"
   >
     <div class="readonly-info">
-      <t-row :gutter="[0, 20]">
-        <t-col :span="6"></t-col>
-      </t-row>
+      <t-form labelWidth="70px">
+        <t-row :gutter="[10, 0]">
+          <t-col :span="6">
+            <t-form-item label="项目单号:">
+              {{ curRow?.crmNo || '-' }}
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item label="供应商:">
+              {{ curRow?.supplierName }}
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item label="设备编号:">
+              {{ curRow?.deviceNo }}
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item label="序列号:">
+              {{ curRow?.serialNo }}
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item label="品牌:">
+              {{ curRow?.brand }}
+            </t-form-item>
+          </t-col>
+          <t-col :span="6">
+            <t-form-item label="型号:">
+              {{ curRow?.model }}
+            </t-form-item>
+          </t-col>
+        </t-row>
+      </t-form>
     </div>
     <t-form ref="formRef" :data="formData" :rules="rules" labelWidth="120px">
       <t-row :gutter="[0, 20]">
         <t-col :span="6">
-          <t-form-item label="邮寄地址" name="a">
-            <t-input v-model="formData.a"></t-input>
+          <t-form-item label="邮寄地址" name="mailingAddress">
+            <t-input v-model="formData.mailingAddress"></t-input>
           </t-form-item>
         </t-col>
         <t-col :span="6">
-          <t-form-item label="收件人" name="b">
-            <t-input v-model="formData.b"></t-input>
+          <t-form-item label="收件人" name="consignee">
+            <t-input v-model="formData.consignee"></t-input>
           </t-form-item>
         </t-col>
         <t-col :span="6">
-          <t-form-item label="收件人联系电话" name="c">
-            <t-input v-model="formData.c"></t-input>
+          <t-form-item label="收件人联系电话" name="consigneePhone">
+            <t-input v-model="formData.consigneePhone"></t-input>
           </t-form-item>
         </t-col>
         <t-col :span="6">
-          <t-form-item label="快递单号" name="d">
-            <t-input v-model="formData.d"></t-input>
+          <t-form-item label="快递单号" name="expressNo">
+            <t-input v-model="formData.expressNo"></t-input>
           </t-form-item>
         </t-col>
       </t-row>
@@ -48,8 +79,9 @@
 import { ref, computed } from 'vue';
 import { MessagePlugin } from 'tdesign-vue-next';
 import useClearDialog from '@/hooks/useClearDialog';
+import { editDeviceSendApi } from '@/api/resource-guard';
 
-const emit = defineEmits(['update:visible']);
+const emit = defineEmits(['update:visible', 'success']);
 const props = defineProps({
   visible: Boolean,
   curRow: Object,
@@ -59,10 +91,10 @@ const formRef = ref(null);
 
 const { formData, isEdit } = useClearDialog(
   {
-    a: '',
-    b: '',
-    c: '',
-    d: '',
+    mailingAddress: '',
+    consignee: '',
+    consigneePhone: '',
+    expressNo: '',
   },
   props,
   formRef,
@@ -74,7 +106,7 @@ const { formData, isEdit } = useClearDialog(
 );
 
 const rules = {
-  a: [
+  mailingAddress: [
     {
       required: true,
       message: '请输入邮寄地址',
@@ -82,7 +114,7 @@ const rules = {
       trigger: 'change',
     },
   ],
-  b: [
+  consignee: [
     {
       required: true,
       message: '请输入收件人',
@@ -90,7 +122,7 @@ const rules = {
       trigger: 'change',
     },
   ],
-  c: [
+  consigneePhone: [
     {
       required: true,
       message: '请输入收件人联系电话',
@@ -104,7 +136,7 @@ const rules = {
       trigger: 'change',
     },
   ],
-  d: [
+  expressNo: [
     {
       required: true,
       message: '请输入快递单号',
@@ -118,8 +150,11 @@ const save = async () => {
   const valid = await formRef.value.validate();
   if (valid !== true) return;
 
-  //   const res = await personFilesEditApi(formData).catch(() => {});
-  //   if (!res) return;
+  const res = await editDeviceSendApi({
+    ...formData,
+    id: props.curRow.id,
+  }).catch(() => {});
+  if (!res) return;
 
   MessagePlugin.success('保存成功');
   emit('update:visible', false);

+ 156 - 50
src/views/resource-guard/device-guard/device-send/index.vue

@@ -3,7 +3,7 @@
     <div class="page-action">
       <t-space size="small">
         <upload-button
-          upload-url="/api/admin/user/archives/import"
+          upload-url="/api/admin/device/delivery/import"
           :format="['xls', 'xlsx']"
         >
           <t-button variant="outline">
@@ -18,7 +18,7 @@
         <t-button
           variant="outline"
           :disabled="!selectedRowKeys.length"
-          @click="handleDestroy"
+          @click="handleOperate('DELIVER')"
         >
           <template #icon><svg-icon name="delete" color="#262626" /></template
           >批量发货
@@ -26,7 +26,7 @@
         <t-button
           variant="outline"
           :disabled="!selectedRowKeys.length"
-          @click="handleDestroy"
+          @click="handleOperate('RECEIVE')"
         >
           <template #icon><svg-icon name="delete" color="#262626" /></template
           >批量签收
@@ -34,7 +34,7 @@
         <t-button
           variant="outline"
           :disabled="!selectedRowKeys.length"
-          @click="handleDestroy"
+          @click="handleOperate('CANCEL')"
         >
           <template #icon><svg-icon name="delete" color="#262626" /></template
           >批量作废
@@ -65,7 +65,7 @@
     <div class="flex-1 page-wrap">
       <t-table
         size="small"
-        row-key="userArchivesId"
+        row-key="id"
         :columns="columns"
         :data="tableData"
         bordered
@@ -81,16 +81,46 @@
         :selected-row-keys="selectedRowKeys"
         @select-change="selectChange"
       >
+        <template #serviceName="{ row }">
+          {{ row.serviceName || '-' }}
+        </template>
+        <template #crmNo="{ row }">
+          {{ row.crmNo || '-' }}
+        </template>
         <template #operate="{ row }">
-          <div class="table-operations" @click.stop>
+          <div class="table-operations" @click.stop v-if="row.effect">
             <t-link
-              v-if="perm.LINK_Update"
               theme="primary"
               hover="color"
               @click="handleEdit(row)"
+              v-if="row.status == 'UN_DELIVER'"
             >
               修改
             </t-link>
+            <t-link
+              theme="primary"
+              hover="color"
+              v-if="row.status == 'UN_DELIVER'"
+              @click="handleOperate('CANCEL', [row.id])"
+            >
+              作废
+            </t-link>
+            <t-link
+              theme="primary"
+              hover="color"
+              v-if="row.status == 'UN_DELIVER'"
+              @click="handleOperate('DELIVER', [row.id])"
+            >
+              发货
+            </t-link>
+            <t-link
+              theme="primary"
+              hover="color"
+              v-if="row.status == 'DELIVER'"
+              @click="handleOperate('RECEIVE', [row.id])"
+            >
+              签收
+            </t-link>
           </div>
         </template>
       </t-table>
@@ -98,6 +128,7 @@
     <EditDeviceInfoDialog
       v-model:visible="showEditDeviceDialog"
       :curRow="curRow"
+      @success="fetchData"
     ></EditDeviceInfoDialog>
   </div>
 </template>
@@ -110,12 +141,11 @@ import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
 import { ErrorCircleFilledIcon } from 'tdesign-icons-vue-next';
 import useFetchTable from '@/hooks/useFetchTable';
 import {
-  personFilesListApi,
-  personFilesStatisticsApi,
-  personFilesDestroyApi,
-  personFilesExportApi,
+  deviceSendStatusUpdateApi,
+  deviceSendListApi,
+  deviceSendExportApi,
 } from '@/api/resource-guard';
-import { DEVICE_SEND_STATUS } from '@/config/constants';
+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';
@@ -123,13 +153,10 @@ const { perm } = usePermission();
 
 const curRow = ref(null);
 const showEditDeviceDialog = ref(false);
-setTimeout(() => {
-  showEditDeviceDialog.value = true;
-});
 
 const fields = ref([
   {
-    prop: 'a',
+    prop: 'serviceId',
     label: '服务单元',
     type: 'select',
     labelWidth: 80,
@@ -137,18 +164,18 @@ const fields = ref([
     cell: 'service',
   },
   {
-    prop: 'b',
+    prop: 'usageType',
     label: '用途类型',
     type: 'select',
     labelWidth: 80,
     colSpan: 6,
-    options: [],
+    options: dictToOptionList(DEVICE_USAGE_TYPE),
     attrs: {
       clearable: true,
     },
   },
   {
-    prop: 'c',
+    prop: 'crmNo',
     label: '项目单号',
     labelWidth: 80,
     colSpan: 6,
@@ -157,7 +184,7 @@ const fields = ref([
     },
   },
   {
-    prop: 'd',
+    prop: 'status',
     label: '发货状态',
     type: 'select',
     labelWidth: 80,
@@ -168,7 +195,7 @@ const fields = ref([
     },
   },
   {
-    prop: 'e',
+    prop: 'deliverUserId',
     label: '发货人',
     type: 'select',
     labelWidth: 80,
@@ -176,7 +203,7 @@ const fields = ref([
     cell: 'user',
   },
   {
-    prop: 'f',
+    prop: 'deliverTime',
     label: '发货时间',
     labelWidth: 80,
     type: 'daterange',
@@ -187,7 +214,7 @@ const fields = ref([
     },
   },
   {
-    prop: 'h',
+    prop: 'deviceNo',
     label: '设备编号',
     labelWidth: 80,
     colSpan: 6,
@@ -204,7 +231,7 @@ const fields = ref([
     cell: 'supplier',
   },
   {
-    prop: 'g',
+    prop: 'receiveTime',
     label: '验收时间',
     labelWidth: 80,
     type: 'daterange',
@@ -214,7 +241,15 @@ const fields = ref([
       valueType: 'time-stamp',
     },
   },
-
+  {
+    prop: 'serialNo',
+    label: '设备序列号',
+    labelWidth: 80,
+    colSpan: 6,
+    attrs: {
+      clearable: true,
+    },
+  },
   {
     type: 'buttons',
     colSpan: 2,
@@ -232,20 +267,24 @@ const fields = ref([
 ]);
 
 const params = reactive({
-  a: '',
-  b: '',
-  c: '',
-  d: '',
-  e: '',
-  f: [],
-  g: [],
-  h: '',
+  serviceId: '',
+  usageType: '',
+  crmNo: '',
+  status: '',
+  deliverUserId: '',
+  deliverTime: [],
+  deviceNo: '',
+  receiveTime: [],
   supplierId: '',
+  serialNo: '',
 });
 const computedParams = computed(() => {
-  let data = omit(params, ['f', 'g']);
-  // data.archivesTimeStart = params.archivesTime[0];
-  // data.archivesTimeEnd = params.archivesTime[1];
+  let data = omit(params, ['deliverTime', 'receiveStartTime']);
+  data.deliverStartTime = params.deliverTime[0];
+  data.deliverEndTime = params.deliverTime[1];
+
+  data.receiveStartTime = params.receiveTime[0];
+  data.receiveEndTime = params.receiveTime[1];
   return data;
 });
 
@@ -256,15 +295,72 @@ const columns = [
     width: 50,
     fixed: 'left',
   },
+  {
+    title: '服务单元',
+    colKey: 'serviceName',
+    width: 100,
+    cell: 'serviceName',
+  },
+  {
+    title: '项目单号',
+    colKey: 'crmNo',
+    width: 140,
+    cell: 'crmNo',
+  },
+  {
+    title: '用途',
+    colKey: 'usageTypeStr',
+    width: 70,
+  },
+  {
+    title: '出库/入库',
+    colKey: 'deliveryTypeStr',
+    width: 110,
+  },
+  {
+    title: '供应商',
+    colKey: 'supplierName',
+    width: 100,
+  },
+  {
+    title: '设备编号',
+    colKey: 'deviceNo',
+    width: 100,
+  },
+  {
+    title: '序列号',
+    colKey: 'serialNo',
+    width: 100,
+  },
+  {
+    title: '品牌',
+    colKey: 'brand',
+    width: 70,
+  },
+  {
+    title: '型号',
+    colKey: 'model',
+    width: 70,
+  },
+  {
+    title: '邮寄地址',
+    colKey: 'mailingAddress',
+    minWidth: 150,
+  },
+  {
+    title: '收件人',
+    colKey: 'consignee',
+    width: 90,
+  },
   {
     title: '管理',
     colKey: 'operate',
     fixed: 'right',
-    width: 80,
+    width: 160,
   },
 ];
 const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
-  personFilesListApi,
+  deviceSendListApi,
   {
     fetchDataHandle: () => {
       selectedRowKeys.value = [];
@@ -280,24 +376,36 @@ const selectChange = (value) => {
 
 const handleEdit = (row) => {
   curRow.value = row;
-  showAddPersonFileDialog.value = true;
+  showEditDeviceDialog.value = true;
 };
-const handleDestroy = () => {
-  if (!selectedRowKeys.value.length) {
+const handleOperate = (status, ids) => {
+  let str =
+    status === 'DELIVER'
+      ? '发货'
+      : status === 'RECEIVE'
+      ? '签收'
+      : status === 'CANCEL'
+      ? '作废'
+      : '';
+  let confirmBody = !ids
+    ? `确定要${str}当前选择的所有记录吗?`
+    : `确定要${str}当前记录吗?`;
+  if (!selectedRowKeys.value.length && !ids) {
     MessagePlugin.error('请选择要作废的记录');
     return;
   }
 
   const confirmDia = DialogPlugin({
     header: '操作提示',
-    body: `确定要作废当前选择的所有记录吗`,
+    body: confirmBody,
     confirmBtn: '确定',
     cancelBtn: '取消',
     onConfirm: async () => {
       confirmDia.hide();
-      const res = await personFilesDestroyApi(selectedRowKeys.value).catch(
-        () => {}
-      );
+      const res = await deviceSendStatusUpdateApi({
+        idList: ids || selectedRowKeys.value,
+        status,
+      }).catch(() => {});
       if (!res) return;
       MessagePlugin.success('操作成功');
       fetchData();
@@ -312,11 +420,9 @@ const multExport = () => {
     cancelBtn: '取消',
     onConfirm: async () => {
       confirmDia.hide();
-      const res = await personFilesExportApi(computedParams.value).catch(
-        () => {}
-      );
-      if (!res) return;
-      MessagePlugin.success('导出任务提交成功,请前往任务管理中下载');
+      deviceSendExportApi(computedParams.value).catch(() => {
+        MessagePlugin.error('导出失败');
+      });
     },
   });
 };

+ 1 - 1
src/views/sop/components/dynamic-form-item/device-table/device-in-table.vue

@@ -140,7 +140,7 @@ const columns = [
   {
     title: '操作',
     colKey: 'operate',
-    width: 90,
+    width: 120,
     align: 'center',
   },
 ];

+ 30 - 3
src/views/sop/components/dynamic-form-item/device-table/index.vue

@@ -14,7 +14,12 @@
           <t-link theme="primary" hover="color" @click="handleEdit(row)">
             编辑
           </t-link>
-          <t-link theme="primary" hover="color" @click="deleteRow(rowIndex)">
+          <t-link
+            theme="primary"
+            hover="color"
+            @click="deleteRow(rowIndex)"
+            v-if="!isFromAsyncData(row.serialNo)"
+          >
             删除
           </t-link>
         </div>
@@ -61,6 +66,8 @@
 <script setup name="DEVICEOUTTABLE">
 import { computed, ref, watch } from 'vue';
 import EditColumnDialog from './edit-column-dialog.vue';
+import { deviceCanOut } from '@/api/sop';
+import { cloneDeep } from 'lodash';
 
 const props = defineProps({
   config: { type: Object },
@@ -132,7 +139,7 @@ const columns = [
   {
     title: '操作',
     colKey: 'operate',
-    width: 90,
+    width: 120,
     align: 'center',
   },
 ];
@@ -183,10 +190,30 @@ const emitChange = () => {
   emit('update:modelValue', tableData.value);
   emit('change', tableData.value);
 };
-
+const asyncData = ref([]);
+const initTableData = () => {
+  deviceCanOut({ sopNo: props.sop.sopNo }).then((res) => {
+    let result = (res || [])
+      .filter((item) => !!item.defaultData)
+      .map((item) => {
+        item.address = item.address || props.sop?.customName || '';
+        return item;
+      });
+    tableData.value = result;
+    asyncData.value = cloneDeep(result);
+    emitChange();
+  });
+};
+const isFromAsyncData = (serialNo) => {
+  return !!asyncData.value.find((item) => item.serialNo == serialNo);
+};
 watch(
   () => props.modelValue,
   (val, oldval) => {
+    if (!val) {
+      initTableData();
+      return;
+    }
     if (val === oldval) return;
     const vals = val || [];
     tableData.value = vals;

+ 15 - 0
src/views/sop/sop-manage/device-out-in/add-device-dialog.vue

@@ -28,6 +28,19 @@
             />
           </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">
             <device-table
@@ -64,6 +77,7 @@ const formData = reactive({
   inOutType: 'OUT',
   inOutTime: '',
   deviceInOutFormList: [],
+  pushSupplier: true,
 });
 
 const deviceConfig = computed(() => {
@@ -98,6 +112,7 @@ const rules = {
   ],
   deviceInOutFormList: [
     {
+      required: true,
       validator: (val) => {
         if (!val.length) {
           return {

+ 1 - 1
src/views/sop/sop-manage/sop-step/sop-step-dialog.vue

@@ -3,7 +3,7 @@
     class="sop-dialog"
     :visible="visible"
     :header="title"
-    size="80%"
+    size="85%"
     attach="body"
     :closeOnOverlayClick="false"
     :close-btn="true"

+ 8 - 4
src/views/user/auth-manage/user-manage/bind-supplier-dialog.vue

@@ -6,7 +6,7 @@
     :closeOnOverlayClick="false"
     @close="emit('update:visible', false)"
   >
-    <t-form ref="formRef" :data="formData" :rules="rules" labelAlign="top">
+    <t-form ref="formRef" :data="formData" :rules="rules">
       <t-form-item label="供应商:" name="id">
         <!-- <t-input v-model="formData.id"></t-input> -->
         <select-supplier
@@ -26,7 +26,7 @@
 <script setup name="AddUserDialog">
 import useClearDialog from '@/hooks/useClearDialog';
 import { ref, computed } from 'vue';
-import { userBindSupplier } from '@/api/user';
+import { userBindSupplier, getUserBindSupplier } from '@/api/user';
 
 const props = defineProps({
   visible: Boolean,
@@ -35,7 +35,11 @@ const props = defineProps({
 });
 const emit = defineEmits(['close', 'success']);
 const formRef = ref(null);
-const getDetail = async () => {};
+const getDetail = async () => {
+  getUserBindSupplier({ id: props.curRow.id }).then((res) => {
+    res && (formData.id = res.id);
+  });
+};
 const { formData, isEdit } = useClearDialog(
   {
     id: '',
@@ -58,7 +62,7 @@ const rules = {
 const bindHandler = () => {
   userBindSupplier({
     supplierId: formData.id,
-    id: curRow.id,
+    id: props.curRow.id,
   }).then(() => {
     emit('success');
   });

+ 1 - 1
src/views/user/auth-manage/user-manage/index.vue

@@ -146,7 +146,7 @@ const handleEdit = (row) => {
 const showBindSupplierDialog = ref(false);
 const handleBindSupplier = (row) => {
   curRow.value = row;
-  getSupplierByUser({ id: row.id })
+  getSupplierByUser({ userRoleEnum: row.userRoleEnum })
     .then((res) => {
       canBindSupplierList.value = res;
     })