Răsfoiți Sursa

库存导出,设备调拨

Zimo 3 zile în urmă
părinte
comite
6a69371c33

+ 4 - 0
src/api/pms/device/index.ts

@@ -153,6 +153,10 @@ export const IotDeviceApi = {
     return await request.download({ url: `/rq/iot-device/export-excel`, params })
   },
 
+  exportIotDeviceAllot: async (params) => {
+    return await request.download({ url: `/pms/iot-device-allot-log/export-excel`, params })
+  },
+
   exportIotDeviceMainAlarm: async (params) => {
     return await request.download({ url: `/pms/iot-main-work-order/exportMaintenances`, params })
   },

+ 1 - 1
src/api/pms/iotlockstock/index.ts

@@ -65,5 +65,5 @@ export const IotLockStockApi = {
   // 导出PMS 本地 库存 Excel
   exportIotLockStock: async (params) => {
     return await request.download({ url: `/pms/iot-lock-stock/export-excel`, params })
-  },
+  }
 }

+ 1 - 1
src/api/pms/iotsapstock/index.ts

@@ -64,5 +64,5 @@ export const IotSapStockApi = {
   // 导出PMS SAP 库存(通用库存/项目部库存) Excel
   exportIotSapStock: async (params) => {
     return await request.download({ url: `/pms/iot-sap-stock/export-excel`, params })
-  },
+  }
 }

+ 21 - 14
src/views/pms/device/allotlog/DeviceAllot.vue

@@ -16,7 +16,11 @@
           :inline="true"
           label-width="68px"
         >
-          <el-form-item :label="t('devicePerson.deviceCode')" prop="deviceCode" style="margin-left: 25px">
+          <el-form-item
+            :label="t('devicePerson.deviceCode')"
+            prop="deviceCode"
+            style="margin-left: 25px"
+          >
             <el-input
               v-model="queryParams.deviceCode"
               :placeholder="t('devicePerson.codeHolder')"
@@ -49,7 +53,12 @@
               />
             </el-select>
           </el-form-item>
-          <el-form-item v-show="ifShow" :label="t('devicePerson.status')" label-width="85px" prop="deviceStatus">
+          <el-form-item
+            v-show="ifShow"
+            :label="t('devicePerson.status')"
+            label-width="85px"
+            prop="deviceStatus"
+          >
             <el-select
               v-model="queryParams.deviceStatus"
               :placeholder="t('devicePerson.status')"
@@ -93,10 +102,12 @@
 
           <el-form-item>
             <el-button v-if="!ifShow" @click="moreQuery(true)" type="warning"
-              ><Icon icon="ep:search" class="mr-5px" /> {{ t('devicePerson.moreSearch') }}</el-button
+              ><Icon icon="ep:search" class="mr-5px" />
+              {{ t('devicePerson.moreSearch') }}</el-button
             >
             <el-button v-if="ifShow" @click="moreQuery(false)" type="danger"
-              ><Icon icon="ep:search" class="mr-5px" /> {{ t('devicePerson.closeSearch') }}</el-button
+              ><Icon icon="ep:search" class="mr-5px" />
+              {{ t('devicePerson.closeSearch') }}</el-button
             >
             <el-button @click="handleQuery"
               ><Icon icon="ep:search" class="mr-5px" /> {{ t('devicePerson.search') }}</el-button
@@ -133,7 +144,7 @@
               {{ scope.$index + 1 }}
             </template>
           </el-table-column>
-          <el-table-column :label="t('monitor.deviceCode')"  align="center" prop="deviceCode" />
+          <el-table-column :label="t('monitor.deviceCode')" align="center" prop="deviceCode" />
           <el-table-column :label="t('monitor.deviceName')" align="center" prop="deviceName">
             <template #default="scope">
               <el-link :underline="false" type="primary" @click="handleDetail(scope.row.id)">
@@ -180,7 +191,7 @@
   </el-row>
   <DeviceAllotLogDrawer
     :model-value="drawerVisible"
-    @update:model-value="val => drawerVisible = val"
+    @update:model-value="(val) => (drawerVisible = val)"
     :device-id="currentDeviceId"
     ref="showDrawer"
   />
@@ -190,10 +201,9 @@
 import download from '@/utils/download'
 import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
 import DeptTree from '@/views/system/user/DeptTree.vue'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
-import DeviceAllotLogDrawer from "@/views/pms/device/allotlog/DeviceAllotLogDrawer.vue";
+import DeviceAllotLogDrawer from '@/views/pms/device/allotlog/DeviceAllotLogDrawer.vue'
 
 /** 设备调拨 列表 */
 defineOptions({ name: 'IotDeviceAllot' })
