|
|
<template>
|
|
|
<div>
|
|
|
<!-- 盘点计划 Section -->
|
|
|
<div class="content-wrapper">
|
|
|
<div class="page-header">
|
|
|
<h1 class="page-title">盘点计划</h1>
|
|
|
<Button type="primary" @click="showPlanModal('new')">新建计划</Button>
|
|
|
</div>
|
|
|
|
|
|
<!-- Plan Search Filters -->
|
|
|
<div class="search-filters">
|
|
|
<div class="filter-item">
|
|
|
<span class="selector-item__label">关键词:</span>
|
|
|
<Input v-model="planSearch.keyword" style="width: 180px" :clearable="true" placeholder="计划名称/编号" />
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
<span class="selector-item__label">计划日期:</span>
|
|
|
<DatePicker v-model="planSearch.dateRange" type="daterange" split-panels style="width: 200px" :clearable="true"></DatePicker>
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
<span class="selector-item__label">状态:</span>
|
|
|
<Select v-model="planSearch.status" style="width: 120px" :clearable="true">
|
|
|
<Option value="0">未开始</Option>
|
|
|
<Option value="1">进行中</Option>
|
|
|
<Option value="2">已完成</Option>
|
|
|
</Select>
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
<Button type="primary" @click="searchPlans">查询</Button>
|
|
|
<Button style="margin-left: 10px" @click="resetPlanSearch">重置</Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- Plan Table -->
|
|
|
<Table :columns="planColumns" :data="planList" :loading="planLoading">
|
|
|
<template slot-scope="{ row }" slot="status">
|
|
|
<Tag :color="getStatusColor(row.status)">{{ getStatusText(row.status) }}</Tag>
|
|
|
</template>
|
|
|
<template slot-scope="{ row }" slot="action">
|
|
|
<div style="display: flex; gap: 8px; justify-content: center;">
|
|
|
<Button type="primary" size="small" style="border-radius: 6px;" @click="showInventorySelectModal(row.id)">选择对象</Button>
|
|
|
<Button type="primary" size="small" style="border-radius: 6px;" ghost @click="showPlanModal('edit', row)">编辑</Button>
|
|
|
<Button type="error" size="small" style="border-radius: 6px;" ghost @click="deletePlan(row.id)">删除</Button>
|
|
|
</div>
|
|
|
</template>
|
|
|
</Table>
|
|
|
|
|
|
<!-- Plan Pagination -->
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
@size-change="handlePlanPageSizeChange"
|
|
|
@current-change="handlePlanPageChange"
|
|
|
:current-page="planSearch.pageIndex"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
:page-size="planSearch.pageSize"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="planTotal"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 盘点列表 Section -->
|
|
|
<div class="content-wrapper" style="margin-top: 20px">
|
|
|
<div class="page-header">
|
|
|
<h1 class="page-title">盘点列表</h1>
|
|
|
</div>
|
|
|
|
|
|
<!-- List Search Filters -->
|
|
|
<div class="search-filters">
|
|
|
<div class="filter-item">
|
|
|
<span class="selector-item__label">关键词:</span>
|
|
|
<Input v-model="listSearch.keyword" :clearable="true" style="width: 180px" placeholder="单号/计划/区域/执行人" />
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
<span class="selector-item__label">所属计划:</span>
|
|
|
<Select v-model="listSearch.planId" style="width: 180px" :clearable="true">
|
|
|
<Option v-for="plan in planList" :key="plan.id" :value="plan.id">{{ plan.name }}</Option>
|
|
|
</Select>
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
<span class="selector-item__label">状态:</span>
|
|
|
<Select v-model="listSearch.status" style="width: 120px" :clearable="true">
|
|
|
<Option value="0">未盘点</Option>
|
|
|
<Option value="1">已盘点</Option>
|
|
|
</Select>
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
<Button type="primary" @click="searchList">查询</Button>
|
|
|
<Button style="margin-left: 10px" @click="resetListSearch">重置</Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- Inventory List Table -->
|
|
|
<Table :columns="listColumns" :data="inventoryList" :loading="listLoading">
|
|
|
<template slot-scope="{ row }" slot="planName">
|
|
|
{{ row.material_infos_plan ? row.material_infos_plan.name : '-' }}
|
|
|
</template>
|
|
|
<template slot-scope="{ row }" slot="area">
|
|
|
{{ row.material_info ? row.material_info.suozaicangku : '-' }}
|
|
|
</template>
|
|
|
<template slot-scope="{ row }" slot="executor">
|
|
|
{{ row.responsible_admin ? row.responsible_admin.name : '-' }}
|
|
|
</template>
|
|
|
<template slot-scope="{ row }" slot="startTime">
|
|
|
{{ row.material_infos_plan ? row.material_infos_plan.start_date : '-' }}
|
|
|
</template>
|
|
|
<template slot-scope="{ row }" slot="endTime">
|
|
|
{{ row.material_infos_plan ? row.material_infos_plan.end_date : '-' }}
|
|
|
</template>
|
|
|
<template slot-scope="{ row }" slot="status">
|
|
|
<Tag :color="getInventoryStatusColor(row.status)">{{ getInventoryStatusText(row.status) }}</Tag>
|
|
|
</template>
|
|
|
<template slot-scope="{ row }" slot="action">
|
|
|
<div style="display: flex; gap: 8px; justify-content: center;">
|
|
|
<Button type="primary" size="small" style="border-radius: 6px;" @click="viewInventoryDetail(row)">查看</Button>
|
|
|
</div>
|
|
|
</template>
|
|
|
</Table>
|
|
|
|
|
|
<!-- List Pagination -->
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
@size-change="handleListPageSizeChange"
|
|
|
@current-change="handleListPageChange"
|
|
|
:current-page="listSearch.pageIndex"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
:page-size="listSearch.pageSize"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="listTotal"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- Plan Create/Edit Modal -->
|
|
|
<Modal v-model="planModal.visible" :title="planModal.title">
|
|
|
<Form ref="planForm" :model="planModal.form" :rules="planModal.rules" width="80">
|
|
|
<FormItem label="计划名称" prop="name">
|
|
|
<Input v-model="planModal.form.name" placeholder="请输入计划名称" :clearable="true"/>
|
|
|
</FormItem>
|
|
|
<FormItem label="计划日期" prop="dateRange">
|
|
|
<DatePicker
|
|
|
v-model="planModal.form.dateRange"
|
|
|
type="daterange"
|
|
|
split-panels
|
|
|
style="width: 100%"
|
|
|
@on-clear="handleDateClear"
|
|
|
:clearable="true"
|
|
|
:editable="false"
|
|
|
/>
|
|
|
</FormItem>
|
|
|
<FormItem label="备注" prop="remark">
|
|
|
<Input v-model="planModal.form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" />
|
|
|
</FormItem>
|
|
|
</Form>
|
|
|
<template slot="footer">
|
|
|
<Button @click="planModal.visible = false">取消</Button>
|
|
|
<Button type="primary" @click="handlePlanSubmit">确定</Button>
|
|
|
</template>
|
|
|
</Modal>
|
|
|
|
|
|
<!-- Inventory Selection Modal -->
|
|
|
<Modal v-model="materialModal.visible" title="选择盘点物资" width="900">
|
|
|
<div class="material-search">
|
|
|
<Input v-model="materialModal.keyword" placeholder="物资名称" style="width: 180px; margin-right: 10px;" />
|
|
|
<Select v-model="materialModal.storehouses_id" @on-change="onStorehouseTypeChange" clearable placeholder="仓库类型" style="width: 120px; margin-right: 10px;">
|
|
|
<Option v-for="item in storehouseTypeOptions" :key="item.value" :value="item.value">{{ item.label }}</Option>
|
|
|
</Select>
|
|
|
<Select v-model="materialModal.area" @on-change="onAreaChange" clearable placeholder="所在区域" style="width: 120px; margin-right: 10px;">
|
|
|
<Option v-for="item in areaOptions" :key="item.value" :value="item.value">{{ item.value }}</Option>
|
|
|
</Select>
|
|
|
<Select v-model="materialModal.warehouseName" clearable placeholder="仓库名称" style="width: 140px; margin-right: 10px;">
|
|
|
<Option v-for="item in warehouseNameOptions" :key="item.value" :value="item.value">{{ item.label }}</Option>
|
|
|
</Select>
|
|
|
<Button type="primary" style="margin-left: 10px" @click="searchMaterials">查询</Button>
|
|
|
<Button style="margin-left: 10px" @click="resetMaterialSearch">重置</Button>
|
|
|
</div>
|
|
|
<el-table
|
|
|
ref="materialTable"
|
|
|
:data="materialList"
|
|
|
v-loading="materialModal.loading"
|
|
|
@select="handleSelect"
|
|
|
@select-all="handleSelectAll"
|
|
|
row-key="id"
|
|
|
>
|
|
|
<el-table-column
|
|
|
type="selection"
|
|
|
width="55">
|
|
|
</el-table-column>
|
|
|
<el-table-column
|
|
|
prop="zichanmingcheng"
|
|
|
label="物资名称">
|
|
|
</el-table-column>
|
|
|
<el-table-column
|
|
|
prop="guigexinghao"
|
|
|
label="规格型号">
|
|
|
</el-table-column>
|
|
|
<el-table-column
|
|
|
prop="jiliangdanwei"
|
|
|
label="单位">
|
|
|
</el-table-column>
|
|
|
<el-table-column
|
|
|
prop="inventorys_total"
|
|
|
label="当前库存">
|
|
|
<template slot-scope="scope">
|
|
|
{{ scope.row.inventorys_total === null ? '0' : scope.row.inventorys_total }}
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column
|
|
|
label="操作"
|
|
|
width="100">
|
|
|
<template slot-scope="scope">
|
|
|
<div style="display: flex; gap: 8px; justify-content: center;">
|
|
|
<el-button
|
|
|
:type="isSelected(scope.row) ? 'warning' : 'success'"
|
|
|
size="small"
|
|
|
style="border-radius: 6px;"
|
|
|
@click="toggleMaterialSelection(scope.row, scope.$index)"
|
|
|
>
|
|
|
{{ isSelected(scope.row) ? '移出计划' : '加入计划' }}
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
@size-change="handleMaterialPageSizeChange"
|
|
|
@current-change="handleMaterialPageChange"
|
|
|
:current-page="materialModal.pageIndex"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
:page-size="materialModal.pageSize"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="materialModal.total"
|
|
|
/>
|
|
|
</div>
|
|
|
<template slot="footer">
|
|
|
<Button @click="materialModal.visible = false">取消</Button>
|
|
|
<Button type="primary" @click="handleMaterialSubmit">确定</Button>
|
|
|
</template>
|
|
|
</Modal>
|
|
|
|
|
|
<!-- 盘点任务详情弹窗 -->
|
|
|
<Modal
|
|
|
v-model="detailModal.visible"
|
|
|
title="盘点任务详情"
|
|
|
width="1000"
|
|
|
:mask-closable="true"
|
|
|
>
|
|
|
<div class="detail-content" v-if="detailModal.data">
|
|
|
<div class="detail-header">
|
|
|
<div class="detail-item">
|
|
|
<span class="label">盘点单号:</span>
|
|
|
<span class="value">{{ detailModal.data.no }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">所属计划:</span>
|
|
|
<span class="value">{{ detailModal.data.planName }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">盘点区域:</span>
|
|
|
<span class="value">{{ detailModal.data.area }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">执行人:</span>
|
|
|
<span class="value">{{ detailModal.data.executor }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">开始时间:</span>
|
|
|
<span class="value">{{ detailModal.data.startTime }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">完成时间:</span>
|
|
|
<span class="value">{{ detailModal.data.endTime || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">状态:</span>
|
|
|
<span class="value">
|
|
|
<Tag :color="getInventoryStatusColor(detailModal.data.status)">
|
|
|
{{ getInventoryStatusText(detailModal.data.status) }}
|
|
|
</Tag>
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="material-info-section">
|
|
|
<h3>物资基本信息</h3>
|
|
|
<div class="material-info-content">
|
|
|
<div class="info-item">
|
|
|
<span class="label">物资名称:</span>
|
|
|
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.zichanmingcheng || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="label">物资代码:</span>
|
|
|
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.wuzibianma || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="label">规格型号:</span>
|
|
|
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.guigexinghao || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="label">计量单位:</span>
|
|
|
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.jiliangdanwei || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="label">当前库存:</span>
|
|
|
<span class="value">{{ detailModal.data.material_info && detailModal.data.material_info.inventorys_total || '0' }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="history-section">
|
|
|
<h3>盘点历史记录</h3>
|
|
|
<Table
|
|
|
:columns="detailColumns"
|
|
|
:data="detailModal.data.history || []"
|
|
|
:loading="detailModal.loading"
|
|
|
border
|
|
|
>
|
|
|
<template slot-scope="{ row }" slot="status">
|
|
|
<Tag :color="getInventoryStatusColor(row.status)">{{ getInventoryStatusText(row.status) }}</Tag>
|
|
|
</template>
|
|
|
<template slot-scope="{ row }" slot="photos">
|
|
|
<div class="photo-list" v-if="row.files && row.files.length">
|
|
|
<div class="photo-item" v-for="(file, index) in row.files.slice(0, 3)" :key="index">
|
|
|
<img :src="file.url" @click="previewImage(file.url)" />
|
|
|
</div>
|
|
|
<div class="photo-more" v-if="row.files.length > 3">
|
|
|
+{{ row.files.length - 3 }}
|
|
|
</div>
|
|
|
</div>
|
|
|
<span v-else>-</span>
|
|
|
</template>
|
|
|
</Table>
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
@size-change="handleDetailPageSizeChange"
|
|
|
@current-change="handleDetailPageChange"
|
|
|
:current-page="detailModal.pageIndex"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
:page-size="detailModal.pageSize"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="detailModal.total"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<template slot="footer">
|
|
|
<Button @click="detailModal.visible = false">关闭</Button>
|
|
|
</template>
|
|
|
</Modal>
|
|
|
|
|
|
<!-- 预览弹窗 -->
|
|
|
<div v-if="previewUrl" class="image-preview-modal" @click="closePreview">
|
|
|
<img :src="previewUrl" class="image-preview-large" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import { saveStocktakingPlan, getStocktakingPlanList, deleteStocktakingPlan, getStocktakingPlanLinkList, getStocktakingPlanLinkDetail, getStocktakingHistoryList } from '@/api/system/stocktaking'
|
|
|
import { getStorehouseTypeList } from '@/api/system/storehouseType'
|
|
|
import { getparameteritem } from '@/api/system/dictionary'
|
|
|
import { index as getWarehouseList } from '@/api/system/baseForm'
|
|
|
import qs from 'qs'
|
|
|
import request from '@/utils/request'
|
|
|
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
planSearch: {
|
|
|
keyword: '',
|
|
|
dateRange: [],
|
|
|
status: '',
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10
|
|
|
},
|
|
|
planList: [],
|
|
|
planTotal: 0,
|
|
|
planLoading: false,
|
|
|
planColumns: [
|
|
|
{ title: '计划编号', key: 'no' },
|
|
|
{ title: '计划名称', key: 'name' },
|
|
|
{ title: '开始日期', key: 'start_date' },
|
|
|
{ title: '结束日期', key: 'end_date' },
|
|
|
{
|
|
|
title: '状态',
|
|
|
slot: 'status',
|
|
|
key: 'status'
|
|
|
},
|
|
|
{
|
|
|
title: '操作',
|
|
|
slot: 'action',
|
|
|
width: 200
|
|
|
}
|
|
|
],
|
|
|
|
|
|
listSearch: {
|
|
|
keyword: '',
|
|
|
planId: '',
|
|
|
status: '',
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10
|
|
|
},
|
|
|
inventoryList: [],
|
|
|
listTotal: 0,
|
|
|
listLoading: false,
|
|
|
listColumns: [
|
|
|
{ title: '盘点单号', key: 'no' },
|
|
|
{
|
|
|
title: '所属计划',
|
|
|
slot: 'planName',
|
|
|
key: 'material_infos_plan.name'
|
|
|
},
|
|
|
{
|
|
|
title: '盘点区域',
|
|
|
slot: 'area',
|
|
|
key: 'material_info.suozaicangku'
|
|
|
},
|
|
|
{
|
|
|
title: '执行人',
|
|
|
slot: 'executor',
|
|
|
key: 'responsible_admin.name'
|
|
|
},
|
|
|
{
|
|
|
title: '开始时间',
|
|
|
slot: 'startTime',
|
|
|
key: 'material_infos_plan.start_date'
|
|
|
},
|
|
|
{
|
|
|
title: '完成时间',
|
|
|
slot: 'endTime',
|
|
|
key: 'material_infos_plan.end_date'
|
|
|
},
|
|
|
{
|
|
|
title: '状态',
|
|
|
slot: 'status',
|
|
|
key: 'status'
|
|
|
},
|
|
|
{
|
|
|
title: '操作',
|
|
|
slot: 'action',
|
|
|
width: 100
|
|
|
}
|
|
|
],
|
|
|
|
|
|
planModal: {
|
|
|
visible: false,
|
|
|
title: '新建盘点计划',
|
|
|
form: {
|
|
|
id: '',
|
|
|
name: '',
|
|
|
dateRange: [],
|
|
|
remark: ''
|
|
|
},
|
|
|
rules: {
|
|
|
name: [{ required: true, message: '请输入计划名称', trigger: 'blur' }],
|
|
|
dateRange: [
|
|
|
{
|
|
|
required: true,
|
|
|
type: 'array',
|
|
|
min: 2,
|
|
|
message: '请选择计划日期',
|
|
|
trigger: 'change',
|
|
|
validator: (rule, value, callback) => {
|
|
|
if (!value || value.length !== 2) {
|
|
|
callback(new Error('请选择计划日期'));
|
|
|
} else {
|
|
|
callback();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
},
|
|
|
|
|
|
materialModal: {
|
|
|
visible: false,
|
|
|
loading: false,
|
|
|
keyword: '',
|
|
|
storehouses_id: '',
|
|
|
area: '',
|
|
|
warehouseName: '',
|
|
|
currentPlanId: '',
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10,
|
|
|
total: 0,
|
|
|
selectedMaterialIds: new Set(),
|
|
|
isInitialLoad: true
|
|
|
},
|
|
|
materialList: [],
|
|
|
materialColumns: [
|
|
|
{ type: 'selection', width: 60 },
|
|
|
{ title: '物资名称', key: 'zichanmingcheng' },
|
|
|
{ title: '规格型号', key: 'guigexinghao' },
|
|
|
{ title: '单位', key: 'jiliangdanwei' },
|
|
|
{
|
|
|
title: '当前库存',
|
|
|
key: 'inventorys_total',
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventorys_total === null ? '0' : params.row.inventorys_total)
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '操作',
|
|
|
slot: 'action',
|
|
|
width: 100
|
|
|
}
|
|
|
],
|
|
|
planMaterials: {
|
|
|
'P202504001': ['M002', 'M004'],
|
|
|
'P202505001': ['M001', 'M003', 'M006']
|
|
|
},
|
|
|
storehouseTypeOptions: [],
|
|
|
areaOptions: [],
|
|
|
warehouseNameOptions: [],
|
|
|
selectedRows: [],
|
|
|
detailModal: {
|
|
|
visible: false,
|
|
|
loading: false,
|
|
|
data: null,
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10,
|
|
|
total: 0
|
|
|
},
|
|
|
detailColumns: [
|
|
|
{
|
|
|
title: '盘点日期',
|
|
|
key: 'check_date',
|
|
|
width: 180,
|
|
|
align: 'center'
|
|
|
},
|
|
|
{
|
|
|
title: '盘点数量',
|
|
|
key: 'check_num',
|
|
|
width: 100,
|
|
|
align: 'center'
|
|
|
},
|
|
|
{
|
|
|
title: '状态',
|
|
|
slot: 'status',
|
|
|
key: 'status',
|
|
|
width: 100,
|
|
|
align: 'center'
|
|
|
},
|
|
|
{
|
|
|
title: '备注',
|
|
|
key: 'remark',
|
|
|
minWidth: 200,
|
|
|
tooltip: true,
|
|
|
align: 'center'
|
|
|
},
|
|
|
{
|
|
|
title: '照片',
|
|
|
slot: 'photos',
|
|
|
width: 200,
|
|
|
align: 'center'
|
|
|
}
|
|
|
],
|
|
|
previewUrl: ''
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
this.searchPlans()
|
|
|
this.searchList()
|
|
|
this.initMaterialSelectOptions()
|
|
|
},
|
|
|
methods: {
|
|
|
getStatusColor(status) {
|
|
|
const colors = {
|
|
|
'0': 'red',
|
|
|
'1': 'orange',
|
|
|
'2': 'green'
|
|
|
}
|
|
|
return colors[status] || 'default'
|
|
|
},
|
|
|
getStatusText(status) {
|
|
|
const texts = {
|
|
|
'0': '未开始',
|
|
|
'1': '进行中',
|
|
|
'2': '已完成'
|
|
|
}
|
|
|
return texts[status] || status
|
|
|
},
|
|
|
getInventoryStatusColor(status) {
|
|
|
const colors = {
|
|
|
'0': 'red',
|
|
|
'1': 'green'
|
|
|
}
|
|
|
return colors[status] || 'default'
|
|
|
},
|
|
|
getInventoryStatusText(status) {
|
|
|
const texts = {
|
|
|
'0': '未盘点',
|
|
|
'1': '已盘点'
|
|
|
}
|
|
|
return texts[status] || status
|
|
|
},
|
|
|
|
|
|
async searchPlans() {
|
|
|
this.planLoading = true;
|
|
|
try {
|
|
|
const formData = new FormData();
|
|
|
formData.append('page', this.planSearch.pageIndex);
|
|
|
formData.append('page_size', this.planSearch.pageSize);
|
|
|
|
|
|
// 只添加非空的搜索条件
|
|
|
if (this.planSearch.keyword) {
|
|
|
formData.append('keyword', this.planSearch.keyword);
|
|
|
}
|
|
|
|
|
|
// 添加状态过滤
|
|
|
if (this.planSearch.status) {
|
|
|
const filterIndex = this.planSearch.keyword ? 1 : 0;
|
|
|
formData.append(`filter[${filterIndex}][key]`, 'status');
|
|
|
formData.append(`filter[${filterIndex}][op]`, 'eq');
|
|
|
formData.append(`filter[${filterIndex}][value]`, this.planSearch.status);
|
|
|
}
|
|
|
|
|
|
// 确保日期范围有效且不为空
|
|
|
if (this.planSearch.dateRange &&
|
|
|
this.planSearch.dateRange.length === 2 &&
|
|
|
this.planSearch.dateRange[0] &&
|
|
|
this.planSearch.dateRange[1]) {
|
|
|
const formatDate = (date) => {
|
|
|
const d = new Date(date);
|
|
|
if (isNaN(d.getTime())) return null; // 检查日期是否有效
|
|
|
const year = d.getFullYear();
|
|
|
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
|
const day = String(d.getDate()).padStart(2, '0');
|
|
|
return `${year}-${month}-${day}`;
|
|
|
};
|
|
|
|
|
|
const startDate = formatDate(this.planSearch.dateRange[0]);
|
|
|
const endDate = formatDate(this.planSearch.dateRange[1]);
|
|
|
|
|
|
// 只有当两个日期都有效时才添加过滤条件
|
|
|
if (startDate && endDate) {
|
|
|
const filterIndex = (this.planSearch.keyword ? 1 : 0) + (this.planSearch.status ? 1 : 0);
|
|
|
formData.append(`filter[${filterIndex}][key]`, 'start_date');
|
|
|
formData.append(`filter[${filterIndex}][op]`, 'range');
|
|
|
formData.append(`filter[${filterIndex}][value]`, startDate+','+endDate);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const res = await getStocktakingPlanList(formData);
|
|
|
if (res && res.list) {
|
|
|
this.planList = res.list.data;
|
|
|
this.planTotal = res.list.total;
|
|
|
}
|
|
|
} catch (error) {
|
|
|
this.$Message.error('获取盘点计划列表失败');
|
|
|
console.error('获取盘点计划列表失败:', error);
|
|
|
} finally {
|
|
|
this.planLoading = false;
|
|
|
}
|
|
|
},
|
|
|
resetPlanSearch() {
|
|
|
this.planSearch = {
|
|
|
keyword: '',
|
|
|
dateRange: [],
|
|
|
status: '',
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10
|
|
|
}
|
|
|
this.searchPlans()
|
|
|
},
|
|
|
handlePlanPageChange(page) {
|
|
|
this.planSearch.pageIndex = page
|
|
|
this.searchPlans()
|
|
|
},
|
|
|
handlePlanPageSizeChange(size) {
|
|
|
this.planSearch.pageSize = size
|
|
|
this.planSearch.pageIndex = 1
|
|
|
this.searchPlans()
|
|
|
},
|
|
|
showPlanModal(mode, plan) {
|
|
|
this.planModal.title = mode === 'new' ? '新建盘点计划' : '编辑盘点计划'
|
|
|
if (mode === 'edit' && plan) {
|
|
|
this.planModal.form = {
|
|
|
id: plan.id,
|
|
|
name: plan.name,
|
|
|
dateRange: [plan.start_date, plan.end_date],
|
|
|
remark: plan.remark
|
|
|
}
|
|
|
this.planModal.visible = true
|
|
|
} else {
|
|
|
this.planModal.form = {
|
|
|
id: '',
|
|
|
name: '',
|
|
|
dateRange: [],
|
|
|
remark: ''
|
|
|
}
|
|
|
this.planModal.visible = true
|
|
|
this.$nextTick(() => {
|
|
|
if (this.$refs.planForm) {
|
|
|
this.$refs.planForm.resetFields();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
},
|
|
|
handleDateClear() {
|
|
|
this.planModal.form.dateRange = null;
|
|
|
this.$nextTick(() => {
|
|
|
this.planModal.form.dateRange = [];
|
|
|
this.$refs.planForm.validateField('dateRange');
|
|
|
});
|
|
|
},
|
|
|
async handlePlanSubmit() {
|
|
|
this.$refs.planForm.validateField('dateRange', (errorMessage) => {
|
|
|
if (errorMessage) {
|
|
|
this.$Message.error(errorMessage);
|
|
|
return;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
this.$refs.planForm.validate(async (valid) => {
|
|
|
if (!valid) {
|
|
|
this.$Message.error('请完整填写必填项');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const { id, name, dateRange, remark } = this.planModal.form;
|
|
|
const formatDate = (date) => {
|
|
|
const d = new Date(date);
|
|
|
const year = d.getFullYear();
|
|
|
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
|
const day = String(d.getDate()).padStart(2, '0');
|
|
|
return `${year}-${month}-${day}`;
|
|
|
};
|
|
|
|
|
|
// 获取当前计划选中的物资ID
|
|
|
const selectedMaterialIds = this.planMaterials[id] || [];
|
|
|
|
|
|
// 构建 formdata 格式的数据
|
|
|
const formData = new FormData();
|
|
|
formData.append('name', name);
|
|
|
formData.append('start_date', formatDate(dateRange[0]));
|
|
|
formData.append('end_date', formatDate(dateRange[1]));
|
|
|
if (remark) formData.append('remark', remark);
|
|
|
if (id) formData.append('id', id);
|
|
|
|
|
|
// 添加选中的物资ID数组
|
|
|
selectedMaterialIds.forEach((materialId, index) => {
|
|
|
formData.append(`material_infos_plan_links[${index}][material_info_id]`, materialId);
|
|
|
});
|
|
|
|
|
|
try {
|
|
|
await saveStocktakingPlan(formData);
|
|
|
this.$Message.success('保存成功');
|
|
|
this.planModal.visible = false;
|
|
|
this.$refs.planForm.resetFields();
|
|
|
this.searchPlans();
|
|
|
} catch (e) {
|
|
|
this.$Message.error('保存失败');
|
|
|
}
|
|
|
});
|
|
|
},
|
|
|
deletePlan(id) {
|
|
|
this.$confirm('确认要删除该计划吗?', '提示', {
|
|
|
confirmButtonText: '确定',
|
|
|
cancelButtonText: '取消',
|
|
|
type: 'warning'
|
|
|
}).then(async () => {
|
|
|
await deleteStocktakingPlan({ id })
|
|
|
this.$message.success('删除成功')
|
|
|
this.searchPlans()
|
|
|
}).catch(() => {});
|
|
|
},
|
|
|
|
|
|
async searchList() {
|
|
|
this.listLoading = true;
|
|
|
try {
|
|
|
const formData = new FormData();
|
|
|
formData.append('page', this.listSearch.pageIndex);
|
|
|
formData.append('page_size', this.listSearch.pageSize);
|
|
|
|
|
|
// 只添加非空的搜索条件
|
|
|
if (this.listSearch.keyword) {
|
|
|
formData.append('keyword', this.listSearch.keyword);
|
|
|
}
|
|
|
|
|
|
// 计划筛选
|
|
|
if (this.listSearch.planId) {
|
|
|
const filterIndex = this.listSearch.keyword ? 1 : 0;
|
|
|
formData.append(`filter[${filterIndex}][key]`, 'material_infos_plan_id');
|
|
|
formData.append(`filter[${filterIndex}][op]`, 'eq');
|
|
|
formData.append(`filter[${filterIndex}][value]`, this.listSearch.planId);
|
|
|
}
|
|
|
|
|
|
// 状态筛选
|
|
|
if (this.listSearch.status) {
|
|
|
const filterIndex = (this.listSearch.keyword ? 1 : 0) + (this.listSearch.planId ? 1 : 0);
|
|
|
formData.append(`filter[${filterIndex}][key]`, 'status');
|
|
|
formData.append(`filter[${filterIndex}][op]`, 'eq');
|
|
|
formData.append(`filter[${filterIndex}][value]`, this.listSearch.status);
|
|
|
}
|
|
|
|
|
|
const res = await getStocktakingPlanLinkList(formData);
|
|
|
if (res && res.list) {
|
|
|
this.inventoryList = res.list.data;
|
|
|
this.listTotal = res.list.total;
|
|
|
}
|
|
|
} catch (error) {
|
|
|
this.$Message.error('获取盘点列表失败');
|
|
|
console.error('获取盘点列表失败:', error);
|
|
|
} finally {
|
|
|
this.listLoading = false;
|
|
|
}
|
|
|
},
|
|
|
resetListSearch() {
|
|
|
this.listSearch = {
|
|
|
keyword: '',
|
|
|
planId: '',
|
|
|
status: '',
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10
|
|
|
}
|
|
|
this.searchList()
|
|
|
},
|
|
|
handleListPageChange(page) {
|
|
|
this.listSearch.pageIndex = page
|
|
|
this.searchList()
|
|
|
},
|
|
|
handleListPageSizeChange(size) {
|
|
|
this.listSearch.pageSize = size
|
|
|
this.listSearch.pageIndex = 1
|
|
|
this.searchList()
|
|
|
},
|
|
|
async viewInventoryDetail(row) {
|
|
|
this.detailModal.loading = true;
|
|
|
this.detailModal.visible = true;
|
|
|
this.detailModal.pageIndex = 1;
|
|
|
|
|
|
// 先用列表行数据初始化基本信息
|
|
|
this.detailModal.data = {
|
|
|
id: row.id,
|
|
|
no: row.no,
|
|
|
planName: row.material_infos_plan ? row.material_infos_plan.name : '-',
|
|
|
area: row.material_info ? row.material_info.suozaicangku : '-',
|
|
|
executor: row.responsible_admin ? row.responsible_admin.name : '-',
|
|
|
startTime: row.material_infos_plan ? row.material_infos_plan.start_date : '-',
|
|
|
endTime: row.material_infos_plan ? row.material_infos_plan.end_date : '-',
|
|
|
status: row.status,
|
|
|
material_info: row.material_info || {},
|
|
|
history: []
|
|
|
};
|
|
|
|
|
|
try {
|
|
|
// 1. 获取物资详情
|
|
|
const materialRes = await request({
|
|
|
url: '/api/admin/material-infos/show',
|
|
|
method: 'get',
|
|
|
params: { id: row.material_info.id }
|
|
|
});
|
|
|
if (materialRes) {
|
|
|
this.detailModal.data.material_info = materialRes;
|
|
|
}
|
|
|
|
|
|
// 2. 获取盘点历史记录
|
|
|
const params = {};
|
|
|
params['filter[0][key]'] = 'material_info_id';
|
|
|
params['filter[0][op]'] = 'eq';
|
|
|
params['filter[0][value]'] = row.material_info.id;
|
|
|
params['page'] = this.detailModal.pageIndex || 1;
|
|
|
params['page_size'] = this.detailModal.pageSize || 10;
|
|
|
const res = await getStocktakingHistoryList(qs.stringify(params));
|
|
|
|
|
|
if (res) {
|
|
|
// 更新物资信息和历史记录数据
|
|
|
this.detailModal.data = {
|
|
|
...this.detailModal.data,
|
|
|
history: res.data || []
|
|
|
};
|
|
|
this.detailModal.total = res.data ? res.data.length : 0;
|
|
|
}
|
|
|
} catch (error) {
|
|
|
this.$Message.error('获取盘点任务详情失败');
|
|
|
console.error('获取盘点任务详情失败:', error);
|
|
|
} finally {
|
|
|
this.detailModal.loading = false;
|
|
|
}
|
|
|
},
|
|
|
|
|
|
showInventorySelectModal(planId) {
|
|
|
this.materialModal.currentPlanId = planId
|
|
|
this.materialModal.visible = true
|
|
|
this.materialModal.pageIndex = 1
|
|
|
this.materialModal.selectedMaterialIds = new Set();
|
|
|
this.searchMaterials()
|
|
|
},
|
|
|
isSelected(item) {
|
|
|
return this.materialModal.selectedMaterialIds.has(item.id);
|
|
|
},
|
|
|
handleSelect(selection, item) {
|
|
|
console.log('handleSelect called', selection, item);
|
|
|
|
|
|
// 更新选中状态集合
|
|
|
const planId = this.materialModal.currentPlanId;
|
|
|
if (planId) {
|
|
|
if (selection.includes(item)) {
|
|
|
this.materialModal.selectedMaterialIds.add(item.id);
|
|
|
} else {
|
|
|
this.materialModal.selectedMaterialIds.delete(item.id);
|
|
|
}
|
|
|
|
|
|
// 更新界面
|
|
|
this.$nextTick(() => {
|
|
|
if (this.$refs.materialTable) {
|
|
|
this.materialList.forEach(row => {
|
|
|
this.$refs.materialTable.toggleRowSelection(row, this.materialModal.selectedMaterialIds.has(row.id));
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
},
|
|
|
handleSelectAll(selection) {
|
|
|
console.log('handleSelectAll called', selection);
|
|
|
|
|
|
const planId = this.materialModal.currentPlanId;
|
|
|
if (planId) {
|
|
|
// 更新选中状态集合
|
|
|
if (selection.length > 0) {
|
|
|
// 全选:将所有当前页的项添加到选中集合
|
|
|
this.materialList.forEach(item => {
|
|
|
this.materialModal.selectedMaterialIds.add(item.id);
|
|
|
});
|
|
|
} else {
|
|
|
// 取消全选:从选中集合中移除当前页的所有项
|
|
|
this.materialList.forEach(item => {
|
|
|
this.materialModal.selectedMaterialIds.delete(item.id);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 更新界面
|
|
|
this.$nextTick(() => {
|
|
|
if (this.$refs.materialTable) {
|
|
|
// 更新复选框状态
|
|
|
this.materialList.forEach(row => {
|
|
|
this.$refs.materialTable.toggleRowSelection(row, this.materialModal.selectedMaterialIds.has(row.id));
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
},
|
|
|
toggleMaterialSelection(item, index) {
|
|
|
console.log('toggleMaterialSelection called', item, index);
|
|
|
|
|
|
// 更新选中状态
|
|
|
if (!this.isSelected(item)) {
|
|
|
console.log('Adding to plan');
|
|
|
this.materialModal.selectedMaterialIds.add(item.id);
|
|
|
} else {
|
|
|
console.log('Removing from plan');
|
|
|
this.materialModal.selectedMaterialIds.delete(item.id);
|
|
|
}
|
|
|
|
|
|
// 更新界面
|
|
|
this.$nextTick(() => {
|
|
|
if (this.$refs.materialTable) {
|
|
|
this.materialList.forEach(row => {
|
|
|
this.$refs.materialTable.toggleRowSelection(row, this.materialModal.selectedMaterialIds.has(row.id));
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
},
|
|
|
async searchMaterials() {
|
|
|
this.materialModal.loading = true;
|
|
|
this.materialModal.isInitialLoad = true; // 重置标志
|
|
|
try {
|
|
|
const data = {
|
|
|
page_size: this.materialModal.pageSize,
|
|
|
page: this.materialModal.pageIndex,
|
|
|
'materialstorages_cangkumingcheng': this.materialModal.warehouseName || '',
|
|
|
'materialstorages_suozaiquyu': this.materialModal.area || '',
|
|
|
'storehouses_id': this.materialModal.storehouses_id || '',
|
|
|
'filter[0][key]': 'zichanmingcheng',
|
|
|
'filter[0][op]': 'like',
|
|
|
'filter[0][value]': this.materialModal.keyword || '',
|
|
|
'filter[1][key]': 'wuzileixing',
|
|
|
'filter[1][op]': 'eq',
|
|
|
'filter[1][value]': '一物一码'
|
|
|
};
|
|
|
const res = await request({
|
|
|
url: '/api/admin/material-infos/index',
|
|
|
method: 'post',
|
|
|
data: qs.stringify(data),
|
|
|
headers: {
|
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
|
}
|
|
|
});
|
|
|
if (res && res.data) {
|
|
|
this.materialList = res.data;
|
|
|
this.materialModal.total = res.total || 0;
|
|
|
|
|
|
// 初始化选中状态
|
|
|
const planId = this.materialModal.currentPlanId;
|
|
|
if (planId) {
|
|
|
// 如果是第一次加载,初始化选中状态
|
|
|
if (this.materialModal.selectedMaterialIds.size === 0) {
|
|
|
this.materialList.forEach(item => {
|
|
|
if (item.material_infos_plan_link && item.material_infos_plan_link.length > 0) {
|
|
|
// 遍历物资绑定的所有盘点计划
|
|
|
const hasMatchingPlan = item.material_infos_plan_link.some(link =>
|
|
|
link.material_infos_plan_id === planId
|
|
|
);
|
|
|
if (hasMatchingPlan) {
|
|
|
console.log('item.id', item.id);
|
|
|
this.materialModal.selectedMaterialIds.add(item.id);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
console.log('this.materialModal.selectedMaterialIds', this.materialModal.selectedMaterialIds)
|
|
|
}
|
|
|
}
|
|
|
} finally {
|
|
|
this.materialModal.loading = false;
|
|
|
}
|
|
|
},
|
|
|
async batchAddMaterials() {
|
|
|
const selection = this.materialList.filter(m => m.selected)
|
|
|
if (selection.length === 0) {
|
|
|
this.$Message.warning('请至少选择一项物资')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
const planId = this.materialModal.currentPlanId
|
|
|
if (!this.planMaterials[planId]) {
|
|
|
this.planMaterials[planId] = []
|
|
|
}
|
|
|
|
|
|
selection.forEach(material => {
|
|
|
if (!this.planMaterials[planId].includes(material.id)) {
|
|
|
this.planMaterials[planId].push(material.id)
|
|
|
}
|
|
|
})
|
|
|
|
|
|
this.$Message.success(`已批量加入 ${selection.length} 项物资`)
|
|
|
this.searchMaterials()
|
|
|
},
|
|
|
async batchRemoveMaterials() {
|
|
|
const selection = this.materialList.filter(m => m.selected)
|
|
|
if (selection.length === 0) {
|
|
|
this.$Message.warning('请至少选择一项物资')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
const planId = this.materialModal.currentPlanId
|
|
|
if (this.planMaterials[planId]) {
|
|
|
this.planMaterials[planId] = this.planMaterials[planId].filter(
|
|
|
id => !selection.some(m => m.id === id)
|
|
|
)
|
|
|
}
|
|
|
|
|
|
this.$Message.success(`已批量移出 ${selection.length} 项物资`)
|
|
|
this.searchMaterials()
|
|
|
},
|
|
|
async initMaterialSelectOptions() {
|
|
|
// 仓库类型
|
|
|
try {
|
|
|
const res = await getStorehouseTypeList({ page: 1, page_size: 999 })
|
|
|
this.storehouseTypeOptions = (res.data || []).map(item => ({
|
|
|
value: item.id,
|
|
|
label: item.name
|
|
|
}))
|
|
|
} catch (e) {
|
|
|
this.storehouseTypeOptions = []
|
|
|
}
|
|
|
// 区域
|
|
|
try {
|
|
|
const res = await getparameteritem('area')
|
|
|
this.areaOptions = (res.detail || []).map(item => ({
|
|
|
value: item.value,
|
|
|
label: item.label
|
|
|
}))
|
|
|
} catch (e) {
|
|
|
this.areaOptions = []
|
|
|
}
|
|
|
// 仓库名称
|
|
|
try {
|
|
|
const res = await getWarehouseList({
|
|
|
page: 1,
|
|
|
page_size: 999,
|
|
|
table_name: 'materialstorages'
|
|
|
})
|
|
|
this.warehouseNameOptions = (res.data || []).map(item => ({
|
|
|
value: item.id,
|
|
|
label: item.cangkumingcheng
|
|
|
}))
|
|
|
} catch (e) {
|
|
|
this.warehouseNameOptions = []
|
|
|
}
|
|
|
},
|
|
|
resetMaterialSearch() {
|
|
|
this.materialModal.keyword = ''
|
|
|
this.materialModal.storehouses_id = ''
|
|
|
this.materialModal.area = ''
|
|
|
this.materialModal.warehouseName = ''
|
|
|
this.materialModal.pageIndex = 1
|
|
|
this.searchMaterials()
|
|
|
},
|
|
|
async onStorehouseTypeChange(val) {
|
|
|
this.materialModal.storehouses_id = val
|
|
|
await this.updateWarehouseNameOptions()
|
|
|
},
|
|
|
async onAreaChange(val) {
|
|
|
this.materialModal.area = val
|
|
|
await this.updateWarehouseNameOptions()
|
|
|
},
|
|
|
async updateWarehouseNameOptions() {
|
|
|
// 联动过滤仓库名称
|
|
|
try {
|
|
|
const res = await getWarehouseList({
|
|
|
page: 1,
|
|
|
page_size: 999,
|
|
|
table_name: 'materialstorages',
|
|
|
filter: [
|
|
|
{ key: 'storehouses_id', op: 'eq', value: this.materialModal.storehouses_id || '' },
|
|
|
{ key: 'quyu_id', op: 'eq', value: this.materialModal.area || '' }
|
|
|
]
|
|
|
})
|
|
|
this.warehouseNameOptions = (res.data || []).map(item => ({
|
|
|
value: item.id,
|
|
|
label: item.cangkumingcheng
|
|
|
}))
|
|
|
} catch (e) {
|
|
|
this.warehouseNameOptions = []
|
|
|
}
|
|
|
},
|
|
|
handleMaterialPageChange(page) {
|
|
|
this.materialModal.pageIndex = page
|
|
|
this.searchMaterials()
|
|
|
},
|
|
|
handleMaterialPageSizeChange(size) {
|
|
|
this.materialModal.pageSize = size
|
|
|
this.materialModal.pageIndex = 1
|
|
|
this.searchMaterials()
|
|
|
},
|
|
|
async handleMaterialSubmit() {
|
|
|
const planId = this.materialModal.currentPlanId;
|
|
|
if (!planId) {
|
|
|
this.$Message.error('未找到计划ID');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 获取当前选中的物资ID
|
|
|
const selectedMaterialIds = Array.from(this.materialModal.selectedMaterialIds);
|
|
|
|
|
|
// 构建formdata
|
|
|
const formData = new FormData();
|
|
|
formData.append('id', planId);
|
|
|
selectedMaterialIds.forEach((materialId, index) => {
|
|
|
formData.append(`material_infos_plan_links[${index}][material_info_id]`, materialId);
|
|
|
});
|
|
|
|
|
|
try {
|
|
|
await saveStocktakingPlan(formData);
|
|
|
this.$Message.success('物资关联成功');
|
|
|
this.materialModal.visible = false;
|
|
|
this.searchPlans();
|
|
|
} catch (e) {
|
|
|
this.$Message.error('物资关联失败');
|
|
|
}
|
|
|
},
|
|
|
handleDetailPageChange(page) {
|
|
|
this.detailModal.pageIndex = page;
|
|
|
this.viewInventoryDetail(this.detailModal.data.id);
|
|
|
},
|
|
|
handleDetailPageSizeChange(size) {
|
|
|
this.detailModal.pageSize = size;
|
|
|
this.detailModal.pageIndex = 1;
|
|
|
this.viewInventoryDetail(this.detailModal.data.id);
|
|
|
},
|
|
|
previewImage(url) {
|
|
|
this.previewUrl = url;
|
|
|
},
|
|
|
closePreview() {
|
|
|
this.previewUrl = '';
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
'materialModal.loading': {
|
|
|
handler(newVal, oldVal) {
|
|
|
// 当loading从true变为false时,说明表格加载完成
|
|
|
if (oldVal === true && newVal === false) {
|
|
|
this.$nextTick(() => {
|
|
|
if (this.$refs.materialTable) {
|
|
|
this.materialList.forEach((item, index) => {
|
|
|
if (this.materialModal.selectedMaterialIds.has(item.id)) {
|
|
|
this.$refs.materialTable.toggleRowSelection(item, true);
|
|
|
console.log('item.id1111111111111111111111', item.id)
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
.code {
|
|
|
position: absolute;
|
|
|
left: 0;
|
|
|
top: 0;
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
z-index: 9999
|
|
|
}
|
|
|
|
|
|
#qrCode {
|
|
|
position: absolute;
|
|
|
top: 50%;
|
|
|
left: 50%;
|
|
|
transform: translate(-50%, -50%);
|
|
|
}
|
|
|
|
|
|
.content-wrapper {
|
|
|
background-color: #fff;
|
|
|
padding: 20px;
|
|
|
border-radius: 8px;
|
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
|
}
|
|
|
|
|
|
.page-header {
|
|
|
margin-bottom: 20px;
|
|
|
padding-bottom: 15px;
|
|
|
border-bottom: 1px solid #e8e8e8;
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.page-title {
|
|
|
font-size: 1.3rem;
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
margin: 0;
|
|
|
}
|
|
|
|
|
|
.search-filters {
|
|
|
padding: 15px 0;
|
|
|
margin-bottom: 20px;
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
align-items: center;
|
|
|
gap: 15px 20px;
|
|
|
}
|
|
|
|
|
|
.filter-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 8px;
|
|
|
}
|
|
|
|
|
|
.pagination-container {
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
margin-top: 20px;
|
|
|
padding-top: 15px;
|
|
|
border-top: 1px solid #f0f0f0;
|
|
|
}
|
|
|
|
|
|
.el-pagination {
|
|
|
padding: 0;
|
|
|
font-weight: normal;
|
|
|
.el-pagination__sizes {
|
|
|
margin-right: 16px;
|
|
|
}
|
|
|
.el-pagination__jump {
|
|
|
margin-left: 16px;
|
|
|
}
|
|
|
.el-pagination__total {
|
|
|
margin-right: 16px;
|
|
|
}
|
|
|
.btn-prev,
|
|
|
.btn-next {
|
|
|
background: #fff;
|
|
|
border: 1px solid #dcdfe6;
|
|
|
&:hover {
|
|
|
color: #409eff;
|
|
|
}
|
|
|
}
|
|
|
.el-pager li {
|
|
|
background: #fff;
|
|
|
border: 1px solid #dcdfe6;
|
|
|
&:hover {
|
|
|
color: #409eff;
|
|
|
}
|
|
|
&.active {
|
|
|
background-color: #409eff;
|
|
|
color: #fff;
|
|
|
border-color: #409eff;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.material-search {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 10px;
|
|
|
margin-bottom: 10px;
|
|
|
}
|
|
|
|
|
|
.ivu-modal {
|
|
|
.ivu-modal-content {
|
|
|
width: 900px;
|
|
|
max-width: 90vw;
|
|
|
}
|
|
|
|
|
|
.ivu-modal-header {
|
|
|
padding: 16px 24px;
|
|
|
border-bottom: 1px solid #e8eaec;
|
|
|
|
|
|
.ivu-modal-header-inner {
|
|
|
font-size: 16px;
|
|
|
font-weight: 500;
|
|
|
color: #17233d;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.ivu-modal-body {
|
|
|
padding: 24px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.ivu-table {
|
|
|
.ivu-table-cell {
|
|
|
padding: 8px 16px;
|
|
|
}
|
|
|
|
|
|
.ivu-table-header {
|
|
|
th {
|
|
|
background: #f8f8f9;
|
|
|
font-weight: 600;
|
|
|
color: #17233d;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.ivu-table-tbody {
|
|
|
tr {
|
|
|
&:hover {
|
|
|
td {
|
|
|
background: #f5f7fa;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.detail-content {
|
|
|
padding: 20px;
|
|
|
|
|
|
.detail-header {
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
gap: 20px;
|
|
|
margin-bottom: 30px;
|
|
|
|
|
|
.detail-item {
|
|
|
.label {
|
|
|
font-weight: bold;
|
|
|
color: #666;
|
|
|
margin-right: 10px;
|
|
|
}
|
|
|
|
|
|
.value {
|
|
|
color: #333;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.material-info-section {
|
|
|
margin-bottom: 30px;
|
|
|
|
|
|
h3 {
|
|
|
margin: 0 0 15px 0;
|
|
|
font-size: 16px;
|
|
|
color: #17233d;
|
|
|
}
|
|
|
|
|
|
.material-info-content {
|
|
|
background: #f8f8f9;
|
|
|
padding: 20px;
|
|
|
border-radius: 8px;
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
gap: 20px;
|
|
|
|
|
|
.info-item {
|
|
|
.label {
|
|
|
font-weight: bold;
|
|
|
color: #666;
|
|
|
margin-right: 10px;
|
|
|
}
|
|
|
|
|
|
.value {
|
|
|
color: #333;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.history-section {
|
|
|
h3 {
|
|
|
margin: 0 0 15px 0;
|
|
|
font-size: 16px;
|
|
|
color: #17233d;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.photo-list {
|
|
|
display: flex;
|
|
|
gap: 8px;
|
|
|
align-items: center;
|
|
|
|
|
|
.photo-item {
|
|
|
width: 40px;
|
|
|
height: 40px;
|
|
|
border-radius: 4px;
|
|
|
overflow: hidden;
|
|
|
cursor: pointer;
|
|
|
|
|
|
img {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
object-fit: cover;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.photo-more {
|
|
|
color: #666;
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.image-preview-modal {
|
|
|
position: fixed;
|
|
|
z-index: 2000;
|
|
|
left: 0; top: 0; right: 0; bottom: 0;
|
|
|
background: rgba(0,0,0,0.7);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.image-preview-large {
|
|
|
max-width: 90vw;
|
|
|
max-height: 90vh;
|
|
|
border-radius: 8px;
|
|
|
box-shadow: 0 4px 24px rgba(0,0,0,0.3);
|
|
|
background: #fff;
|
|
|
}
|
|
|
</style>
|