|
@@ -1,230 +1,52 @@
|
|
|
-<template>
|
|
|
|
|
- <ContentWrap>
|
|
|
|
|
- <!-- 搜索工作栏 -->
|
|
|
|
|
- <el-form
|
|
|
|
|
- class="-mb-15px"
|
|
|
|
|
- :model="queryParams"
|
|
|
|
|
- ref="queryFormRef"
|
|
|
|
|
- :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-option
|
|
|
|
|
- v-for="item in factoryList"
|
|
|
|
|
- :key="item.id"
|
|
|
|
|
- :label="item.factoryName"
|
|
|
|
|
- :value="item.id!"
|
|
|
|
|
- />
|
|
|
|
|
- </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-option
|
|
|
|
|
- v-for="item in filteredCostCenterList"
|
|
|
|
|
- :key="item.id"
|
|
|
|
|
- :label="item.costCenterName"
|
|
|
|
|
- :value="item.id!"
|
|
|
|
|
- />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item :label="t('chooseMaintain.materialCode')" prop="materialCode">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="queryParams.materialCode"
|
|
|
|
|
- :placeholder="t('chooseMaintain.materialCode')"
|
|
|
|
|
- clearable
|
|
|
|
|
- @keyup.enter="handleQuery"
|
|
|
|
|
- class="!w-240px"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item :label="t('chooseMaintain.materialName')" prop="materialName">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="queryParams.materialName"
|
|
|
|
|
- :placeholder="t('chooseMaintain.materialName')"
|
|
|
|
|
- clearable
|
|
|
|
|
- @keyup.enter="handleQuery"
|
|
|
|
|
- class="!w-240px"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item :label="t('chooseMaintain.createTime')" prop="storageTime">
|
|
|
|
|
- <el-date-picker
|
|
|
|
|
- v-model="queryParams.storageTime"
|
|
|
|
|
- value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
- type="daterange"
|
|
|
|
|
- :start-placeholder="t('info.start')"
|
|
|
|
|
- :end-placeholder="t('info.end')"
|
|
|
|
|
- :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
|
|
|
|
- class="!w-220px"
|
|
|
|
|
- />
|
|
|
|
|
- </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="primary"
|
|
|
|
|
- plain
|
|
|
|
|
- @click="openForm('create')"
|
|
|
|
|
- v-hasPermi="['pms:iot-lock-stock:create']"
|
|
|
|
|
- >
|
|
|
|
|
- <Icon icon="ep:plus" class="mr-5px" />{{ t('operationFill.add') }}
|
|
|
|
|
- </el-button>
|
|
|
|
|
- <!-- 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>
|
|
|
|
|
- </el-form>
|
|
|
|
|
- </ContentWrap>
|
|
|
|
|
-
|
|
|
|
|
- <!-- ========== 统计信息卡片 ========== -->
|
|
|
|
|
- <ContentWrap style="margin-bottom: 16px">
|
|
|
|
|
- <el-card shadow="never" class="stat-card">
|
|
|
|
|
- <div class="stat-container">
|
|
|
|
|
- <div class="stat-item">
|
|
|
|
|
- <span class="stat-label">{{ t('stock.totalQuantity') }}:</span>
|
|
|
|
|
- <span class="stat-value">{{ totalQuantity.toLocaleString() }}</span>
|
|
|
|
|
- </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
|
|
|
|
|
- >
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </ContentWrap>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 列表 -->
|
|
|
|
|
- <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-column
|
|
|
|
|
- :label="t('stock.storageTime')"
|
|
|
|
|
- align="center"
|
|
|
|
|
- prop="storageTime"
|
|
|
|
|
- :formatter="dateFormatter"
|
|
|
|
|
- :width="columnWidths.storageTime"
|
|
|
|
|
- />
|
|
|
|
|
- </el-table>
|
|
|
|
|
- <!-- 分页 -->
|
|
|
|
|
- <Pagination
|
|
|
|
|
- :total="total"
|
|
|
|
|
- v-model:page="queryParams.pageNo"
|
|
|
|
|
- v-model:limit="queryParams.pageSize"
|
|
|
|
|
- @pagination="getList"
|
|
|
|
|
- />
|
|
|
|
|
- </ContentWrap>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 表单弹窗:添加/修改 -->
|
|
|
|
|
- <IotLockStockForm ref="formRef" @success="getList" />
|
|
|
|
|
-</template>
|
|
|
|
|
-
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { dateFormatter } from '@/utils/formatTime'
|
|
|
|
|
-import download from '@/utils/download'
|
|
|
|
|
|
|
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
|
|
|
import { IotLockStockApi, IotLockStockVO } from '@/api/pms/iotlockstock'
|
|
import { IotLockStockApi, IotLockStockVO } from '@/api/pms/iotlockstock'
|
|
|
-import IotLockStockForm from './IotLockStockForm.vue'
|
|
|
|
|
-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 { useUserStore } from '@/store/modules/user'
|
|
|
import { checkRole } from '@/utils/permission'
|
|
import { checkRole } from '@/utils/permission'
|
|
|
-import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
|
|
|
|
|
+import { dateFormatter } from '@/utils/formatTime'
|
|
|
|
|
+import download from '@/utils/download'
|
|
|
|
|
+import { erpPriceTableColumnFormatter } from '@/utils'
|
|
|
|
|
+import CountTo from '@/components/count-to1.vue'
|
|
|
|
|
|
|
|
-/** PMS 本地 库存 列表 */
|
|
|
|
|
defineOptions({ name: 'IotLockStock' })
|
|
defineOptions({ name: 'IotLockStock' })
|
|
|
|
|
|
|
|
-const message = useMessage() // 消息弹窗
|
|
|
|
|
-const { t } = useI18n() // 国际化
|
|
|
|
|
|
|
+type LockStockRow = IotLockStockVO & {
|
|
|
|
|
+ totalQuantity?: number | string
|
|
|
|
|
+ totalAmount?: number | string
|
|
|
|
|
+ storageTime?: string
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-const { push } = useRouter() // 路由跳转
|
|
|
|
|
-// 过滤后的成本中心列表(用于动态显示)
|
|
|
|
|
-const filteredCostCenterList = ref<SapOrgVO[]>([])
|
|
|
|
|
-const loading = ref(true) // 列表的加载中
|
|
|
|
|
-const list = ref<IotLockStockVO[]>([]) // 列表的数据
|
|
|
|
|
-const total = ref(0) // 列表的总页数
|
|
|
|
|
-const queryParams = reactive({
|
|
|
|
|
|
|
+interface QueryParams extends PageParam {
|
|
|
|
|
+ deptId?: number
|
|
|
|
|
+ factory?: string
|
|
|
|
|
+ factoryId?: number
|
|
|
|
|
+ projectDepartment?: string
|
|
|
|
|
+ storageLocationId?: number
|
|
|
|
|
+ costCenter?: string
|
|
|
|
|
+ costCenterId?: number
|
|
|
|
|
+ pickingListNumber?: string
|
|
|
|
|
+ materialCode?: string
|
|
|
|
|
+ materialName?: string
|
|
|
|
|
+ materialGroupName?: string
|
|
|
|
|
+ materialGroupId?: number
|
|
|
|
|
+ quantity?: number
|
|
|
|
|
+ unitPrice?: number
|
|
|
|
|
+ unit?: string
|
|
|
|
|
+ storageTime?: string[]
|
|
|
|
|
+ sort?: number
|
|
|
|
|
+ status?: number
|
|
|
|
|
+ processInstanceId?: string
|
|
|
|
|
+ auditStatus?: number
|
|
|
|
|
+ remark?: string
|
|
|
|
|
+ createTime?: string[]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const { t } = useI18n()
|
|
|
|
|
+const { push } = useRouter()
|
|
|
|
|
+const { ZmTable, ZmTableColumn } = useTableComponents<LockStockRow>()
|
|
|
|
|
+
|
|
|
|
|
+const initQuery: QueryParams = {
|
|
|
pageNo: 1,
|
|
pageNo: 1,
|
|
|
pageSize: 10,
|
|
pageSize: 10,
|
|
|
deptId: undefined,
|
|
deptId: undefined,
|
|
@@ -249,414 +71,511 @@ const queryParams = reactive({
|
|
|
auditStatus: undefined,
|
|
auditStatus: undefined,
|
|
|
remark: undefined,
|
|
remark: undefined,
|
|
|
createTime: []
|
|
createTime: []
|
|
|
-})
|
|
|
|
|
-const queryFormRef = ref() // 搜索的表单
|
|
|
|
|
-const exportLoading = ref(false) // 导出的加载中
|
|
|
|
|
-
|
|
|
|
|
-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 tableContainerRef = ref()
|
|
|
|
|
-const tableRef = ref()
|
|
|
|
|
-
|
|
|
|
|
-const shouldHideComponents = computed(() => {
|
|
|
|
|
- // 检查用户是否拥有 'A' 或 'B' 角色
|
|
|
|
|
- return checkRole(['小队队长', '操作员'])
|
|
|
|
|
-})
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-const selectedFactoryReqVO = ref({
|
|
|
|
|
- type: 0, // 类型(1工厂 2成本中心 3库位)
|
|
|
|
|
- factoryCodes: [] // 已经选择的SAP工厂code 列表
|
|
|
|
|
-})
|
|
|
|
|
|
|
+const queryParams = reactive<QueryParams>({ ...initQuery })
|
|
|
|
|
+const queryFormRef = ref()
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+const exportLoading = ref(false)
|
|
|
|
|
+const list = ref<LockStockRow[]>([])
|
|
|
|
|
+const total = ref(0)
|
|
|
|
|
+const totalQuantity = ref(0)
|
|
|
|
|
+const totalAmount = ref(0)
|
|
|
|
|
+const factoryList = ref<SapOrgVO[]>([])
|
|
|
|
|
+const costCenterList = ref<SapOrgVO[]>([])
|
|
|
|
|
+const filteredCostCenterList = ref<SapOrgVO[]>([])
|
|
|
|
|
|
|
|
-// 列宽度配置
|
|
|
|
|
-const columnWidths = ref({
|
|
|
|
|
- factory: '120px',
|
|
|
|
|
- costCenter: '120px',
|
|
|
|
|
- materialCode: '120px',
|
|
|
|
|
- materialName: '200px', // 初始宽度,会被计算覆盖
|
|
|
|
|
- quantity: '100px',
|
|
|
|
|
- unitPrice: '100px',
|
|
|
|
|
- unit: '100px',
|
|
|
|
|
- storageTime: '180px'
|
|
|
|
|
-})
|
|
|
|
|
|
|
+const shouldHideComponents = computed(() => checkRole(['小队队长', '操作员']))
|
|
|
|
|
|
|
|
-/** 获取滚动条宽度 */
|
|
|
|
|
-const getScrollbarWidth = () => {
|
|
|
|
|
- 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 scrollbarWidth = outer.offsetWidth - inner.offsetWidth
|
|
|
|
|
- outer.parentNode?.removeChild(outer)
|
|
|
|
|
-
|
|
|
|
|
- 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 calculateColumnWidths = () => {
|
|
|
|
|
- const MIN_WIDTH = 80 // 最小列宽
|
|
|
|
|
- const PADDING = 25 // 列内边距
|
|
|
|
|
- const FLEXIBLE_COLUMN = 'materialName' // 可伸缩列
|
|
|
|
|
- const scrollbarWidth = getScrollbarWidth() // 动态获取滚动条宽度
|
|
|
|
|
-
|
|
|
|
|
- if (!tableContainerRef.value?.$el || list.value.length === 0) return
|
|
|
|
|
-
|
|
|
|
|
- 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: 'quantity',
|
|
|
|
|
- label: t('route.quantity'),
|
|
|
|
|
- getValue: (row) => erpPriceTableColumnFormatter(null, null, row.quantity, null)
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- key: 'unitPrice',
|
|
|
|
|
- label: t('workOrderMaterial.unitPrice'),
|
|
|
|
|
- getValue: (row) => erpPriceTableColumnFormatter(null, null, row.unitPrice, null)
|
|
|
|
|
- },
|
|
|
|
|
- { key: 'unit', label: t('workOrderMaterial.unit'), getValue: (row) => row.unit },
|
|
|
|
|
- {
|
|
|
|
|
- key: 'storageTime',
|
|
|
|
|
- label: t('stock.storageTime'),
|
|
|
|
|
- getValue: (row) => dateFormatter(null, null, row.storageTime)
|
|
|
|
|
- }
|
|
|
|
|
- ]
|
|
|
|
|
-
|
|
|
|
|
- const newWidths: Record<string, string> = {}
|
|
|
|
|
- let totalFixedWidth = 0 // 所有固定列的总宽度
|
|
|
|
|
-
|
|
|
|
|
- // 计算除可伸缩列外的所有列宽度
|
|
|
|
|
- autoColumns.forEach((col) => {
|
|
|
|
|
- if (col.key === FLEXIBLE_COLUMN) return
|
|
|
|
|
-
|
|
|
|
|
- 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
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- // 取表头宽度、内容最大宽度和最小宽度的最大值
|
|
|
|
|
- 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)
|
|
|
|
|
- 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 baseWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING
|
|
|
|
|
-
|
|
|
|
|
- // 剩余空间 = 容器宽度 - 其他列总宽度 - 垂直滚动条宽度(17px)
|
|
|
|
|
- const remainingWidth = containerWidth - totalFixedWidth - scrollbarWidth
|
|
|
|
|
-
|
|
|
|
|
- // 可伸缩列的宽度取剩余空间和基础宽度的最大值
|
|
|
|
|
- const flexibleWidth = Math.max(remainingWidth, baseWidth)
|
|
|
|
|
- newWidths[FLEXIBLE_COLUMN] = `${flexibleWidth}px`
|
|
|
|
|
|
|
+const stockStatCards = computed(() => [
|
|
|
|
|
+ {
|
|
|
|
|
+ label: t('stock.totalQuantity'),
|
|
|
|
|
+ value: totalQuantity.value,
|
|
|
|
|
+ prefix: '',
|
|
|
|
|
+ unit: '',
|
|
|
|
|
+ decimals: 0,
|
|
|
|
|
+ icon: 'i-tabler:packages text-sky'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ label: t('stock.totalAmount'),
|
|
|
|
|
+ value: totalAmount.value,
|
|
|
|
|
+ prefix: '¥',
|
|
|
|
|
+ unit: '',
|
|
|
|
|
+ decimals: 2,
|
|
|
|
|
+ icon: 'i-material-symbols:payments-outline-rounded text-emerald'
|
|
|
}
|
|
}
|
|
|
|
|
+])
|
|
|
|
|
|
|
|
- // 更新列宽度
|
|
|
|
|
- columnWidths.value = newWidths
|
|
|
|
|
-
|
|
|
|
|
- // 重新布局表格
|
|
|
|
|
- nextTick(() => {
|
|
|
|
|
- tableRef.value?.doLayout()
|
|
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/** 查询列表 */
|
|
|
|
|
const getList = async () => {
|
|
const getList = async () => {
|
|
|
loading.value = true
|
|
loading.value = true
|
|
|
try {
|
|
try {
|
|
|
const data = await IotLockStockApi.getIotLockStockPage(queryParams)
|
|
const data = await IotLockStockApi.getIotLockStockPage(queryParams)
|
|
|
list.value = data.list
|
|
list.value = data.list
|
|
|
total.value = data.total
|
|
total.value = data.total
|
|
|
- // 从第一条记录中提取统计值
|
|
|
|
|
- if (data.list && data.list.length > 0) {
|
|
|
|
|
- // 确保取到有效的数值(第一条记录中的统计值代表整个查询结果)
|
|
|
|
|
- totalQuantity.value = Number(data.list[0].totalQuantity) || 0
|
|
|
|
|
- totalAmount.value = Number(data.list[0].totalAmount) || 0
|
|
|
|
|
- } else {
|
|
|
|
|
- // 没有数据时重置为0
|
|
|
|
|
- totalQuantity.value = 0
|
|
|
|
|
- totalAmount.value = 0
|
|
|
|
|
- }
|
|
|
|
|
- // 数据加载完成后计算列宽
|
|
|
|
|
- nextTick(() => {
|
|
|
|
|
- calculateColumnWidths()
|
|
|
|
|
- })
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const firstRow = data.list?.[0]
|
|
|
|
|
+ totalQuantity.value = Number(firstRow?.totalQuantity) || 0
|
|
|
|
|
+ totalAmount.value = Number(firstRow?.totalAmount) || 0
|
|
|
} finally {
|
|
} finally {
|
|
|
loading.value = false
|
|
loading.value = false
|
|
|
}
|
|
}
|
|
|
- // 获取当前登录人的部门id 查询部门及子部门关联的所有SAP组织
|
|
|
|
|
- // 获取组织数据(移出统计值处理逻辑)
|
|
|
|
|
- await loadOrgData()
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 单独封装组织数据加载方法
|
|
|
|
|
const loadOrgData = async () => {
|
|
const loadOrgData = async () => {
|
|
|
const deptId = useUserStore().getUser.deptId
|
|
const deptId = useUserStore().getUser.deptId
|
|
|
- if (typeof deptId === 'number' && !isNaN(deptId) && deptId > 0) {
|
|
|
|
|
- try {
|
|
|
|
|
- const [factories, costCenters] = await Promise.all([
|
|
|
|
|
- SapOrgApi.filteredSimpleSapOrgList(1, deptId),
|
|
|
|
|
- SapOrgApi.filteredSimpleSapOrgList(2, deptId)
|
|
|
|
|
- ])
|
|
|
|
|
- factoryList.value = factories
|
|
|
|
|
- costCenterList.value = costCenters
|
|
|
|
|
- // 初始化时显示全部成本中心
|
|
|
|
|
- filteredCostCenterList.value = costCenters
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('获取组织数据失败:', error)
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- console.warn('无效的部门ID:', deptId)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (typeof deptId !== 'number' || Number.isNaN(deptId) || deptId <= 0) return
|
|
|
|
|
+
|
|
|
|
|
+ const [factories, costCenters] = await Promise.all([
|
|
|
|
|
+ SapOrgApi.filteredSimpleSapOrgList(1, deptId),
|
|
|
|
|
+ SapOrgApi.filteredSimpleSapOrgList(2, deptId)
|
|
|
|
|
+ ])
|
|
|
|
|
+ factoryList.value = factories
|
|
|
|
|
+ costCenterList.value = costCenters
|
|
|
|
|
+ filteredCostCenterList.value = costCenters
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 搜索按钮操作 */
|
|
|
|
|
const handleQuery = () => {
|
|
const handleQuery = () => {
|
|
|
queryParams.pageNo = 1
|
|
queryParams.pageNo = 1
|
|
|
getList()
|
|
getList()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 重置按钮操作 */
|
|
|
|
|
const resetQuery = () => {
|
|
const resetQuery = () => {
|
|
|
- queryFormRef.value.resetFields()
|
|
|
|
|
- // 重置后恢复成本中心为完整列表
|
|
|
|
|
|
|
+ Object.assign(queryParams, { ...initQuery })
|
|
|
|
|
+ queryFormRef.value?.resetFields()
|
|
|
filteredCostCenterList.value = costCenterList.value
|
|
filteredCostCenterList.value = costCenterList.value
|
|
|
handleQuery()
|
|
handleQuery()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 添加/修改操作 */
|
|
|
|
|
-const formRef = ref()
|
|
|
|
|
-const openForm = (type: string, id?: number) => {
|
|
|
|
|
- if (typeof id === 'number') {
|
|
|
|
|
- formRef.value.open(type, id)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
|
|
+const handleSizeChange = (val: number) => {
|
|
|
|
|
+ queryParams.pageSize = val
|
|
|
|
|
+ handleQuery()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleCurrentChange = (val: number) => {
|
|
|
|
|
+ queryParams.pageNo = val
|
|
|
|
|
+ getList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const openForm = () => {
|
|
|
push({ name: 'LockStockAdd', params: {} })
|
|
push({ name: 'LockStockAdd', params: {} })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 删除按钮操作 */
|
|
|
|
|
-const handleDelete = async (id: number) => {
|
|
|
|
|
- try {
|
|
|
|
|
- // 删除的二次确认
|
|
|
|
|
- await message.delConfirm()
|
|
|
|
|
- // 发起删除
|
|
|
|
|
- await IotLockStockApi.deleteIotLockStock(id)
|
|
|
|
|
- message.success(t('common.delSuccess'))
|
|
|
|
|
- // 刷新列表
|
|
|
|
|
- await getList()
|
|
|
|
|
- } catch {}
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/** 已经选择了 SAP工厂 */
|
|
|
|
|
-/* const selectedFactoryChange = async (selectedId: number | undefined) => {
|
|
|
|
|
-
|
|
|
|
|
- // 获取选中的factoryCode数组
|
|
|
|
|
- const selectedFactory = factoryList.value.find(item => item.id === selectedId)
|
|
|
|
|
- const selectedFactoryCodes = selectedFactory ? [selectedFactory.factoryCode] : []
|
|
|
|
|
-
|
|
|
|
|
- // 获得已经选择的 SAP 工厂 数组
|
|
|
|
|
- // 根据选择的 SAP工厂 调用后台接口查询 SAP工厂下属的 成本中心
|
|
|
|
|
- selectedFactoryReqVO.value.type = 2
|
|
|
|
|
- selectedFactoryReqVO.value.factoryCodes = selectedFactoryCodes
|
|
|
|
|
- costCenterList.value = await SapOrgApi.getSelectedList(selectedFactoryReqVO.value)
|
|
|
|
|
-
|
|
|
|
|
- // 根据选择的 SAP工厂 调用后台接口查询 SAP工厂下属的 库存地点列表
|
|
|
|
|
- selectedFactoryReqVO.value.type = 3
|
|
|
|
|
- selectedFactoryReqVO.value.factoryCodes = selectedFactoryCodes
|
|
|
|
|
- storageLocationList.value = await SapOrgApi.getSelectedList(selectedFactoryReqVO.value)
|
|
|
|
|
-} */
|
|
|
|
|
-
|
|
|
|
|
-/** 已经选择了 SAP工厂 */
|
|
|
|
|
-const selectedFactoryChange = async (selectedId: number | undefined) => {
|
|
|
|
|
- // 清空已选择的成本中心
|
|
|
|
|
|
|
+const selectedFactoryChange = (selectedId: number | undefined) => {
|
|
|
queryParams.costCenterId = undefined
|
|
queryParams.costCenterId = undefined
|
|
|
|
|
|
|
|
if (!selectedId) {
|
|
if (!selectedId) {
|
|
|
- // 未选择工厂时显示全部成本中心
|
|
|
|
|
filteredCostCenterList.value = costCenterList.value
|
|
filteredCostCenterList.value = costCenterList.value
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 获取选中的工厂对象
|
|
|
|
|
const selectedFactory = factoryList.value.find((item) => item.id === selectedId)
|
|
const selectedFactory = factoryList.value.find((item) => item.id === selectedId)
|
|
|
if (!selectedFactory) return
|
|
if (!selectedFactory) return
|
|
|
|
|
|
|
|
- // 根据工厂代码过滤成本中心
|
|
|
|
|
filteredCostCenterList.value = costCenterList.value.filter(
|
|
filteredCostCenterList.value = costCenterList.value.filter(
|
|
|
(item) => item.factoryCode === selectedFactory.factoryCode
|
|
(item) => item.factoryCode === selectedFactory.factoryCode
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 导出按钮操作 */
|
|
|
|
|
const handleExport = async () => {
|
|
const handleExport = async () => {
|
|
|
try {
|
|
try {
|
|
|
exportLoading.value = true
|
|
exportLoading.value = true
|
|
|
const data = await IotLockStockApi.exportIotLockStock(queryParams)
|
|
const data = await IotLockStockApi.exportIotLockStock(queryParams)
|
|
|
download.excel(data, 'PMS 本地库存.xls')
|
|
download.excel(data, 'PMS 本地库存.xls')
|
|
|
- } catch {
|
|
|
|
|
} finally {
|
|
} finally {
|
|
|
exportLoading.value = false
|
|
exportLoading.value = false
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 初始化 **/
|
|
|
|
|
|
|
+const formatNumber = (value: unknown) => erpPriceTableColumnFormatter(null, null, value, null)
|
|
|
|
|
+
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
|
|
+ loadOrgData()
|
|
|
getList()
|
|
getList()
|
|
|
- // 添加窗口大小变化监听
|
|
|
|
|
- window.addEventListener('resize', calculateColumnWidths)
|
|
|
|
|
})
|
|
})
|
|
|
|
|
+</script>
|
|
|
|
|
|
|
|
-onUnmounted(() => {
|
|
|
|
|
- // 移除窗口大小变化监听
|
|
|
|
|
- window.removeEventListener('resize', calculateColumnWidths)
|
|
|
|
|
-})
|
|
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="iot-lock-stock-page grid grid-rows-[auto_auto_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-form
|
|
|
|
|
+ ref="queryFormRef"
|
|
|
|
|
+ :model="queryParams"
|
|
|
|
|
+ size="default"
|
|
|
|
|
+ label-width="82px"
|
|
|
|
|
+ class="iot-lock-stock-query bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-6 py-3 min-w-0"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="query-row">
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ v-if="!shouldHideComponents"
|
|
|
|
|
+ :label="t('workOrderMaterial.factory')"
|
|
|
|
|
+ prop="factoryId"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="queryParams.factoryId"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ filterable
|
|
|
|
|
+ :placeholder="t('faultForm.choose')"
|
|
|
|
|
+ class="query-control"
|
|
|
|
|
+ @change="selectedFactoryChange"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in factoryList"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ :label="item.factoryName"
|
|
|
|
|
+ :value="item.id!"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ v-if="!shouldHideComponents"
|
|
|
|
|
+ :label="t('workOrderMaterial.costCenter')"
|
|
|
|
|
+ prop="costCenterId"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="queryParams.costCenterId"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ filterable
|
|
|
|
|
+ :placeholder="t('faultForm.choose')"
|
|
|
|
|
+ class="query-control"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in filteredCostCenterList"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ :label="item.costCenterName"
|
|
|
|
|
+ :value="item.id!"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item :label="t('chooseMaintain.materialCode')" prop="materialCode">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.materialCode"
|
|
|
|
|
+ :placeholder="t('chooseMaintain.materialCode')"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ class="query-control"
|
|
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item :label="t('chooseMaintain.materialName')" prop="materialName">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.materialName"
|
|
|
|
|
+ :placeholder="t('chooseMaintain.materialName')"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ class="query-control"
|
|
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item :label="t('chooseMaintain.createTime')" prop="storageTime">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="queryParams.storageTime"
|
|
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
+ type="daterange"
|
|
|
|
|
+ :start-placeholder="t('info.start')"
|
|
|
|
|
+ :end-placeholder="t('info.end')"
|
|
|
|
|
+ :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
|
|
|
|
+ class="query-control query-control--date"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
-// 监听列表数据变化,重新计算列宽
|
|
|
|
|
-watch(
|
|
|
|
|
- list,
|
|
|
|
|
- () => {
|
|
|
|
|
- nextTick(calculateColumnWidths)
|
|
|
|
|
- },
|
|
|
|
|
- { deep: true }
|
|
|
|
|
-)
|
|
|
|
|
-</script>
|
|
|
|
|
|
|
+ <el-form-item class="query-actions">
|
|
|
|
|
+ <el-button type="primary" @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
|
|
|
|
|
+ @click="openForm"
|
|
|
|
|
+ v-hasPermi="['pms:iot-lock-stock:create']"
|
|
|
|
|
+ >
|
|
|
|
|
+ <Icon icon="ep:plus" class="mr-5px" />{{ t('operationFill.add') }}
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button type="success" plain :loading="exportLoading" @click="handleExport">
|
|
|
|
|
+ <Icon icon="ep:download" class="mr-5px" />导出
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="stock-stat-grid">
|
|
|
|
|
+ <div v-for="item in stockStatCards" :key="item.label" class="stock-stat-card group">
|
|
|
|
|
+ <div class="relative z-10 flex h-full flex-col justify-center">
|
|
|
|
|
+ <span class="stat-label">{{ item.label }}</span>
|
|
|
|
|
+ <div class="flex items-baseline gap-1">
|
|
|
|
|
+ <span v-if="item.prefix" class="text-[12px] text-gray-400">{{ item.prefix }}</span>
|
|
|
|
|
+ <count-to
|
|
|
|
|
+ class="stat-value"
|
|
|
|
|
+ :start-val="0"
|
|
|
|
|
+ :end-val="item.value"
|
|
|
|
|
+ :decimals="item.decimals"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span v-if="item.unit" class="stat-unit">{{ item.unit }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="stat-icon">
|
|
|
|
|
+ <div :class="item.icon" class="text-5xl"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg flex flex-col p-4 min-w-0 min-h-0">
|
|
|
|
|
+ <div class="flex-1 relative min-h-0">
|
|
|
|
|
+ <el-auto-resizer class="absolute">
|
|
|
|
|
+ <template #default="{ width, height }">
|
|
|
|
|
+ <ZmTable
|
|
|
|
|
+ :data="list"
|
|
|
|
|
+ :loading="loading"
|
|
|
|
|
+ :width="width"
|
|
|
|
|
+ :height="height"
|
|
|
|
|
+ :max-height="height"
|
|
|
|
|
+ show-border
|
|
|
|
|
+ >
|
|
|
|
|
+ <ZmTableColumn
|
|
|
|
|
+ type="index"
|
|
|
|
|
+ :label="t('monitor.serial')"
|
|
|
|
|
+ :width="70"
|
|
|
|
|
+ fixed="left"
|
|
|
|
|
+ hide-in-column-settings
|
|
|
|
|
+ />
|
|
|
|
|
+ <ZmTableColumn
|
|
|
|
|
+ prop="factory"
|
|
|
|
|
+ :label="t('workOrderMaterial.factory')"
|
|
|
|
|
+ min-width="120"
|
|
|
|
|
+ />
|
|
|
|
|
+ <ZmTableColumn
|
|
|
|
|
+ prop="costCenter"
|
|
|
|
|
+ :label="t('workOrderMaterial.costCenter')"
|
|
|
|
|
+ min-width="140"
|
|
|
|
|
+ />
|
|
|
|
|
+ <ZmTableColumn
|
|
|
|
|
+ prop="materialCode"
|
|
|
|
|
+ :label="t('chooseMaintain.materialCode')"
|
|
|
|
|
+ min-width="150"
|
|
|
|
|
+ />
|
|
|
|
|
+ <ZmTableColumn
|
|
|
|
|
+ prop="materialName"
|
|
|
|
|
+ :label="t('chooseMaintain.materialName')"
|
|
|
|
|
+ min-width="220"
|
|
|
|
|
+ align="left"
|
|
|
|
|
+ />
|
|
|
|
|
+ <ZmTableColumn prop="quantity" :label="t('route.quantity')" min-width="100">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ {{ formatNumber(row.quantity) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </ZmTableColumn>
|
|
|
|
|
+ <ZmTableColumn
|
|
|
|
|
+ prop="unitPrice"
|
|
|
|
|
+ :label="t('workOrderMaterial.unitPrice')"
|
|
|
|
|
+ min-width="100"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ {{ formatNumber(row.unitPrice) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </ZmTableColumn>
|
|
|
|
|
+ <ZmTableColumn prop="unit" :label="t('workOrderMaterial.unit')" min-width="90" />
|
|
|
|
|
+ <ZmTableColumn prop="storageTime" :label="t('stock.storageTime')" min-width="180">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ {{ dateFormatter(row, null, row.storageTime) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </ZmTableColumn>
|
|
|
|
|
+ </ZmTable>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-auto-resizer>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="h-8 mt-2 flex items-center justify-end">
|
|
|
|
|
+ <el-pagination
|
|
|
|
|
+ v-show="total > 0"
|
|
|
|
|
+ size="default"
|
|
|
|
|
+ :current-page="queryParams.pageNo"
|
|
|
|
|
+ :page-size="queryParams.pageSize"
|
|
|
|
|
+ :background="true"
|
|
|
|
|
+ :page-sizes="[10, 20, 30, 50, 100]"
|
|
|
|
|
+ :total="total"
|
|
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
|
|
+ @size-change="handleSizeChange"
|
|
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
|
-/* 统计卡片样式 */
|
|
|
|
|
-.stat-card {
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- border: 1px solid #ebeef5;
|
|
|
|
|
|
|
+.iot-lock-stock-query {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 12px 24px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.stat-container {
|
|
|
|
|
|
|
+.query-row {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- padding: 1px;
|
|
|
|
|
|
|
+ flex: 1 1 auto;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12px 22px;
|
|
|
|
|
+ min-width: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.stat-item {
|
|
|
|
|
|
|
+.query-actions {
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.query-actions :deep(.el-form-item__content) {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
|
|
- margin-right: 40px; /* 控制项间距 */
|
|
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 8px 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.query-actions :deep(.el-button) {
|
|
|
|
|
+ margin-left: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.query-control {
|
|
|
|
|
+ width: 190px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.query-control--date {
|
|
|
|
|
+ width: 220px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-label {
|
|
.stat-label {
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: #606266;
|
|
|
|
|
- margin-right: 8px;
|
|
|
|
|
|
|
+ margin-bottom: 2px;
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
|
|
+ letter-spacing: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-value {
|
|
.stat-value {
|
|
|
|
|
+ font-family: var(--el-font-family);
|
|
|
font-size: 18px;
|
|
font-size: 18px;
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: #409eff;
|
|
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-unit {
|
|
|
|
|
+ font-size: 10px;
|
|
|
|
|
+ font-weight: 400;
|
|
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stock-stat-grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stock-stat-card {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ min-height: 78px;
|
|
|
|
|
+ padding: 10px 14px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background-color: var(--el-bg-color);
|
|
|
|
|
+ border: 1px solid var(--el-border-color-extra-light);
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ box-shadow: var(--el-box-shadow-lighter);
|
|
|
|
|
+ transition:
|
|
|
|
|
+ border-color 0.3s,
|
|
|
|
|
+ box-shadow 0.3s;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stock-stat-card:hover {
|
|
|
|
|
+ border-color: var(--el-color-primary-light-7);
|
|
|
|
|
+ box-shadow: var(--el-box-shadow-light);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 表格容器样式 - 确保可以水平滚动 */
|
|
|
|
|
-.table-container {
|
|
|
|
|
- overflow-x: auto;
|
|
|
|
|
|
|
+.stat-icon {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ right: -8px;
|
|
|
|
|
+ bottom: -12px;
|
|
|
|
|
+ opacity: 0.4;
|
|
|
|
|
+ transform: rotate(-10deg);
|
|
|
|
|
+ transition:
|
|
|
|
|
+ transform 0.5s,
|
|
|
|
|
+ opacity 0.5s;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 防止表格内容换行 */
|
|
|
|
|
-:deep(.el-table) .cell {
|
|
|
|
|
- white-space: nowrap !important;
|
|
|
|
|
- overflow: visible !important;
|
|
|
|
|
- text-overflow: unset !important;
|
|
|
|
|
|
|
+.stock-stat-card:hover .stat-icon {
|
|
|
|
|
+ opacity: 0.55;
|
|
|
|
|
+ transform: scale(1.08) rotate(0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 确保表格行不换行 */
|
|
|
|
|
-:deep(.el-table__row) {
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
|
|
+:deep(.el-form-item) {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 防止表格内容换行 */
|
|
|
|
|
-:deep(.el-table .cell) {
|
|
|
|
|
- white-space: nowrap !important;
|
|
|
|
|
|
|
+@media (width >= 2400px) {
|
|
|
|
|
+ .iot-lock-stock-query,
|
|
|
|
|
+ .query-row {
|
|
|
|
|
+ flex-wrap: nowrap;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 表头特别处理 */
|
|
|
|
|
-:deep(.el-table__header) {
|
|
|
|
|
- .cell {
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- width: auto !important;
|
|
|
|
|
|
|
+@media (width <= 1500px) {
|
|
|
|
|
+ .iot-lock-stock-query,
|
|
|
|
|
+ .query-row {
|
|
|
|
|
+ gap: 12px 18px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .query-control {
|
|
|
|
|
+ width: 176px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .query-control--date {
|
|
|
|
|
+ width: 210px;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 表格整体布局优化 */
|
|
|
|
|
-:deep(.el-table__inner-wrapper) {
|
|
|
|
|
- min-width: 100% !important;
|
|
|
|
|
- width: auto !important;
|
|
|
|
|
|
|
+@media (width <= 1200px) {
|
|
|
|
|
+ .iot-lock-stock-page {
|
|
|
|
|
+ grid-template-rows: auto auto minmax(480px, 1fr);
|
|
|
|
|
+ height: auto;
|
|
|
|
|
+ min-height: calc(
|
|
|
|
|
+ 100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .query-actions {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 单元格内容完全显示 */
|
|
|
|
|
-:deep(.el-table__body-wrapper) .el-table__cell .cell {
|
|
|
|
|
- display: block;
|
|
|
|
|
- overflow: visible;
|
|
|
|
|
- text-overflow: unset;
|
|
|
|
|
|
|
+@media (width <= 768px) {
|
|
|
|
|
+ .iot-lock-stock-query {
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .query-row,
|
|
|
|
|
+ .query-row :deep(.el-form-item),
|
|
|
|
|
+ .query-actions {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .query-control,
|
|
|
|
|
+ .query-control--date {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .stock-stat-grid {
|
|
|
|
|
+ grid-template-columns: minmax(0, 1fr);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .query-actions :deep(.el-form-item__content) {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .query-actions :deep(.el-button) {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ margin-left: 0;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|