@@ -320,7 +330,7 @@ const resultOptions = computed(() => [
   {
     label: '否',
     value: 'N' // 空值会触发 clearable 效果
-  },
+  }
 ])
 
 const handleDetail = (id: number) => {
@@ -334,12 +344,9 @@ const handleUpload = (id: number) => {
 /** 导出按钮操作 */
 const handleExport = async () => {
   try {
-    // 导出的二次确认
-    await message.exportConfirm()
-    // 发起导出
     exportLoading.value = true
-    const data = await IotDeviceApi.exportIotDevice(queryParams)
-    download.excel(data, '设备台账.xls')
+    const data = await IotDeviceApi.exportIotDeviceAllot(queryParams)
+    download.excel(data, '设备调拨记录.xls')
   } catch {
   } finally {
     exportLoading.value = false

+ 2 - 2
src/views/pms/iotdeviceallotlog/index.vue

@@ -161,7 +161,7 @@ const queryParams = reactive({
   newDeptId: undefined,
   reason: undefined,
   remark: undefined,
-  createTime: [],
+  createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
@@ -228,4 +228,4 @@ const handleExport = async () => {
 onMounted(() => {
   getList()
 })
-</script>
+</script>

+ 194 - 113
src/views/pms/iotlockstock/index.vue

@@ -8,8 +8,19 @@
       :inline="true"
       label-width="68px"
     >
-      <el-form-item :label="t('workOrderMaterial.factory')" prop="factoryId" v-if="!shouldHideComponents">
-        <el-select v-model="queryParams.factoryId" clearable filterable :placeholder="t('faultForm.choose')" class="!w-240px" @change="selectedFactoryChange">
+      <el-form-item
+        :label="t('workOrderMaterial.factory')"
+        prop="factoryId"
+        v-if="!shouldHideComponents"
+      >
+        <el-select
+          v-model="queryParams.factoryId"
+          clearable
+          filterable
+          :placeholder="t('faultForm.choose')"
+          class="!w-240px"
+          @change="selectedFactoryChange"
+        >
           <el-option
             v-for="item in factoryList"
             :key="item.id"
@@ -19,8 +30,18 @@
         </el-select>
       </el-form-item>
 
-      <el-form-item :label="t('workOrderMaterial.costCenter')" prop="costCenterId" v-if="!shouldHideComponents">
-        <el-select v-model="queryParams.costCenterId" clearable filterable :placeholder="t('faultForm.choose')" class="!w-240px">
+      <el-form-item
+        :label="t('workOrderMaterial.costCenter')"
+        prop="costCenterId"
+        v-if="!shouldHideComponents"
+      >
+        <el-select
+          v-model="queryParams.costCenterId"
+          clearable
+          filterable
+          :placeholder="t('faultForm.choose')"
+          class="!w-240px"
+        >
           <el-option
             v-for="item in filteredCostCenterList"
             :key="item.id"
@@ -59,8 +80,12 @@
         />
       </el-form-item>
       <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> {{ t('operationFill.reset') }}</el-button>
+        <el-button @click="handleQuery"
+          ><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button
+        >
+        <el-button @click="resetQuery"
+          ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('operationFill.reset') }}</el-button
+        >
         <el-button
           type="primary"
           plain
@@ -69,13 +94,8 @@
         >
           <Icon icon="ep:plus" class="mr-5px" />{{ t('operationFill.add') }}
         </el-button>
-        <el-button
-          type="success"
-          plain
-          @click="handleExport"
-          :loading="exportLoading"
-          v-hasPermi="['pms:iot-lock-stock:export']"
-        >
+        <!-- v-hasPermi="['pms:iot-lock-stock:export']" -->
+        <el-button type="success" plain @click="handleExport" :loading="exportLoading">
           <Icon icon="ep:download" class="mr-5px" /> 导出
         </el-button>
       </el-form-item>
@@ -92,7 +112,15 @@
         </div>
         <div class="stat-item">
           <span class="stat-label">{{ t('stock.totalAmount') }}:</span>
-          <span class="stat-value">¥ {{ totalAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}</span>
+          <span class="stat-value"
+            >¥
+            {{
+              totalAmount.toLocaleString(undefined, {
+                minimumFractionDigits: 2,
+                maximumFractionDigits: 2
+              })
+            }}</span
+          >
         </div>
       </div>
     </el-card>
@@ -100,16 +128,58 @@
 
   <!-- 列表 -->
   <ContentWrap ref="tableContainerRef" class="table-container">
-    <el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="false" style="width: 100%">
-      <el-table-column :label="t('workOrderMaterial.factory')" align="center" prop="factory" :width="columnWidths.factory"/>
-      <el-table-column :label="t('workOrderMaterial.costCenter')" align="center" prop="costCenter" :width="columnWidths.costCenter"/>
-      <el-table-column :label="t('chooseMaintain.materialCode')" align="center" prop="materialCode" :width="columnWidths.materialCode"/>
-      <el-table-column :label="t('chooseMaintain.materialName')" align="left" prop="materialName" :width="columnWidths.materialName"/>
-      <el-table-column :label="t('route.quantity')" align="center" prop="quantity"
-                       :formatter="erpPriceTableColumnFormatter" :width="columnWidths.quantity"/>
-      <el-table-column :label="t('workOrderMaterial.unitPrice')" align="center" prop="unitPrice"
-                       :formatter="erpPriceTableColumnFormatter" :width="columnWidths.unitPrice"/>
-      <el-table-column :label="t('workOrderMaterial.unit')" align="center" prop="unit" :width="columnWidths.unit"/>
+    <el-table
+      ref="tableRef"
+      v-loading="loading"
+      :data="list"
+      :stripe="true"
+      :show-overflow-tooltip="false"
+      style="width: 100%"
+    >
+      <el-table-column
+        :label="t('workOrderMaterial.factory')"
+        align="center"
+        prop="factory"
+        :width="columnWidths.factory"
+      />
+      <el-table-column
+        :label="t('workOrderMaterial.costCenter')"
+        align="center"
+        prop="costCenter"
+        :width="columnWidths.costCenter"
+      />
+      <el-table-column
+        :label="t('chooseMaintain.materialCode')"
+        align="center"
+        prop="materialCode"
+        :width="columnWidths.materialCode"
+      />
+      <el-table-column
+        :label="t('chooseMaintain.materialName')"
+        align="left"
+        prop="materialName"
+        :width="columnWidths.materialName"
+      />
+      <el-table-column
+        :label="t('route.quantity')"
+        align="center"
+        prop="quantity"
+        :formatter="erpPriceTableColumnFormatter"
+        :width="columnWidths.quantity"
+      />
+      <el-table-column
+        :label="t('workOrderMaterial.unitPrice')"
+        align="center"
+        prop="unitPrice"
+        :formatter="erpPriceTableColumnFormatter"
+        :width="columnWidths.unitPrice"
+      />
+      <el-table-column
+        :label="t('workOrderMaterial.unit')"
+        align="center"
+        prop="unit"
+        :width="columnWidths.unit"
+      />
       <el-table-column
         :label="t('stock.storageTime')"
         align="center"
@@ -117,7 +187,6 @@
         :formatter="dateFormatter"
         :width="columnWidths.storageTime"
       />
-
     </el-table>
     <!-- 分页 -->
     <Pagination
@@ -137,9 +206,9 @@ import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotLockStockApi, IotLockStockVO } from '@/api/pms/iotlockstock'
 import IotLockStockForm from './IotLockStockForm.vue'
-import {erpPriceTableColumnFormatter} from "@/utils";
+import { erpPriceTableColumnFormatter } from '@/utils'
 import { useUserStore } from '@/store/modules/user'
-import { SapOrgApi, SapOrgVO } from "@/api/system/saporg";
+import { SapOrgApi, SapOrgVO } from '@/api/system/saporg'
 import { checkRole } from '@/utils/permission'
 import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
 
@@ -179,18 +248,18 @@ const queryParams = reactive({
   processInstanceId: undefined,
   auditStatus: undefined,
   remark: undefined,
-  createTime: [],
+  createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
 
-const factoryList = ref([] as SapOrgVO[])   // 工厂列表
+const factoryList = ref([] as SapOrgVO[]) // 工厂列表
 const storageLocationList = ref([] as SapOrgVO[]) // 库存地点列表
 const costCenterList = ref([] as SapOrgVO[]) // SAP成本中心列表
 
 // 统计变量
-const totalQuantity = ref(0)   // 总数量
-const totalAmount = ref(0)     // 总金额
+const totalQuantity = ref(0) // 总数量
+const totalAmount = ref(0) // 总金额
 
 // 表格容器和表格的引用
 const tableContainerRef = ref()
@@ -220,54 +289,66 @@ const columnWidths = ref({
 
 /** 获取滚动条宽度 */
 const getScrollbarWidth = () => {
-  const outer = document.createElement('div');
-  outer.style.visibility = 'hidden';
-  outer.style.overflow = 'scroll';
-  document.body.appendChild(outer);
+  const outer = document.createElement('div')
+  outer.style.visibility = 'hidden'
+  outer.style.overflow = 'scroll'
+  document.body.appendChild(outer)
 
-  const inner = document.createElement('div');
-  outer.appendChild(inner);
+  const inner = document.createElement('div')
+  outer.appendChild(inner)
 
-  const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
-  outer.parentNode?.removeChild(outer);
+  const scrollbarWidth = outer.offsetWidth - inner.offsetWidth
+  outer.parentNode?.removeChild(outer)
 
-  return scrollbarWidth;
-};
+  return scrollbarWidth
+}
 
 /** 计算文本宽度 */
 const getTextWidth = (text: string, fontSize = 14) => {
-  const span = document.createElement('span');
-  span.style.visibility = 'hidden';
-  span.style.position = 'absolute';
-  span.style.whiteSpace = 'nowrap';
-  span.style.fontSize = `${fontSize}px`;
-  span.style.fontFamily = 'inherit';
-  span.innerText = text;
-
-  document.body.appendChild(span);
-  const width = span.offsetWidth;
-  document.body.removeChild(span);
-
-  return width;
-};
+  const span = document.createElement('span')
+  span.style.visibility = 'hidden'
+  span.style.position = 'absolute'
+  span.style.whiteSpace = 'nowrap'
+  span.style.fontSize = `${fontSize}px`
+  span.style.fontFamily = 'inherit'
+  span.innerText = text
+
+  document.body.appendChild(span)
+  const width = span.offsetWidth
+  document.body.removeChild(span)
+
+  return width
+}
 
 /** 计算列宽度 */
 const calculateColumnWidths = () => {
-  const MIN_WIDTH = 80; // 最小列宽
-  const PADDING = 25; // 列内边距
-  const FLEXIBLE_COLUMN = 'materialName'; // 可伸缩列
-  const scrollbarWidth = getScrollbarWidth(); // 动态获取滚动条宽度
+  const MIN_WIDTH = 80 // 最小列宽
+  const PADDING = 25 // 列内边距
+  const FLEXIBLE_COLUMN = 'materialName' // 可伸缩列
+  const scrollbarWidth = getScrollbarWidth() // 动态获取滚动条宽度
 
-  if (!tableContainerRef.value?.$el || list.value.length === 0) return;
+  if (!tableContainerRef.value?.$el || list.value.length === 0) return
 
-  const containerWidth = tableContainerRef.value.$el.clientWidth;
+  const containerWidth = tableContainerRef.value.$el.clientWidth
 
   // 需要自适应的列配置
   const autoColumns = [
     { key: 'factory', label: t('workOrderMaterial.factory'), getValue: (row) => row.factory },
-    { key: 'costCenter', label: t('workOrderMaterial.costCenter'), getValue: (row) => row.costCenter },
-    { key: 'materialCode', label: t('chooseMaintain.materialCode'), getValue: (row) => row.materialCode },
-    { key: 'materialName', label: t('chooseMaintain.materialName'), getValue: (row) => row.materialName },
+    {
+      key: 'costCenter',
+      label: t('workOrderMaterial.costCenter'),
+      getValue: (row) => row.costCenter
+    },
+    {
+      key: 'materialCode',
+      label: t('chooseMaintain.materialCode'),
+      getValue: (row) => row.materialCode
+    },
+    {
+      key: 'materialName',
+      label: t('chooseMaintain.materialName'),
+      getValue: (row) => row.materialName
+    },
     {
       key: 'quantity',
       label: t('route.quantity'),
@@ -284,63 +365,65 @@ const calculateColumnWidths = () => {
       label: t('stock.storageTime'),
       getValue: (row) => dateFormatter(null, null, row.storageTime)
     }
-  ];
+  ]
 
-  const newWidths: Record<string, string> = {};
-  let totalFixedWidth = 0; // 所有固定列的总宽度
+  const newWidths: Record<string, string> = {}
+  let totalFixedWidth = 0 // 所有固定列的总宽度
 
   // 计算除可伸缩列外的所有列宽度
-  autoColumns.forEach(col => {
-    if (col.key === FLEXIBLE_COLUMN) return;
+  autoColumns.forEach((col) => {
+    if (col.key === FLEXIBLE_COLUMN) return
 
-    const headerText = col.label;
-    const headerWidth = getTextWidth(headerText) * 1.3; // 表头宽度(加粗效果增加20%)
+    const headerText = col.label
+    const headerWidth = getTextWidth(headerText) * 1.3 // 表头宽度(加粗效果增加20%)
 
     // 计算内容最大宽度
-    let contentMaxWidth = 0;
-    list.value.forEach(row => {
-      const text = col.getValue ? String(col.getValue(row)) : String(row[col.key] || '');
-      const textWidth = getTextWidth(text);
-      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
-    });
+    let contentMaxWidth = 0
+    list.value.forEach((row) => {
+      const text = col.getValue ? String(col.getValue(row)) : String(row[col.key] || '')
+      const textWidth = getTextWidth(text)
+      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth
+    })
 
     // 取表头宽度、内容最大宽度和最小宽度的最大值
-    const finalWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING;
-    newWidths[col.key] = `${finalWidth}px`;
-    totalFixedWidth += finalWidth;
-  });
+    const finalWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING
+    newWidths[col.key] = `${finalWidth}px`
+    totalFixedWidth += finalWidth
+  })
 
   // 处理可伸缩列(materialName)
-  const flexibleCol = autoColumns.find(col => col.key === FLEXIBLE_COLUMN);
+  const flexibleCol = autoColumns.find((col) => col.key === FLEXIBLE_COLUMN)
   if (flexibleCol) {
-    const headerText = flexibleCol.label;
-    const headerWidth = getTextWidth(headerText) * 1.3;
-
-    let contentMaxWidth = 0;
-    list.value.forEach(row => {
-      const text = flexibleCol.getValue ? String(flexibleCol.getValue(row)) : String(row[flexibleCol.key] || '');
-      const textWidth = getTextWidth(text);
-      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
-    });
+    const headerText = flexibleCol.label
+    const headerWidth = getTextWidth(headerText) * 1.3
+
+    let contentMaxWidth = 0
+    list.value.forEach((row) => {
+      const text = flexibleCol.getValue
+        ? String(flexibleCol.getValue(row))
+        : String(row[flexibleCol.key] || '')
+      const textWidth = getTextWidth(text)
+      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth
+    })
 
-    const baseWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING;
+    const baseWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING
 
     // 剩余空间 = 容器宽度 - 其他列总宽度 - 垂直滚动条宽度(17px)
-    const remainingWidth = containerWidth - totalFixedWidth - scrollbarWidth;
+    const remainingWidth = containerWidth - totalFixedWidth - scrollbarWidth
 
     // 可伸缩列的宽度取剩余空间和基础宽度的最大值
-    const flexibleWidth = Math.max(remainingWidth, baseWidth);
-    newWidths[FLEXIBLE_COLUMN] = `${flexibleWidth}px`;
+    const flexibleWidth = Math.max(remainingWidth, baseWidth)
+    newWidths[FLEXIBLE_COLUMN] = `${flexibleWidth}px`
   }
 
   // 更新列宽度
-  columnWidths.value = newWidths;
+  columnWidths.value = newWidths
 
   // 重新布局表格
   nextTick(() => {
-    tableRef.value?.doLayout();
-  });
-};
+    tableRef.value?.doLayout()
+  })
+}
 
 /** 查询列表 */
 const getList = async () => {
@@ -413,7 +496,7 @@ const openForm = (type: string, id?: number) => {
     formRef.value.open(type, id)
     return
   }
-  push({ name: 'LockStockAdd', params:{} })
+  push({ name: 'LockStockAdd', params: {} })
 }
 
 /** 删除按钮操作 */
@@ -460,24 +543,21 @@ const selectedFactoryChange = async (selectedId: number | undefined) => {
   }
 
   // 获取选中的工厂对象
-  const selectedFactory = factoryList.value.find(item => item.id === selectedId)
+  const selectedFactory = factoryList.value.find((item) => item.id === selectedId)
   if (!selectedFactory) return
 
   // 根据工厂代码过滤成本中心
   filteredCostCenterList.value = costCenterList.value.filter(
-    item => item.factoryCode === selectedFactory.factoryCode
+    (item) => item.factoryCode === selectedFactory.factoryCode
   )
 }
 
 /** 导出按钮操作 */
 const handleExport = async () => {
   try {
-    // 导出的二次确认
-    await message.exportConfirm()
-    // 发起导出
     exportLoading.value = true
     const data = await IotLockStockApi.exportIotLockStock(queryParams)
-    download.excel(data, 'PMS 本地 库存.xls')
+    download.excel(data, 'PMS 本地库存.xls')
   } catch {
   } finally {
     exportLoading.value = false
@@ -497,10 +577,13 @@ onUnmounted(() => {
 })
 
 // 监听列表数据变化,重新计算列宽
-watch(list, () => {
-  nextTick(calculateColumnWidths)
-}, { deep: true })
-
+watch(
+  list,
+  () => {
+    nextTick(calculateColumnWidths)
+  },
+  { deep: true }
+)
 </script>
 
 <style scoped>
@@ -530,7 +613,7 @@ watch(list, () => {
 .stat-value {
   font-size: 18px;
   font-weight: bold;
-  color: #409EFF;
+  color: #409eff;
 }
 
 /* 表格容器样式 - 确保可以水平滚动 */
@@ -550,7 +633,6 @@ watch(list, () => {
   white-space: nowrap;
 }
 
-
 /* 防止表格内容换行 */
 :deep(.el-table .cell) {
   white-space: nowrap !important;
@@ -577,5 +659,4 @@ watch(list, () => {
   overflow: visible;
   text-overflow: unset;
 }
-
 </style>

+ 200 - 112
src/views/pms/iotsapstock/index.vue

@@ -9,7 +9,14 @@
       label-width="68px"
     >
       <el-form-item :label="t('workOrderMaterial.factory')" prop="factoryId">
-        <el-select v-model="queryParams.factoryId" clearable filterable :placeholder="t('faultForm.choose')" class="!w-240px" @change="selectedFactoryChange">
+        <el-select
+          v-model="queryParams.factoryId"
+          clearable
+          filterable
+          :placeholder="t('faultForm.choose')"
+          class="!w-240px"
+          @change="selectedFactoryChange"
+        >
           <el-option
             v-for="item in factoryList"
             :key="item.id"
@@ -18,8 +25,18 @@
           />
         </el-select>
       </el-form-item>
-      <el-form-item :label="t('workOrderMaterial.storageLocation')" prop="storageLocationId" style="margin-left: 28px">
-        <el-select v-model="queryParams.storageLocationId" clearable filterable :placeholder="t('faultForm.choose')" class="!w-240px">
+      <el-form-item
+        :label="t('workOrderMaterial.storageLocation')"
+        prop="storageLocationId"
+        style="margin-left: 28px"
+      >
+        <el-select
+          v-model="queryParams.storageLocationId"
+          clearable
+          filterable
+          :placeholder="t('faultForm.choose')"
+          class="!w-240px"
+        >
           <el-option
             v-for="item in storageLocationList"
             :key="item.id"
@@ -73,15 +90,14 @@
         </el-select>
       </el-form-item>
       <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />  {{ t('operationFill.reset') }}</el-button>
-        <el-button
-          type="success"
-          plain
-          @click="handleExport"
-          :loading="exportLoading"
-          v-hasPermi="['pms:iot-sap-stock:export']"
+        <el-button @click="handleQuery"
+          ><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button
+        >
+        <el-button @click="resetQuery"
+          ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('operationFill.reset') }}</el-button
         >
+        <!-- v-hasPermi="['pms:iot-sap-stock:export']" -->
+        <el-button type="success" plain @click="handleExport" :loading="exportLoading">
           <Icon icon="ep:download" class="mr-5px" /> 导出
         </el-button>
       </el-form-item>
@@ -98,7 +114,15 @@
         </div>
         <div class="stat-item">
           <span class="stat-label">{{ t('stock.totalAmount') }}:</span>
-          <span class="stat-value">¥ {{ totalAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}</span>
+          <span class="stat-value"
+            >¥
+            {{
+              totalAmount.toLocaleString(undefined, {
+                minimumFractionDigits: 2,
+                maximumFractionDigits: 2
+              })
+            }}</span
+          >
         </div>
       </div>
     </el-card>
@@ -106,18 +130,62 @@
 
   <!-- 列表 -->
   <ContentWrap ref="tableContainerRef" class="table-container">
-    <el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="false" style="width: 100%">
-      <el-table-column :label="t('workOrderMaterial.factory')" align="center" prop="factory" :width="columnWidths.factory"/>
-      <el-table-column :label="t('workOrderMaterial.storageLocation')" align="center"
-                       prop="projectDepartment" :width="columnWidths.projectDepartment"/>
-      <el-table-column :label="t('chooseMaintain.materialCode')" align="center"
-                       prop="materialCode" :width="columnWidths.materialCode"/>
-      <el-table-column :label="t('chooseMaintain.materialName')" align="left"
-                       prop="materialName" :width="columnWidths.materialName"/>
-      <el-table-column :label="t('route.quantity')" align="center" prop="quantity" :width="columnWidths.quantity"/>
-      <el-table-column :label="t('workOrderMaterial.unitPrice')" align="center" prop="unitPrice" :width="columnWidths.unitPrice"/>
-      <el-table-column :label="t('workOrderMaterial.unit')" align="center" prop="unit" :width="columnWidths.unit"/>
-      <el-table-column :label="t('route.safetyStock')" align="center" prop="safetyStock" :width="columnWidths.safetyStock"/>
+    <el-table
+      ref="tableRef"
+      v-loading="loading"
+      :data="list"
+      :stripe="true"
+      :show-overflow-tooltip="false"
+      style="width: 100%"
+    >
+      <el-table-column
+        :label="t('workOrderMaterial.factory')"
+        align="center"
+        prop="factory"
+        :width="columnWidths.factory"
+      />
+      <el-table-column
+        :label="t('workOrderMaterial.storageLocation')"
+        align="center"
+        prop="projectDepartment"
+        :width="columnWidths.projectDepartment"
+      />
+      <el-table-column
+        :label="t('chooseMaintain.materialCode')"
+        align="center"
+        prop="materialCode"
+        :width="columnWidths.materialCode"
+      />
+      <el-table-column
+        :label="t('chooseMaintain.materialName')"
+        align="left"
+        prop="materialName"
+        :width="columnWidths.materialName"
+      />
+      <el-table-column
+        :label="t('route.quantity')"
+        align="center"
+        prop="quantity"
+        :width="columnWidths.quantity"
+      />
+      <el-table-column
+        :label="t('workOrderMaterial.unitPrice')"
+        align="center"
+        prop="unitPrice"
+        :width="columnWidths.unitPrice"
+      />
+      <el-table-column
+        :label="t('workOrderMaterial.unit')"
+        align="center"
+        prop="unit"
+        :width="columnWidths.unit"
+      />
+      <el-table-column
+        :label="t('route.safetyStock')"
+        align="center"
+        prop="safetyStock"
+        :width="columnWidths.safetyStock"
+      />
       <el-table-column
         :label="t('chooseMaintain.createTime')"
         align="center"
@@ -144,7 +212,7 @@ import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotSapStockApi, IotSapStockVO } from '@/api/pms/iotsapstock'
 import IotSapStockForm from './IotSapStockForm.vue'
-import * as SapOrgApi from "@/api/system/saporg";
+import * as SapOrgApi from '@/api/system/saporg'
 
 /** PMS SAP 库存(通用库存/项目部库存) 列表 */
 defineOptions({ name: 'IotSapStock' })
@@ -152,11 +220,11 @@ defineOptions({ name: 'IotSapStock' })
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
 
-const factoryList = ref([] as SapOrgApi.SapOrgVO[])   // 工厂列表
+const factoryList = ref([] as SapOrgApi.SapOrgVO[]) // 工厂列表
 const storageLocationList = ref([] as SapOrgApi.SapOrgVO[]) // 库存地点列表
 // 新增统计变量
-const totalQuantity = ref(0)   // 总数量
-const totalAmount = ref(0)     // 总金额
+const totalQuantity = ref(0) // 总数量
+const totalAmount = ref(0) // 总金额
 
 const loading = ref(true) // 列表的加载中
 const list = ref<IotSapStockVO[]>([]) // 列表的数据
@@ -187,7 +255,7 @@ const queryParams = reactive({
   status: undefined,
   remark: undefined,
   configFlag: 'A',
-  createTime: [],
+  createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
@@ -211,120 +279,142 @@ const columnWidths = ref({
 
 /** 获取滚动条宽度 */
 const getScrollbarWidth = () => {
-  const outer = document.createElement('div');
-  outer.style.visibility = 'hidden';
-  outer.style.overflow = 'scroll';
-  document.body.appendChild(outer);
+  const outer = document.createElement('div')
+  outer.style.visibility = 'hidden'
+  outer.style.overflow = 'scroll'
+  document.body.appendChild(outer)
 
-  const inner = document.createElement('div');
-  outer.appendChild(inner);
+  const inner = document.createElement('div')
+  outer.appendChild(inner)
 
-  const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
-  outer.parentNode?.removeChild(outer);
+  const scrollbarWidth = outer.offsetWidth - inner.offsetWidth
+  outer.parentNode?.removeChild(outer)
 
-  return scrollbarWidth;
-};
+  return scrollbarWidth
+}
 
 /** 计算文本宽度 */
 const getTextWidth = (text: string, fontSize = 14) => {
-  const span = document.createElement('span');
-  span.style.visibility = 'hidden';
-  span.style.position = 'absolute';
-  span.style.whiteSpace = 'nowrap';
-  span.style.fontSize = `${fontSize}px`;
-  span.style.fontFamily = 'inherit';
-  span.innerText = text;
-
-  document.body.appendChild(span);
-  const width = span.offsetWidth;
-  document.body.removeChild(span);
-
-  return width;
-};
+  const span = document.createElement('span')
+  span.style.visibility = 'hidden'
+  span.style.position = 'absolute'
+  span.style.whiteSpace = 'nowrap'
+  span.style.fontSize = `${fontSize}px`
+  span.style.fontFamily = 'inherit'
+  span.innerText = text
+
+  document.body.appendChild(span)
+  const width = span.offsetWidth
+  document.body.removeChild(span)
+
+  return width
+}
 
 /** 计算列宽度 */
 const calculateColumnWidths = () => {
-  const MIN_WIDTH = 80; // 最小列宽
-  const PADDING = 25; // 列内边距
-  const FLEXIBLE_COLUMN = 'materialName'; // 可伸缩列
-  const scrollbarWidth = getScrollbarWidth(); // 动态获取滚动条宽度
+  const MIN_WIDTH = 80 // 最小列宽
+  const PADDING = 25 // 列内边距
+  const FLEXIBLE_COLUMN = 'materialName' // 可伸缩列
+  const scrollbarWidth = getScrollbarWidth() // 动态获取滚动条宽度
 
-  if (!tableContainerRef.value?.$el || list.value.length === 0) return;
+  if (!tableContainerRef.value?.$el || list.value.length === 0) return
 
-  const containerWidth = tableContainerRef.value.$el.clientWidth;
+  const containerWidth = tableContainerRef.value.$el.clientWidth
 
   // 需要自适应的列配置
   const autoColumns = [
     { key: 'factory', label: t('workOrderMaterial.factory'), getValue: (row) => row.factory },
-    { key: 'projectDepartment', label: t('workOrderMaterial.storageLocation'), getValue: (row) => row.projectDepartment },
-    { key: 'materialCode', label: t('chooseMaintain.materialCode'), getValue: (row) => row.materialCode },
-    { key: 'materialName', label: t('chooseMaintain.materialName'), getValue: (row) => row.materialName },
+    {
+      key: 'projectDepartment',
+      label: t('workOrderMaterial.storageLocation'),
+      getValue: (row) => row.projectDepartment
+    },
+    {
+      key: 'materialCode',
+      label: t('chooseMaintain.materialCode'),
+      getValue: (row) => row.materialCode
+    },
+    {
+      key: 'materialName',
+      label: t('chooseMaintain.materialName'),
+      getValue: (row) => row.materialName
+    },
     { key: 'quantity', label: t('route.quantity'), getValue: (row) => String(row.quantity) },
-    { key: 'unitPrice', label: t('workOrderMaterial.unitPrice'), getValue: (row) => String(row.unitPrice) },
+    {
+      key: 'unitPrice',
+      label: t('workOrderMaterial.unitPrice'),
+      getValue: (row) => String(row.unitPrice)
+    },
     { key: 'unit', label: t('workOrderMaterial.unit'), getValue: (row) => row.unit },
-    { key: 'safetyStock', label: t('route.safetyStock'), getValue: (row) => String(row.safetyStock) },
+    {
+      key: 'safetyStock',
+      label: t('route.safetyStock'),
+      getValue: (row) => String(row.safetyStock)
+    },
     {
       key: 'createTime',
       label: t('chooseMaintain.createTime'),
       getValue: (row) => dateFormatter(null, null, row.createTime)
     }
-  ];
+  ]
 
-  const newWidths: Record<string, string> = {};
-  let totalFixedWidth = 0; // 所有固定列的总宽度
+  const newWidths: Record<string, string> = {}
+  let totalFixedWidth = 0 // 所有固定列的总宽度
 
   // 计算除可伸缩列外的所有列宽度
-  autoColumns.forEach(col => {
-    if (col.key === FLEXIBLE_COLUMN) return;
+  autoColumns.forEach((col) => {
+    if (col.key === FLEXIBLE_COLUMN) return
 
-    const headerText = col.label;
-    const headerWidth = getTextWidth(headerText) * 1.3; // 表头宽度(加粗效果增加30%)
+    const headerText = col.label
+    const headerWidth = getTextWidth(headerText) * 1.3 // 表头宽度(加粗效果增加30%)
 
     // 计算内容最大宽度
-    let contentMaxWidth = 0;
-    list.value.forEach(row => {
-      const text = col.getValue ? String(col.getValue(row)) : String(row[col.key] || '');
-      const textWidth = getTextWidth(text);
-      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
-    });
+    let contentMaxWidth = 0
+    list.value.forEach((row) => {
+      const text = col.getValue ? String(col.getValue(row)) : String(row[col.key] || '')
+      const textWidth = getTextWidth(text)
+      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth
+    })
 
     // 取表头宽度、内容最大宽度和最小宽度的最大值
-    const finalWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING;
-    newWidths[col.key] = `${finalWidth}px`;
-    totalFixedWidth += finalWidth;
-  });
+    const finalWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING
+    newWidths[col.key] = `${finalWidth}px`
+    totalFixedWidth += finalWidth
+  })
 
   // 处理可伸缩列(materialName)
-  const flexibleCol = autoColumns.find(col => col.key === FLEXIBLE_COLUMN);
+  const flexibleCol = autoColumns.find((col) => col.key === FLEXIBLE_COLUMN)
   if (flexibleCol) {
-    const headerText = flexibleCol.label;
-    const headerWidth = getTextWidth(headerText) * 1.3;
-
-    let contentMaxWidth = 0;
-    list.value.forEach(row => {
-      const text = flexibleCol.getValue ? String(flexibleCol.getValue(row)) : String(row[flexibleCol.key] || '');
-      const textWidth = getTextWidth(text);
-      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
-    });
+    const headerText = flexibleCol.label
+    const headerWidth = getTextWidth(headerText) * 1.3
+
+    let contentMaxWidth = 0
+    list.value.forEach((row) => {
+      const text = flexibleCol.getValue
+        ? String(flexibleCol.getValue(row))
+        : String(row[flexibleCol.key] || '')
+      const textWidth = getTextWidth(text)
+      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth
+    })
 
-    const baseWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING;
+    const baseWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING
 
     // 剩余空间 = 容器宽度 - 其他列总宽度 - 垂直滚动条宽度
-    const remainingWidth = containerWidth - totalFixedWidth - scrollbarWidth;
+    const remainingWidth = containerWidth - totalFixedWidth - scrollbarWidth
 
     // 可伸缩列的宽度取剩余空间和基础宽度的最大值
-    const flexibleWidth = Math.max(remainingWidth, baseWidth);
-    newWidths[FLEXIBLE_COLUMN] = `${flexibleWidth}px`;
+    const flexibleWidth = Math.max(remainingWidth, baseWidth)
+    newWidths[FLEXIBLE_COLUMN] = `${flexibleWidth}px`
   }
 
   // 更新列宽度
-  columnWidths.value = newWidths;
+  columnWidths.value = newWidths
 
   // 重新布局表格
   nextTick(() => {
-    tableRef.value?.doLayout();
-  });
-};
+    tableRef.value?.doLayout()
+  })
+}
 
 /** 查询列表 */
 const getList = async () => {
@@ -381,9 +471,8 @@ const selectedFactoryReqVO = ref({
 
 /** 已经选择了 SAP工厂 */
 const selectedFactoryChange = async (selectedId: number | undefined) => {
-
   // 获取选中的factoryCode数组
-  const selectedFactory = factoryList.value.find(item => item.id === selectedId)
+  const selectedFactory = factoryList.value.find((item) => item.id === selectedId)
   const selectedFactoryCodes = selectedFactory ? [selectedFactory.factoryCode] : []
 
   // 获得已经选择的 SAP 工厂 数组
@@ -408,7 +497,7 @@ const resultOptions = computed(() => [
   {
     label: '否',
     value: 'N' // 空值会触发 clearable 效果
-  },
+  }
 ])
 
 /** 删除按钮操作 */
@@ -427,12 +516,9 @@ const handleDelete = async (id: number) => {
 /** 导出按钮操作 */
 const handleExport = async () => {
   try {
-    // 导出的二次确认
-    await message.exportConfirm()
-    // 发起导出
     exportLoading.value = true
     const data = await IotSapStockApi.exportIotSapStock(queryParams)
-    download.excel(data, 'PMS SAP 库存(通用库存/项目部库存).xls')
+    download.excel(data, 'SPA库存.xls')
   } catch {
   } finally {
     exportLoading.value = false
@@ -452,10 +538,13 @@ onUnmounted(() => {
 })
 
 // 监听列表数据变化,重新计算列宽
-watch(list, () => {
-  nextTick(calculateColumnWidths)
-}, { deep: true })
-
+watch(
+  list,
+  () => {
+    nextTick(calculateColumnWidths)
+  },
+  { deep: true }
+)
 </script>
 
 <style scoped>
@@ -485,7 +574,7 @@ watch(list, () => {
 .stat-value {
   font-size: 18px;
   font-weight: bold;
-  color: #409EFF;
+  color: #409eff;
 }
 
 /* 表格容器样式 - 确保可以水平滚动 */
@@ -526,5 +615,4 @@ watch(list, () => {
   overflow: visible;
   text-overflow: unset;
 }
-
 </style>