|
|
@@ -1,11 +1,343 @@
|
|
|
<template>
|
|
|
- <div>this is page inventory check</div>
|
|
|
+ <div class="safety-platform-container">
|
|
|
+ <div class="safety-platform-container__header">
|
|
|
+ <div class="breadcrumb-title">物资盘点</div>
|
|
|
+ </div>
|
|
|
+ <div class="safety-platform-container__main">
|
|
|
+ <div class="search-table-container">
|
|
|
+ <header class="disaster-precaution__header">
|
|
|
+ <BasicSearch
|
|
|
+ :searchConfig="INVENTORY_LIST_SEARCH_CONFIG"
|
|
|
+ :searchData="searchData"
|
|
|
+ @update:searchData="handleSearch"
|
|
|
+ >
|
|
|
+ <template #taskId>
|
|
|
+ <el-select v-model="searchData.taskId" placeholder="请选择盘点任务" filterable @change="handleTaskChange">
|
|
|
+ <el-option
|
|
|
+ v-for="item in inventoryTaskOptions"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.taskName"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ <template #emergencyType>
|
|
|
+ <el-select v-model="searchData.emergencyType" placeholder="请选择应急事件类型" filterable>
|
|
|
+ <el-option
|
|
|
+ v-for="item in emergencyEventDice"
|
|
|
+ :key="item.itemCode"
|
|
|
+ :label="item.itemValue"
|
|
|
+ :value="item.itemCode"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ <template #supplyType>
|
|
|
+ <el-select v-model="searchData.supplyType" placeholder="请选择物资类型" filterable>
|
|
|
+ <el-option
|
|
|
+ v-for="item in emergencySupplyDice"
|
|
|
+ :key="item.itemCode"
|
|
|
+ :label="item.itemValue"
|
|
|
+ :value="item.itemCode"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </BasicSearch>
|
|
|
+ <div class="date-time-container" v-if="endTime">
|
|
|
+ <span>任务期限:{{ endTime }}</span>
|
|
|
+ <el-button type="primary" :disabled="isTaskExpired" @click="handleConfirm" v-if="isInventory"
|
|
|
+ >盘点确认</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </header>
|
|
|
+ <BasicTable :tableData="tableData" :tableConfig="tableConfig">
|
|
|
+ <template #emergencyType="scope">
|
|
|
+ <span>{{ getEmergencyEvent(scope.row.emergencyType) }}</span>
|
|
|
+ </template>
|
|
|
+ <template #supplyType="scope">
|
|
|
+ <span>{{ getEmergencySupply(scope.row.supplyType) }}</span>
|
|
|
+ </template>
|
|
|
+ <template #afterKeeperName="scope">
|
|
|
+ <span :class="{ 'font-red': scope.row.afterKeeperName !== scope.row.beforeKeeperName }">
|
|
|
+ {{ scope.row.afterKeeperName }}
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ <template #afterQuantity="scope">
|
|
|
+ <span :class="{ 'font-red': scope.row.afterQuantity !== scope.row.beforeQuantity }">
|
|
|
+ {{ scope.row.afterQuantity }}
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ <template #supplementQuantity="scope">
|
|
|
+ <span v-if="scope.row.supplementQuantity > 0">
|
|
|
+ <span class="font-red">{{ scope.row.supplementQuantity }}</span>
|
|
|
+ </span>
|
|
|
+ <span v-else>
|
|
|
+ <span>0</span>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ <template #park="scope">
|
|
|
+ <span>{{ getPark(scope.row.park) }}</span>
|
|
|
+ </template>
|
|
|
+ <template #beforeLocation="scope">
|
|
|
+ <span>{{ getLocation(scope.row.beforeLocation) }}</span>
|
|
|
+ </template>
|
|
|
+ <template #afterLocation="scope">
|
|
|
+ <span :class="{ 'font-red': scope.row.afterLocation !== scope.row.beforeLocation }">
|
|
|
+ {{ getLocation(scope.row.afterLocation) }}
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ <template #inventoryResult="scope">
|
|
|
+ <span>{{ INVENTORY_RESULT_MAP[scope.row.inventoryResult] }}</span>
|
|
|
+ </template>
|
|
|
+ <template #remark="scope">
|
|
|
+ <div class="remark--div">
|
|
|
+ <el-tooltip
|
|
|
+ :content="scope.row.remark"
|
|
|
+ placement="top"
|
|
|
+ effect="light"
|
|
|
+ :popper-class="['custom-tooltip', scope.row.remark && scope.row.remark.length > 10 ? '' : 'custom-tooltip--opacity0']"
|
|
|
+ >
|
|
|
+ <span>{{ scope.row.remark }}</span>
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #imageList="scope">
|
|
|
+ <div class="image-list" v-if="scope.row.imageList && JSON.parse(scope.row.imageList).length > 0">
|
|
|
+ <el-image
|
|
|
+ :src="PreviewIcon"
|
|
|
+ :zoom-rate="1.2"
|
|
|
+ :max-scale="7"
|
|
|
+ :min-scale="0.2"
|
|
|
+ :preview-src-list="JSON.parse(scope.row.imageList)"
|
|
|
+ :initial-index="0"
|
|
|
+ show-progress
|
|
|
+ preview-teleported
|
|
|
+ fit="cover"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </BasicTable>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
+ import { ref, reactive, onMounted, watch, computed, onBeforeUnmount } from 'vue';
|
|
|
+ import { ElMessage } from 'element-plus';
|
|
|
+ import BasicSearch from '@/components/BasicSearch.vue';
|
|
|
+ import BasicTable from '@/components/BasicTable.vue';
|
|
|
+ import {
|
|
|
+ INVENTORY_LIST_SEARCH_CONFIG,
|
|
|
+ INVENTORY_CHECK_TABLE_COLUMNS,
|
|
|
+ INVENTORY_CHECK_TABLE_OPTIONS,
|
|
|
+ INVENTORY_CHECK_TABLE_MAX_HEIGHT_PERMISSION,
|
|
|
+ INVENTORY_CHECK_TABLE_MAX_HEIGHT_DEFAULT,
|
|
|
+ } from './src/config';
|
|
|
+ import useTableConfig from '@/hooks/useTableConfigHook';
|
|
|
+ import { useEmergencySuppliesHook } from './src/hook';
|
|
|
+ import { openMessageBox } from '@/utils/element-plus/messageBox';
|
|
|
+ import type {
|
|
|
+ InventoryCheckListQuery,
|
|
|
+ InventoryCheckListResponse,
|
|
|
+ InventoryTaskListRes,
|
|
|
+ } from '@/types/emergency-supplier';
|
|
|
+ import type { QueryPageRequest } from '@/types/basic-query';
|
|
|
+ import {
|
|
|
+ getInventoryTaskList,
|
|
|
+ getInventoryCheckList,
|
|
|
+ confirmInventoryTask,
|
|
|
+ getInventoryTaskConfirm,
|
|
|
+ } from '@/api/emergency-supplier';
|
|
|
+ import { INVENTORY_RESULT_MAP } from './src/constant';
|
|
|
+ import PreviewIcon from './src/svg/img-preview.svg';
|
|
|
|
|
|
+ const {
|
|
|
+ emergencyEventDice,
|
|
|
+ getEmergencyEventDict,
|
|
|
+ getEmergencyEvent,
|
|
|
+ emergencySupplyDice,
|
|
|
+ getEmergencySupplyDict,
|
|
|
+ getEmergencySupply,
|
|
|
+ getParkDict,
|
|
|
+ getPark,
|
|
|
+ getLocationDict,
|
|
|
+ getLocation,
|
|
|
+ } = useEmergencySuppliesHook();
|
|
|
+ const { tableConfig, pagination } = useTableConfig(INVENTORY_CHECK_TABLE_COLUMNS, INVENTORY_CHECK_TABLE_OPTIONS);
|
|
|
+ let inventoryCheckListQuery: QueryPageRequest<InventoryCheckListQuery> = {
|
|
|
+ pageNumber: pagination.pageNumber,
|
|
|
+ pageSize: pagination.pageSize,
|
|
|
+ queryParam: {},
|
|
|
+ };
|
|
|
+ const tableData = ref<InventoryCheckListResponse[]>([]);
|
|
|
+ const inventoryTaskOptions = ref<InventoryTaskListRes[]>([]);
|
|
|
+ const endTime = ref<string>('');
|
|
|
+ const isInventory = ref<boolean>(false);
|
|
|
+ const searchData = reactive<InventoryCheckListQuery>({
|
|
|
+ taskId: null,
|
|
|
+ emergencyType: null,
|
|
|
+ supplyType: null,
|
|
|
+ supplyName: null,
|
|
|
+ keeperName: null,
|
|
|
+ inventoryResult: null,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 定时器引用
|
|
|
+ const pollTimer = ref<number | null>(null);
|
|
|
+
|
|
|
+ const isTaskExpired = computed(() => {
|
|
|
+ if (!endTime.value) return true;
|
|
|
+
|
|
|
+ const currentTime = new Date();
|
|
|
+ const taskEndTime = new Date(endTime.value);
|
|
|
+
|
|
|
+ return currentTime > taskEndTime;
|
|
|
+ });
|
|
|
+
|
|
|
+ const handleSearch = () => {
|
|
|
+ inventoryCheckListQuery.queryParam = {};
|
|
|
+ if (searchData.taskId) {
|
|
|
+ inventoryCheckListQuery.queryParam.taskId = searchData.taskId;
|
|
|
+ }
|
|
|
+ if (searchData.emergencyType) {
|
|
|
+ inventoryCheckListQuery.queryParam.emergencyType = searchData.emergencyType;
|
|
|
+ }
|
|
|
+ if (searchData.supplyType) {
|
|
|
+ inventoryCheckListQuery.queryParam.supplyType = searchData.supplyType;
|
|
|
+ }
|
|
|
+ if (searchData.supplyName) {
|
|
|
+ inventoryCheckListQuery.queryParam.supplyName = searchData.supplyName;
|
|
|
+ }
|
|
|
+ if (searchData.keeperName) {
|
|
|
+ inventoryCheckListQuery.queryParam.keeperName = searchData.keeperName;
|
|
|
+ }
|
|
|
+ if (searchData.inventoryResult) {
|
|
|
+ inventoryCheckListQuery.queryParam.inventoryResult = searchData.inventoryResult;
|
|
|
+ }
|
|
|
+ getTableData();
|
|
|
+ };
|
|
|
+ const getTableData = async () => {
|
|
|
+ tableConfig.loading = true;
|
|
|
+ const res = await getInventoryCheckList(inventoryCheckListQuery);
|
|
|
+ tableData.value = res.records;
|
|
|
+ pagination.total = res.totalRow;
|
|
|
+ tableConfig.loading = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ const getFirstTaskInfo = () => {
|
|
|
+ const firstTask = inventoryTaskOptions.value[0];
|
|
|
+ if (!firstTask) return;
|
|
|
+ searchData.taskId = firstTask.id;
|
|
|
+ endTime.value = firstTask.endTime;
|
|
|
+ };
|
|
|
+
|
|
|
+ const getInventoryTaskOptions = async () => {
|
|
|
+ inventoryTaskOptions.value = await getInventoryTaskList();
|
|
|
+ await getFirstTaskInfo();
|
|
|
+ handleSearch();
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleTaskChange = (id: number) => {
|
|
|
+ const task = inventoryTaskOptions.value.find((item) => item.id === id);
|
|
|
+ if (!task) return;
|
|
|
+ endTime.value = task.endTime;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 检查任务状态
|
|
|
+ const checkTaskStatus = async () => {
|
|
|
+ if (!searchData.taskId) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res = await getInventoryTaskConfirm(searchData.taskId);
|
|
|
+ isInventory.value = res.taskState === 2;
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error(`获取任务状态失败:${error}`);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 开始轮询
|
|
|
+ const startPolling = () => {
|
|
|
+ // 清除之前的轮询
|
|
|
+ if (pollTimer.value) {
|
|
|
+ clearInterval(pollTimer.value);
|
|
|
+ pollTimer.value = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果有任务ID,开始轮询
|
|
|
+ if (searchData.taskId) {
|
|
|
+ // 设置1分钟轮询一次
|
|
|
+ pollTimer.value = window.setInterval(checkTaskStatus, 30000);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleConfirm = async () => {
|
|
|
+ const confirmed = await openMessageBox('', '是否盘点该条任务?', 'warning');
|
|
|
+ if (!confirmed) return;
|
|
|
+ if (!searchData.taskId) return;
|
|
|
+ await confirmInventoryTask(searchData.taskId);
|
|
|
+ ElMessage.success('盘点成功');
|
|
|
+ };
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ getInventoryTaskOptions();
|
|
|
+ getEmergencyEventDict();
|
|
|
+ getEmergencySupplyDict();
|
|
|
+ getParkDict();
|
|
|
+ getLocationDict('all');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 组件销毁前清除定时器
|
|
|
+ onBeforeUnmount(() => {
|
|
|
+ if (pollTimer.value) {
|
|
|
+ clearInterval(pollTimer.value);
|
|
|
+ pollTimer.value = null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ watch(
|
|
|
+ () => searchData.taskId,
|
|
|
+ async (newId) => {
|
|
|
+ if (newId === null) {
|
|
|
+ getFirstTaskInfo();
|
|
|
+ } else {
|
|
|
+ // 任务ID变化时检查状态
|
|
|
+ await checkTaskStatus();
|
|
|
+ // 任务ID变化时重新启动轮询
|
|
|
+ startPolling();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ );
|
|
|
+ watch(
|
|
|
+ () => endTime.value,
|
|
|
+ () => {
|
|
|
+ tableConfig.maxHeight = endTime.value
|
|
|
+ ? INVENTORY_CHECK_TABLE_MAX_HEIGHT_PERMISSION
|
|
|
+ : INVENTORY_CHECK_TABLE_MAX_HEIGHT_DEFAULT;
|
|
|
+ },
|
|
|
+ );
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
-
|
|
|
-</style>
|
|
|
+ @use '@/styles/page-details-layout.scss' as *;
|
|
|
+ @use '@/styles/page-main-layout.scss' as *;
|
|
|
+ @use './src/styles/page-common.scss' as *;
|
|
|
+ .date-time-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 20px;
|
|
|
+ margin-top: 20px;
|
|
|
+ span {
|
|
|
+ font-size: 14px;
|
|
|
+ color: $primary-color;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .font-red {
|
|
|
+ color: red;
|
|
|
+ }
|
|
|
+ .image-list {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+</style>
|