|
|
<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"
|
|
|
/>
|
|
|
</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" height="400">
|
|
|
<template slot="status" slot-scope="{ row }">
|
|
|
<Tag :color="getStatusColor(row.status)">{{ getStatusText(row.status) }}</Tag>
|
|
|
</template>
|
|
|
<template slot="progress" slot-scope="{ row }">
|
|
|
<Progress :percent="getPlanProgress(row)" :stroke-width="15" />
|
|
|
</template>
|
|
|
<template slot="action" slot-scope="{ row }">
|
|
|
<div style="display: flex; gap: 8px; justify-content: center">
|
|
|
<Button
|
|
|
type="primary"
|
|
|
size="small"
|
|
|
style="border-radius: 6px"
|
|
|
@click="showInventorySelectModal(row.id)"
|
|
|
>选择对象</Button
|
|
|
>
|
|
|
<!-- <Button
|
|
|
v-if="row.status === 2"
|
|
|
type="success"
|
|
|
size="small"
|
|
|
style="border-radius: 6px"
|
|
|
ghost
|
|
|
@click="showSummaryModal(row)"
|
|
|
>盘点小结</Button
|
|
|
> -->
|
|
|
<template v-if="row.status === 2">
|
|
|
<el-dropdown trigger="hover" @command="command => handleMoreCommand(command, row)">
|
|
|
<Button type="info" size="small" style="border-radius: 6px" ghost
|
|
|
>更多<i class="el-icon-arrow-down el-icon--right"
|
|
|
/></Button>
|
|
|
<el-dropdown-menu slot="dropdown">
|
|
|
<el-dropdown-item command="edit">编辑</el-dropdown-item>
|
|
|
<el-dropdown-item command="delete">删除</el-dropdown-item>
|
|
|
<el-dropdown-item command="view">查看</el-dropdown-item>
|
|
|
</el-dropdown-menu>
|
|
|
</el-dropdown>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<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
|
|
|
>
|
|
|
<Button
|
|
|
type="info"
|
|
|
size="small"
|
|
|
style="border-radius: 6px"
|
|
|
ghost
|
|
|
@click="viewPlanDetail(row)"
|
|
|
>查看</Button
|
|
|
>
|
|
|
</template>
|
|
|
</div>
|
|
|
</template>
|
|
|
</Table>
|
|
|
|
|
|
<!-- Plan Pagination -->
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
:current-page="planSearch.pageIndex"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
:page-size="planSearch.pageSize"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="planTotal"
|
|
|
@size-change="handlePlanPageSizeChange"
|
|
|
@current-change="handlePlanPageChange"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 历史盘点计划 Section -->
|
|
|
<div class="content-wrapper" style="margin-top: 20px">
|
|
|
<div class="page-header">
|
|
|
<h1 class="page-title">历史盘点计划</h1>
|
|
|
</div>
|
|
|
|
|
|
<Tabs v-model="historyTab" @on-click="handleHistoryTabChange">
|
|
|
<!-- 计划内 Tab -->
|
|
|
<TabPane label="计划内" name="plan">
|
|
|
<!-- Plan Search Filters -->
|
|
|
<div class="search-filters">
|
|
|
<div class="filter-item">
|
|
|
<span class="selector-item__label">关键词:</span>
|
|
|
<Input
|
|
|
v-model="historyPlanSearch.keyword"
|
|
|
style="width: 180px"
|
|
|
:clearable="true"
|
|
|
placeholder="计划名称/编号"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
<span class="selector-item__label">计划日期:</span>
|
|
|
<DatePicker
|
|
|
v-model="historyPlanSearch.dateRange"
|
|
|
type="daterange"
|
|
|
split-panels
|
|
|
style="width: 200px"
|
|
|
:clearable="true"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
<Button type="primary" @click="searchHistoryPlans">查询</Button>
|
|
|
<Button style="margin-left: 10px" @click="resetHistoryPlanSearch">重置</Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- Plan Table -->
|
|
|
<Table :columns="planColumns" :data="historyPlanList" :loading="historyPlanLoading">
|
|
|
<template slot="status" slot-scope="{ row }">
|
|
|
<Tag :color="getStatusColor(row.status)">{{ getStatusText(row.status) }}</Tag>
|
|
|
</template>
|
|
|
<template slot="progress" slot-scope="{ row }">
|
|
|
<Progress :percent="getPlanProgress(row)" :stroke-width="15" />
|
|
|
</template>
|
|
|
<template slot="action" slot-scope="{ row }">
|
|
|
<div style="display: flex; gap: 8px; justify-content: center">
|
|
|
<Button
|
|
|
type="success"
|
|
|
size="small"
|
|
|
style="border-radius: 6px"
|
|
|
ghost
|
|
|
@click="showSummaryModal(row)"
|
|
|
>盘点小结</Button
|
|
|
>
|
|
|
<el-dropdown trigger="hover" @command="command => handleMoreCommand(command, row)">
|
|
|
<Button type="info" size="small" style="border-radius: 6px" ghost
|
|
|
>更多<i class="el-icon-arrow-down el-icon--right"
|
|
|
/></Button>
|
|
|
<el-dropdown-menu slot="dropdown">
|
|
|
<el-dropdown-item command="edit">编辑</el-dropdown-item>
|
|
|
<el-dropdown-item command="delete">删除</el-dropdown-item>
|
|
|
<el-dropdown-item command="view">查看</el-dropdown-item>
|
|
|
</el-dropdown-menu>
|
|
|
</el-dropdown>
|
|
|
</div>
|
|
|
</template>
|
|
|
</Table>
|
|
|
|
|
|
<!-- Plan Pagination -->
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
:current-page="historyPlanSearch.pageIndex"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
:page-size="historyPlanSearch.pageSize"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="historyPlanTotal"
|
|
|
@size-change="handleHistoryPlanPageSizeChange"
|
|
|
@current-change="handleHistoryPlanPageChange"
|
|
|
/>
|
|
|
</div>
|
|
|
</TabPane>
|
|
|
|
|
|
<!-- 计划外 Tab -->
|
|
|
<TabPane label="计划外" name="outside">
|
|
|
<!-- List Search Filters -->
|
|
|
<div class="search-filters">
|
|
|
<div class="filter-item">
|
|
|
<span class="selector-item__label">盘点日期:</span>
|
|
|
<DatePicker
|
|
|
v-model="listSearch.dateRange"
|
|
|
type="daterange"
|
|
|
split-panels
|
|
|
style="width: 200px"
|
|
|
:clearable="true"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
<Button type="primary" @click="searchList">查询</Button>
|
|
|
<Button style="margin-left: 10px" @click="resetListSearch">重置</Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- History List Table -->
|
|
|
<Table :columns="outsideListColumns" :data="inventoryList" :loading="listLoading" border height="400">
|
|
|
<template slot="status" slot-scope="{ row }">
|
|
|
<Tag :color="getInventoryStatusColor(row.status)">{{
|
|
|
getInventoryStatusText(row.status)
|
|
|
}}</Tag>
|
|
|
</template>
|
|
|
<template slot="responsible_admin" slot-scope="{ row }">
|
|
|
{{ row.responsible_admin ? row.responsible_admin.name : '-' }}
|
|
|
</template>
|
|
|
<template slot="photos" slot-scope="{ row }">
|
|
|
<div v-if="row.files && row.files.length" class="photo-list">
|
|
|
<div v-for="(file, index) in row.files.slice(0, 3)" :key="index" class="photo-item">
|
|
|
<img :src="file.url" @click="previewImage(file.url)" />
|
|
|
</div>
|
|
|
<div v-if="row.files.length > 3" class="photo-more">
|
|
|
+{{ row.files.length - 3 }}
|
|
|
</div>
|
|
|
</div>
|
|
|
<span v-else>-</span>
|
|
|
</template>
|
|
|
</Table>
|
|
|
|
|
|
<!-- List Pagination -->
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
:current-page="listSearch.pageIndex"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
:page-size="listSearch.pageSize"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="listTotal"
|
|
|
@size-change="handleListPageSizeChange"
|
|
|
@current-change="handleListPageChange"
|
|
|
/>
|
|
|
</div>
|
|
|
</TabPane>
|
|
|
</Tabs>
|
|
|
</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%"
|
|
|
:clearable="true"
|
|
|
:editable="false"
|
|
|
@on-clear="handleDateClear"
|
|
|
/>
|
|
|
</FormItem>
|
|
|
<FormItem label="盘点类型" prop="type">
|
|
|
<Select v-model="planModal.form.type" style="width: 200px">
|
|
|
<Option :value="1">年度</Option>
|
|
|
<Option :value="2">季度</Option>
|
|
|
</Select>
|
|
|
</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="70%"
|
|
|
@on-cancel="handleMaterialModalClose"
|
|
|
>
|
|
|
<div style="display: flex; justify-content: flex-start; flex-wrap: wrap">
|
|
|
<div class="selector-item">
|
|
|
<el-input
|
|
|
v-model="materialModal.keyword"
|
|
|
style="width: 120px; margin-right: 10px"
|
|
|
placeholder="物资名称搜索"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="selector-item">
|
|
|
<el-cascader
|
|
|
v-model="materialModal.fenlei"
|
|
|
placeholder="请选择所属种类"
|
|
|
ref="cascaders"
|
|
|
clearable
|
|
|
popper-class="select_popper"
|
|
|
style="margin-right: 10px; flex-basis: 20%"
|
|
|
:options="fenleiList"
|
|
|
:props="{ label: 'name', value: 'id' }"
|
|
|
@change="
|
|
|
e => {
|
|
|
changeFenlei(e)
|
|
|
}
|
|
|
"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="selector-item">
|
|
|
<el-select
|
|
|
clearable
|
|
|
v-model="materialModal.suozaicangku"
|
|
|
style="width: 100%"
|
|
|
placeholder="请选择所在仓库"
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="item in cangkuList"
|
|
|
:key="item.id"
|
|
|
:label="item.cangkumingcheng"
|
|
|
:value="item.id"
|
|
|
/>
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div class="selector-item">
|
|
|
<el-date-picker
|
|
|
type="daterange"
|
|
|
range-separator="至"
|
|
|
start-placeholder="入库日期开始"
|
|
|
end-placeholder="入库日期结束"
|
|
|
format="yyyy-MM-dd"
|
|
|
value-format="yyyy-MM-dd"
|
|
|
v-model="materialModal.rukuriqi"
|
|
|
></el-date-picker>
|
|
|
</div>
|
|
|
<div class="selector-item">
|
|
|
<el-date-picker
|
|
|
format="yyyy-MM-dd"
|
|
|
type="daterange"
|
|
|
range-separator="至"
|
|
|
start-placeholder="生产日期开始"
|
|
|
end-placeholder="生产日期结束"
|
|
|
value-format="yyyy-MM-dd"
|
|
|
v-model="materialModal.shengchanriqi"
|
|
|
></el-date-picker>
|
|
|
</div>
|
|
|
<div class="selector-item">
|
|
|
<el-select
|
|
|
clearable
|
|
|
v-model="materialModal.wuzizhuangtai"
|
|
|
style="width: 100%"
|
|
|
placeholder="请选择物资状态"
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="item in paraOptions.materials_status"
|
|
|
:key="item.id"
|
|
|
:label="item.value"
|
|
|
:value="item.id"
|
|
|
/>
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div class="selector-item">
|
|
|
<el-select
|
|
|
clearable
|
|
|
v-model="materialModal.chanquanxinxi"
|
|
|
style="width: 100%"
|
|
|
placeholder="请选择产权信息"
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="item in paraOptions.materials_property"
|
|
|
:key="item.id"
|
|
|
:label="item.value"
|
|
|
:value="item.id"
|
|
|
/>
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div class="selector-item">
|
|
|
<el-select
|
|
|
clearable
|
|
|
v-model="materialModal.chubeifangshi"
|
|
|
style="width: 100%"
|
|
|
placeholder="请选择储备方式"
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="item in paraOptions.material_reserve"
|
|
|
:key="item.id"
|
|
|
:label="item.value"
|
|
|
:value="item.id"
|
|
|
/>
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div class="selector-item">
|
|
|
<el-select
|
|
|
clearable
|
|
|
v-model="materialModal.dengjifenlei"
|
|
|
style="width: 100%"
|
|
|
placeholder="请选择等级分类"
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="item in paraOptions.dengjifenlei"
|
|
|
:key="item.id"
|
|
|
:label="item.value"
|
|
|
:value="item.id"
|
|
|
/>
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div>
|
|
|
<el-button type="primary" style="margin-left: 10px" @click="searchMaterials"
|
|
|
>查询</el-button
|
|
|
>
|
|
|
<el-button type="primary" style="margin-left: 10px" @click="resetMaterialSearch"
|
|
|
>重置</el-button
|
|
|
>
|
|
|
</div>
|
|
|
</div>
|
|
|
<xy-table
|
|
|
ref="materialTable"
|
|
|
:key="materialModal.tableKey"
|
|
|
v-loading="materialModal.loading"
|
|
|
:pageSize="materialModal.pageSize"
|
|
|
:pageIndex="materialModal.pageIndex"
|
|
|
:span-method="objectSpanMethod"
|
|
|
:list="materialList"
|
|
|
:total="materialModal.total"
|
|
|
:table-item="materialTable"
|
|
|
:auths="[]"
|
|
|
:default-selections="getSelectedRows()"
|
|
|
@selection-change="selectionChange"
|
|
|
@pageSizeChange="handleMaterialPageSizeChange"
|
|
|
@pageIndexChange="handleMaterialPageChange"
|
|
|
>
|
|
|
<template v-slot:btns>
|
|
|
<el-table-column
|
|
|
fixed="right"
|
|
|
align="center"
|
|
|
label="操作"
|
|
|
width="120"
|
|
|
header-align="center"
|
|
|
>
|
|
|
<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>
|
|
|
</template>
|
|
|
</xy-table>
|
|
|
<template slot="footer">
|
|
|
<Button @click="handleMaterialModalClose">取消</Button>
|
|
|
<Button type="primary" @click="handleMaterialSubmit">确定</Button>
|
|
|
</template>
|
|
|
</Modal>
|
|
|
|
|
|
<!-- 盘点任务详情弹窗 -->
|
|
|
<Modal v-model="detailModal.visible" title="盘点任务详情" width="1000" :mask-closable="true">
|
|
|
<div v-if="detailModal.data" class="detail-content">
|
|
|
<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="status" slot-scope="{ row }">
|
|
|
<Tag :color="getInventoryStatusColor(row.status)">{{
|
|
|
getInventoryStatusText(row.status)
|
|
|
}}</Tag>
|
|
|
</template>
|
|
|
<template slot="photos" slot-scope="{ row }">
|
|
|
<div v-if="row.files && row.files.length" class="photo-list">
|
|
|
<div v-for="(file, index) in row.files.slice(0, 3)" :key="index" class="photo-item">
|
|
|
<img :src="file.url" @click="previewImage(file.url)" />
|
|
|
</div>
|
|
|
<div v-if="row.files.length > 3" class="photo-more">
|
|
|
+{{ row.files.length - 3 }}
|
|
|
</div>
|
|
|
</div>
|
|
|
<span v-else>-</span>
|
|
|
</template>
|
|
|
</Table>
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
:current-page="detailModal.pageIndex"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
:page-size="detailModal.pageSize"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="detailModal.total"
|
|
|
@size-change="handleDetailPageSizeChange"
|
|
|
@current-change="handleDetailPageChange"
|
|
|
/>
|
|
|
</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>
|
|
|
|
|
|
<!-- Plan Detail Modal -->
|
|
|
<Modal
|
|
|
v-model="planDetailModal.visible"
|
|
|
title="盘点计划详情"
|
|
|
width="1000"
|
|
|
:mask-closable="true"
|
|
|
>
|
|
|
<div v-if="planDetailModal.data" class="plan-detail-content">
|
|
|
<div class="detail-header">
|
|
|
<div class="detail-item">
|
|
|
<span class="label">计划编号:</span>
|
|
|
<span class="value">{{ planDetailModal.data.no }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">计划名称:</span>
|
|
|
<span class="value">{{ planDetailModal.data.name }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">开始日期:</span>
|
|
|
<span class="value">{{ planDetailModal.data.start_date }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">结束日期:</span>
|
|
|
<span class="value">{{ planDetailModal.data.end_date }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">状态:</span>
|
|
|
<span class="value">
|
|
|
<Tag :color="getStatusColor(planDetailModal.data.status)">
|
|
|
{{ getStatusText(planDetailModal.data.status) }}
|
|
|
</Tag>
|
|
|
</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="label">备注:</span>
|
|
|
<span class="value">{{ planDetailModal.data.remark || '-' }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="inventory-list-section">
|
|
|
<h3>盘点物资列表</h3>
|
|
|
<Table
|
|
|
:columns="planDetailColumns"
|
|
|
:data="planDetailModal.inventoryList"
|
|
|
:loading="planDetailModal.loading"
|
|
|
border
|
|
|
>
|
|
|
<template slot="status" slot-scope="{ row }">
|
|
|
<Tag :color="getInventoryStatusColor(row.status)">{{
|
|
|
getInventoryStatusText(row.status)
|
|
|
}}</Tag>
|
|
|
</template>
|
|
|
<template slot="photos" slot-scope="{ row }">
|
|
|
<div v-if="row.files && row.files.length" class="photo-list">
|
|
|
<div v-for="(file, index) in row.files.slice(0, 3)" :key="index" class="photo-item">
|
|
|
<img :src="file.url" @click="previewImage(file.url)" />
|
|
|
</div>
|
|
|
<div v-if="row.files.length > 3" class="photo-more">
|
|
|
+{{ row.files.length - 3 }}
|
|
|
</div>
|
|
|
</div>
|
|
|
<span v-else>-</span>
|
|
|
</template>
|
|
|
</Table>
|
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
|
:current-page="planDetailModal.pageIndex"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
:page-size="planDetailModal.pageSize"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="planDetailModal.total"
|
|
|
@size-change="handlePlanDetailPageSizeChange"
|
|
|
@current-change="handlePlanDetailPageChange"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<template slot="footer">
|
|
|
<Button @click="planDetailModal.visible = false">关闭</Button>
|
|
|
</template>
|
|
|
</Modal>
|
|
|
|
|
|
<!-- 盘点小结 Modal -->
|
|
|
<Modal v-model="summaryModal.visible" title="盘点小结" width="800">
|
|
|
<div>
|
|
|
<div id="print-table">
|
|
|
<div style="text-align: center; font-size: 20px; font-weight: bold; margin-bottom: 12px">
|
|
|
盘点小结
|
|
|
</div>
|
|
|
<table class="summary-table">
|
|
|
<tr>
|
|
|
<td>盘点计划名称</td>
|
|
|
<td>{{ summaryModal.data ? summaryModal.data.name : '' }}</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>计划开始日期</td>
|
|
|
<td>{{ summaryModal.data ? summaryModal.data.planStart : '' }}</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>计划结束日期</td>
|
|
|
<td>{{ summaryModal.data ? summaryModal.data.planEnd : '' }}</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>实际开始日期</td>
|
|
|
<td>{{ summaryModal.data ? summaryModal.data.actualStart : '' }}</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>实际结束日期</td>
|
|
|
<td>{{ summaryModal.data ? summaryModal.data.actualEnd : '' }}</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>计划盘点清单数量</td>
|
|
|
<td>{{ summaryModal.data ? summaryModal.data.planCount : '' }}</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>实际盘点清单数量</td>
|
|
|
<td>{{ summaryModal.data ? summaryModal.data.actualCount : '' }}</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td class="sign-cell">签字区</td>
|
|
|
<td style="text-align: center">
|
|
|
<img :src="summaryModal.signImage" alt="" style="max-width: 200px; max-height: 100px; border: 1px solid #eee; cursor: pointer">
|
|
|
</td>
|
|
|
</tr>
|
|
|
</table>
|
|
|
</div>
|
|
|
<div style="margin: 24px 0 8px 0">
|
|
|
<span style="font-weight: bold">签字图片上传:</span>
|
|
|
<div v-if="summaryModal.signImage" style="margin-top: 8px">
|
|
|
<div style="position: relative; display: inline-block">
|
|
|
<img
|
|
|
:src="summaryModal.signImage"
|
|
|
style="max-width: 200px; max-height: 100px; border: 1px solid #eee; cursor: pointer"
|
|
|
@click="previewImage(summaryModal.signImage)"
|
|
|
/>
|
|
|
<Button
|
|
|
type="error"
|
|
|
size="small"
|
|
|
style="position: absolute; top: 5px; right: 5px"
|
|
|
@click.stop="
|
|
|
summaryModal.signImage = null
|
|
|
summaryModal.signImageId = null
|
|
|
"
|
|
|
>
|
|
|
重新上传
|
|
|
</Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else style="margin-top: 8px">
|
|
|
<Upload
|
|
|
:before-upload="beforeSignUpload"
|
|
|
:on-success="handleSignUpload"
|
|
|
:show-upload-list="false"
|
|
|
:action="baseUrl + 'api/admin/upload-file'"
|
|
|
>
|
|
|
<Button type="primary" size="small">上传签字图片</Button>
|
|
|
</Upload>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<template slot="footer">
|
|
|
<Button v-print="'#print-table'">打印</Button>
|
|
|
<Button type="primary" style="margin-left: 8px" @click="handleSummarySubmit">提交</Button>
|
|
|
</template>
|
|
|
</Modal>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import {
|
|
|
saveStocktakingPlan,
|
|
|
getStocktakingPlanList,
|
|
|
deleteStocktakingPlan,
|
|
|
getStocktakingPlanLinkList,
|
|
|
getStocktakingHistoryList,
|
|
|
deleteStocktakingPlanLink
|
|
|
} from '@/api/system/stocktaking'
|
|
|
import { index } from '@/api/inventory.js'
|
|
|
import { index as getFenleilist } from '@/api/fenlei.js'
|
|
|
import { index as baseFormIndex } from '@/api/system/baseForm.js'
|
|
|
import { getparameteritemMore } from '@/api/system/dictionary.js'
|
|
|
import qs from 'qs'
|
|
|
import request from '@/utils/request'
|
|
|
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
baseUrl: process.env.VUE_APP_BASE_API || window.location.origin + '/',
|
|
|
planSearch: {
|
|
|
keyword: '',
|
|
|
dateRange: [],
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10
|
|
|
},
|
|
|
planList: [],
|
|
|
planTotal: 0,
|
|
|
planLoading: false,
|
|
|
planColumns: [
|
|
|
{ title: '计划编号', key: 'no' },
|
|
|
{ title: '计划名称', key: 'name' },
|
|
|
{ title: '对象数量', key: 'chart_total' },
|
|
|
{
|
|
|
title: '盘点类型',
|
|
|
key: 'type',
|
|
|
render: (h, params) => {
|
|
|
const typeMap = {
|
|
|
1: '年度',
|
|
|
2: '季度'
|
|
|
}
|
|
|
return h('span', typeMap[params.row.type] || '-')
|
|
|
}
|
|
|
},
|
|
|
{ title: '开始日期', key: 'start_date' },
|
|
|
{ title: '结束日期', key: 'end_date' },
|
|
|
{
|
|
|
title: '状态',
|
|
|
slot: 'status',
|
|
|
key: 'status'
|
|
|
},
|
|
|
{
|
|
|
title: '进度',
|
|
|
slot: 'progress',
|
|
|
width: 200
|
|
|
},
|
|
|
{
|
|
|
title: '操作',
|
|
|
slot: 'action',
|
|
|
width: 280
|
|
|
}
|
|
|
],
|
|
|
|
|
|
listSearch: {
|
|
|
keyword: '',
|
|
|
status: '',
|
|
|
dateRange: [],
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10
|
|
|
},
|
|
|
inventoryList: [],
|
|
|
listTotal: 0,
|
|
|
listLoading: false,
|
|
|
historyTab: 'plan',
|
|
|
historyPlanSearch: {
|
|
|
keyword: '',
|
|
|
dateRange: [],
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10
|
|
|
},
|
|
|
historyPlanList: [],
|
|
|
historyPlanTotal: 0,
|
|
|
historyPlanLoading: 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: [],
|
|
|
type: 1, // 默认为日常检查
|
|
|
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: '',
|
|
|
fenlei: '',
|
|
|
wuzizhuangtai: '',
|
|
|
chanquanxinxi: '',
|
|
|
chubeifangshi: '',
|
|
|
suozaicangku: '',
|
|
|
rukuriqi: '',
|
|
|
shengchanriqi: '',
|
|
|
dengjifenlei: '',
|
|
|
currentPlanId: '',
|
|
|
pageIndex: 1,
|
|
|
pageSize: 40,
|
|
|
total: 0,
|
|
|
selectedMaterialIds: new Set(),
|
|
|
selectedMaterialData: new Map(), // 存储选中物资的详细数据 {inventorys_id: {id, inventorys_id}}
|
|
|
isInitialLoad: true,
|
|
|
isUpdatingSelection: false, // 标志是否正在程序更新选中状态
|
|
|
tableKey: 0 // 用于强制重新渲染表格
|
|
|
},
|
|
|
materialList: [],
|
|
|
fenleiList: [],
|
|
|
cangkuList: [],
|
|
|
paraOptions: {
|
|
|
materials_status: [],
|
|
|
material_reserve: [],
|
|
|
materials_property: [],
|
|
|
dengjifenlei: []
|
|
|
},
|
|
|
materialTable: [
|
|
|
{
|
|
|
type: 'selection',
|
|
|
fixed: 'left',
|
|
|
selectable: (row, index) => {
|
|
|
// 根据选中状态返回是否可选
|
|
|
return true
|
|
|
},
|
|
|
reserveSelection: true // 保留选中状态
|
|
|
},
|
|
|
{
|
|
|
label: '物资信息',
|
|
|
align: 'left',
|
|
|
prop: 'wuzibianma_material_infos_wuzibianma_relation',
|
|
|
multiHd: [
|
|
|
{
|
|
|
label: '一级分类',
|
|
|
width: 120,
|
|
|
prop: 'wuzibianma_material_infos_wuzibianma_relation.material_info_type',
|
|
|
align: 'center',
|
|
|
formatter: (cell, data, value) => {
|
|
|
return value ? value.split('-')[0] : ''
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
label: '二级分类',
|
|
|
width: 120,
|
|
|
prop: 'wuzibianma_material_infos_wuzibianma_relation.material_info_type',
|
|
|
align: 'center',
|
|
|
formatter: (cell, data, value) => {
|
|
|
return value
|
|
|
? value.split('-').length > 1
|
|
|
? value.split('-')[1]
|
|
|
: value.split('-')[0]
|
|
|
: ''
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
label: '所属种类',
|
|
|
width: 120,
|
|
|
prop: 'wuzibianma_material_infos_wuzibianma_relation.fenlei_detail.name',
|
|
|
align: 'center'
|
|
|
},
|
|
|
{
|
|
|
label: '物资名称',
|
|
|
prop: 'zichanmingcheng',
|
|
|
align: 'center',
|
|
|
width: 180,
|
|
|
fixed: 'left'
|
|
|
},
|
|
|
{
|
|
|
label: '物资类型',
|
|
|
width: 120,
|
|
|
prop: 'wuzileixing',
|
|
|
align: 'center'
|
|
|
},
|
|
|
{
|
|
|
label: '物资型号',
|
|
|
prop: 'wuzibianma_material_infos_wuzibianma_relation.guigexinghao',
|
|
|
align: 'center',
|
|
|
width: 180
|
|
|
},
|
|
|
{
|
|
|
label: '物资规格',
|
|
|
prop: 'wuzibianma_material_infos_wuzibianma_relation.wuziguige',
|
|
|
align: 'center',
|
|
|
width: 180
|
|
|
},
|
|
|
{
|
|
|
label: '单位',
|
|
|
width: 120,
|
|
|
prop: 'wuzibianma_material_infos_wuzibianma_relation.jiliangdanwei',
|
|
|
align: 'center'
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
label: '库存信息',
|
|
|
prop: 'kucun',
|
|
|
align: 'left',
|
|
|
multiHd: [
|
|
|
{
|
|
|
label: '入库批次',
|
|
|
width: 120,
|
|
|
prop: 'rukupici'
|
|
|
},
|
|
|
{
|
|
|
label: '在库数量',
|
|
|
width: 120,
|
|
|
prop: 'zaikushuliang',
|
|
|
customFn: row => {
|
|
|
return (
|
|
|
<span>
|
|
|
{row.row.zaikushuliang}
|
|
|
{row.row.jiliangdanwei ? row.row.jiliangdanwei : ''}
|
|
|
</span>
|
|
|
)
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
label: '顺序号',
|
|
|
width: 180,
|
|
|
prop: 'shunxuhao',
|
|
|
customFn: row => {
|
|
|
return <span>{row.row.shunxuhao ? row.row.shunxuhao : ''}</span>
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
label: '待出库',
|
|
|
width: 120,
|
|
|
prop: 'wait_num'
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
label: '物资明细',
|
|
|
prop: 'stocks_items_info',
|
|
|
align: 'left',
|
|
|
multiHd: [
|
|
|
{
|
|
|
label: '生产日期',
|
|
|
width: 120,
|
|
|
prop: 'shengchanriqi'
|
|
|
},
|
|
|
{
|
|
|
label: '入库日期',
|
|
|
width: 120,
|
|
|
prop: 'rukuriqi'
|
|
|
},
|
|
|
{
|
|
|
label: '物资状态',
|
|
|
width: 120,
|
|
|
prop: 'wuzizhuangtai_detail.value'
|
|
|
},
|
|
|
{
|
|
|
label: '储备方式',
|
|
|
width: 120,
|
|
|
prop: 'chubeifangshi_detail.value'
|
|
|
},
|
|
|
{
|
|
|
label: '所在仓库',
|
|
|
width: 120,
|
|
|
prop: 'materialstorages.cangkumingcheng'
|
|
|
},
|
|
|
{
|
|
|
label: '所在货架',
|
|
|
width: 120,
|
|
|
prop: 'shelfs.huojiamingcheng'
|
|
|
},
|
|
|
{
|
|
|
label: '所在货架层',
|
|
|
width: 120,
|
|
|
prop: 'huojiaceng'
|
|
|
},
|
|
|
{
|
|
|
label: '等级分类',
|
|
|
width: 120,
|
|
|
prop: 'dengjifenlei_detail.value'
|
|
|
},
|
|
|
{
|
|
|
label: '产权信息',
|
|
|
width: 120,
|
|
|
prop: 'chanquanxinxi_detail.value'
|
|
|
},
|
|
|
{
|
|
|
label: '是否为固定资产',
|
|
|
width: 120,
|
|
|
prop: 'shifouweigudingzichan'
|
|
|
},
|
|
|
{
|
|
|
label: '固定资产编码',
|
|
|
width: 120,
|
|
|
prop: 'gudingzichanbianma'
|
|
|
},
|
|
|
{
|
|
|
label: '储备年限',
|
|
|
width: 120,
|
|
|
prop: 'chubeinianxian'
|
|
|
},
|
|
|
{
|
|
|
label: '存放要求',
|
|
|
width: 120,
|
|
|
prop: 'cunfangyaoqiu'
|
|
|
},
|
|
|
{
|
|
|
label: '维护要求',
|
|
|
width: 120,
|
|
|
prop: 'weihuyaoqiu'
|
|
|
},
|
|
|
{
|
|
|
label: '保养频次',
|
|
|
width: 120,
|
|
|
prop: 'equipment_maintain_config.name'
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
],
|
|
|
spanArr: [], // 存储合并规则的数组
|
|
|
position: 0, // 记录当前合并的起始位置
|
|
|
// 定义需要合并的列索引范围(从selection列开始计数)
|
|
|
mergeColumnRange: {
|
|
|
start: 1, // 基础信息分组的起始列索引
|
|
|
end: 8 // 基础信息分组的结束列索引
|
|
|
},
|
|
|
planMaterials: {
|
|
|
P202504001: ['M002', 'M004'],
|
|
|
P202505001: ['M001', 'M003', 'M006']
|
|
|
},
|
|
|
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'
|
|
|
}
|
|
|
],
|
|
|
outsideListColumns: [
|
|
|
{
|
|
|
title: '一级分类',
|
|
|
key: 'yijifenlei',
|
|
|
width: 120,
|
|
|
align: 'center',
|
|
|
render: (h, params) => {
|
|
|
if (!params.row.inventory || !params.row.inventory.wuzibianma_material_infos_wuzibianma_relation) {
|
|
|
return h('span', '-')
|
|
|
}
|
|
|
const materialInfoType = params.row.inventory.wuzibianma_material_infos_wuzibianma_relation.material_info_type
|
|
|
const value = materialInfoType ? materialInfoType.split('-')[0] : ''
|
|
|
return h('span', value || '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '二级分类',
|
|
|
key: 'erjifenlei',
|
|
|
width: 120,
|
|
|
align: 'center',
|
|
|
render: (h, params) => {
|
|
|
if (!params.row.inventory || !params.row.inventory.wuzibianma_material_infos_wuzibianma_relation) {
|
|
|
return h('span', '-')
|
|
|
}
|
|
|
const materialInfoType = params.row.inventory.wuzibianma_material_infos_wuzibianma_relation.material_info_type
|
|
|
let value = ''
|
|
|
if (materialInfoType) {
|
|
|
const parts = materialInfoType.split('-')
|
|
|
value = parts.length > 1 ? parts[1] : parts[0]
|
|
|
}
|
|
|
return h('span', value || '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '所属种类',
|
|
|
key: 'suoshuzhonglei',
|
|
|
width: 120,
|
|
|
align: 'center',
|
|
|
render: (h, params) => {
|
|
|
if (!params.row.inventory || !params.row.inventory.wuzibianma_material_infos_wuzibianma_relation) {
|
|
|
return h('span', '-')
|
|
|
}
|
|
|
const fenleiDetail = params.row.inventory.wuzibianma_material_infos_wuzibianma_relation.fenlei_detail
|
|
|
const value = fenleiDetail ? fenleiDetail.name : ''
|
|
|
return h('span', value || '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '物资名称',
|
|
|
key: 'zichanmingcheng',
|
|
|
minWidth: 150,
|
|
|
align: 'center',
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.zichanmingcheng : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '物资型号',
|
|
|
key: 'guigexinghao',
|
|
|
width: 120,
|
|
|
align: 'center',
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.guigexinghao : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '物资规格',
|
|
|
key: 'wuziguige',
|
|
|
width: 120,
|
|
|
align: 'center',
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.wuziguige : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '单位',
|
|
|
key: 'jiliangdanwei',
|
|
|
width: 80,
|
|
|
align: 'center',
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.jiliangdanwei : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '原库存数量',
|
|
|
key: 'chushishuliang',
|
|
|
width: 120,
|
|
|
align: 'center',
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.chushishuliang : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
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: '盘点人',
|
|
|
slot: 'responsible_admin',
|
|
|
key: 'responsible_admin',
|
|
|
width: 120,
|
|
|
align: 'center'
|
|
|
},
|
|
|
{
|
|
|
title: '照片',
|
|
|
slot: 'photos',
|
|
|
width: 200,
|
|
|
align: 'center'
|
|
|
},
|
|
|
{
|
|
|
title: '备注',
|
|
|
key: 'remark',
|
|
|
minWidth: 200,
|
|
|
tooltip: true,
|
|
|
align: 'center'
|
|
|
}
|
|
|
],
|
|
|
previewUrl: '',
|
|
|
planDetailModal: {
|
|
|
visible: false,
|
|
|
loading: false,
|
|
|
data: null,
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10,
|
|
|
total: 0,
|
|
|
inventoryList: []
|
|
|
},
|
|
|
planDetailColumns: [
|
|
|
{
|
|
|
title: '一级分类',
|
|
|
key: 'yijifenlei',
|
|
|
width: 120,
|
|
|
render: (h, params) => {
|
|
|
if (!params.row.inventory || !params.row.inventory.wuzibianma_material_infos_wuzibianma_relation) {
|
|
|
return h('span', '-')
|
|
|
}
|
|
|
const materialInfoType = params.row.inventory.wuzibianma_material_infos_wuzibianma_relation.material_info_type
|
|
|
const value = materialInfoType ? materialInfoType.split('-')[0] : ''
|
|
|
return h('span', value || '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '二级分类',
|
|
|
key: 'erjifenlei',
|
|
|
width: 120,
|
|
|
render: (h, params) => {
|
|
|
if (!params.row.inventory || !params.row.inventory.wuzibianma_material_infos_wuzibianma_relation) {
|
|
|
return h('span', '-')
|
|
|
}
|
|
|
const materialInfoType = params.row.inventory.wuzibianma_material_infos_wuzibianma_relation.material_info_type
|
|
|
let value = ''
|
|
|
if (materialInfoType) {
|
|
|
const parts = materialInfoType.split('-')
|
|
|
value = parts.length > 1 ? parts[1] : parts[0]
|
|
|
}
|
|
|
return h('span', value || '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '所属种类',
|
|
|
key: 'suoshuzhonglei',
|
|
|
width: 120,
|
|
|
render: (h, params) => {
|
|
|
if (!params.row.inventory || !params.row.inventory.wuzibianma_material_infos_wuzibianma_relation) {
|
|
|
return h('span', '-')
|
|
|
}
|
|
|
const fenleiDetail = params.row.inventory.wuzibianma_material_infos_wuzibianma_relation.fenlei_detail
|
|
|
const value = fenleiDetail ? fenleiDetail.name : ''
|
|
|
return h('span', value || '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '物资名称',
|
|
|
key: 'zichanmingcheng',
|
|
|
minWidth: 150,
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.zichanmingcheng : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '物资型号',
|
|
|
key: 'guigexinghao',
|
|
|
width: 120,
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.guigexinghao : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '物资规格',
|
|
|
key: 'wuziguige',
|
|
|
width: 120,
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.wuziguige : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '单位',
|
|
|
key: 'jiliangdanwei',
|
|
|
width: 80,
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.jiliangdanwei : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '原库存数量',
|
|
|
key: 'chushishuliang',
|
|
|
width: 120,
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.inventory ? params.row.inventory.chushishuliang : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '状态',
|
|
|
slot: 'status',
|
|
|
width: 100
|
|
|
},
|
|
|
{
|
|
|
title: '盘点人',
|
|
|
key: 'responsible_admin_name',
|
|
|
width: 120,
|
|
|
render: (h, params) => {
|
|
|
return h('span', params.row.responsible_admin ? params.row.responsible_admin.name : '-')
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
title: '盘点数量',
|
|
|
key: 'check_num',
|
|
|
width: 100
|
|
|
},
|
|
|
{
|
|
|
title: '盘点日期',
|
|
|
key: 'check_date',
|
|
|
width: 150
|
|
|
},
|
|
|
{
|
|
|
title: '照片',
|
|
|
slot: 'photos',
|
|
|
width: 150
|
|
|
},
|
|
|
{
|
|
|
title: '备注',
|
|
|
key: 'remark',
|
|
|
minWidth: 150,
|
|
|
tooltip: true
|
|
|
}
|
|
|
],
|
|
|
summaryModal: {
|
|
|
visible: false,
|
|
|
data: null,
|
|
|
signImageId: null,
|
|
|
signImage: null,
|
|
|
type: 1 // 默认为日常检查
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
// 监听物资列表变化,确保选中状态正确显示
|
|
|
materialList: {
|
|
|
handler(newList, oldList) {
|
|
|
console.log('materialList 发生变化,新数据长度:', newList.length)
|
|
|
this.$nextTick(() => {
|
|
|
setTimeout(() => {
|
|
|
this.updateTableSelection()
|
|
|
}, 100)
|
|
|
})
|
|
|
},
|
|
|
deep: true
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
this.searchPlans()
|
|
|
this.searchHistoryPlans()
|
|
|
this.getFenlei()
|
|
|
this.getCangku()
|
|
|
this.getData()
|
|
|
},
|
|
|
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)
|
|
|
|
|
|
// 添加 wherein 参数,使用 filter 格式
|
|
|
formData.append('filter[0][key]', 'status')
|
|
|
formData.append('filter[0][op]', 'wherein')
|
|
|
formData.append('filter[0][value][0]', '0')
|
|
|
formData.append('filter[0][value][1]', '1')
|
|
|
|
|
|
// 只添加非空的搜索条件
|
|
|
if (this.planSearch.keyword) {
|
|
|
formData.append('keyword', this.planSearch.keyword)
|
|
|
}
|
|
|
|
|
|
// 确保日期范围有效且不为空
|
|
|
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])
|
|
|
|
|
|
// 只有当两个日期都有效时才添加过滤条件
|
|
|
// filter[0] 已被 wherein 占用,所以日期范围从 filter[1] 开始
|
|
|
if (startDate && endDate) {
|
|
|
formData.append('filter[1][key]', 'start_date')
|
|
|
formData.append('filter[1][op]', 'range')
|
|
|
formData.append('filter[1][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: [],
|
|
|
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],
|
|
|
type: plan.type,
|
|
|
remark: plan.remark
|
|
|
}
|
|
|
this.planModal.visible = true
|
|
|
} else {
|
|
|
this.planModal.form = {
|
|
|
id: '',
|
|
|
name: '',
|
|
|
dateRange: [],
|
|
|
type: 1, // 默认为日常检查
|
|
|
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, type, 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] || []
|
|
|
|
|
|
// 构建参数对象
|
|
|
const params = {
|
|
|
name,
|
|
|
start_date: formatDate(dateRange[0]),
|
|
|
end_date: formatDate(dateRange[1]),
|
|
|
type,
|
|
|
material_infos_plan_links: selectedMaterialIds.map(materialId => ({
|
|
|
id: null, // 新建计划时都是新的关联记录
|
|
|
inventorys_id: materialId
|
|
|
}))
|
|
|
}
|
|
|
|
|
|
if (remark) params.remark = remark
|
|
|
if (id) params.id = id
|
|
|
|
|
|
console.log('提交计划参数:', params)
|
|
|
|
|
|
try {
|
|
|
await saveStocktakingPlan(params)
|
|
|
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 params = {
|
|
|
page: this.listSearch.pageIndex,
|
|
|
page_size: this.listSearch.pageSize,
|
|
|
has_material_infos_plan_id: '0'
|
|
|
}
|
|
|
|
|
|
// 确保日期范围有效且不为空
|
|
|
if (
|
|
|
this.listSearch.dateRange &&
|
|
|
this.listSearch.dateRange.length === 2 &&
|
|
|
this.listSearch.dateRange[0] &&
|
|
|
this.listSearch.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.listSearch.dateRange[0])
|
|
|
const endDate = formatDate(this.listSearch.dateRange[1])
|
|
|
|
|
|
// 只有当两个日期都有效时才添加过滤条件
|
|
|
if (startDate && endDate) {
|
|
|
params['filter[0][key]'] = 'check_date'
|
|
|
params['filter[0][op]'] = 'range'
|
|
|
params['filter[0][value]'] = startDate + ',' + endDate
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const res = await getStocktakingHistoryList(qs.stringify(params))
|
|
|
if (res) {
|
|
|
// 处理返回数据,可能是 res.data 或 res.list.data
|
|
|
if (res.list && res.list.data) {
|
|
|
this.inventoryList = res.list.data
|
|
|
this.listTotal = res.list.total || res.list.data.length
|
|
|
} else if (res.data) {
|
|
|
this.inventoryList = res.data
|
|
|
this.listTotal = res.total || res.data.length
|
|
|
} else {
|
|
|
this.inventoryList = []
|
|
|
this.listTotal = 0
|
|
|
}
|
|
|
}
|
|
|
} catch (error) {
|
|
|
this.$Message.error('获取盘点列表失败')
|
|
|
console.error('获取盘点列表失败:', error)
|
|
|
} finally {
|
|
|
this.listLoading = false
|
|
|
}
|
|
|
},
|
|
|
resetListSearch() {
|
|
|
this.listSearch = {
|
|
|
keyword: '',
|
|
|
status: '',
|
|
|
dateRange: [],
|
|
|
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()
|
|
|
},
|
|
|
handleHistoryTabChange(name) {
|
|
|
if (name === 'plan') {
|
|
|
this.searchHistoryPlans()
|
|
|
} else if (name === 'outside') {
|
|
|
this.searchList()
|
|
|
}
|
|
|
},
|
|
|
async searchHistoryPlans() {
|
|
|
this.historyPlanLoading = true
|
|
|
try {
|
|
|
const formData = new FormData()
|
|
|
formData.append('page', this.historyPlanSearch.pageIndex)
|
|
|
formData.append('page_size', this.historyPlanSearch.pageSize)
|
|
|
|
|
|
// 添加状态过滤 status:2
|
|
|
formData.append('filter[0][key]', 'status')
|
|
|
formData.append('filter[0][op]', 'eq')
|
|
|
formData.append('filter[0][value]', '2')
|
|
|
|
|
|
// 只添加非空的搜索条件
|
|
|
if (this.historyPlanSearch.keyword) {
|
|
|
formData.append('keyword', this.historyPlanSearch.keyword)
|
|
|
}
|
|
|
|
|
|
// 确保日期范围有效且不为空
|
|
|
if (
|
|
|
this.historyPlanSearch.dateRange &&
|
|
|
this.historyPlanSearch.dateRange.length === 2 &&
|
|
|
this.historyPlanSearch.dateRange[0] &&
|
|
|
this.historyPlanSearch.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.historyPlanSearch.dateRange[0])
|
|
|
const endDate = formatDate(this.historyPlanSearch.dateRange[1])
|
|
|
|
|
|
// 只有当两个日期都有效时才添加过滤条件
|
|
|
if (startDate && endDate) {
|
|
|
const filterIndex = this.historyPlanSearch.keyword ? 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.historyPlanList = res.list.data
|
|
|
this.historyPlanTotal = res.list.total
|
|
|
}
|
|
|
} catch (error) {
|
|
|
this.$Message.error('获取历史盘点计划列表失败')
|
|
|
console.error('获取历史盘点计划列表失败:', error)
|
|
|
} finally {
|
|
|
this.historyPlanLoading = false
|
|
|
}
|
|
|
},
|
|
|
resetHistoryPlanSearch() {
|
|
|
this.historyPlanSearch = {
|
|
|
keyword: '',
|
|
|
dateRange: [],
|
|
|
pageIndex: 1,
|
|
|
pageSize: 10
|
|
|
}
|
|
|
this.searchHistoryPlans()
|
|
|
},
|
|
|
handleHistoryPlanPageChange(page) {
|
|
|
this.historyPlanSearch.pageIndex = page
|
|
|
this.searchHistoryPlans()
|
|
|
},
|
|
|
handleHistoryPlanPageSizeChange(size) {
|
|
|
this.historyPlanSearch.pageSize = size
|
|
|
this.historyPlanSearch.pageIndex = 1
|
|
|
this.searchHistoryPlans()
|
|
|
},
|
|
|
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
|
|
|
}
|
|
|
},
|
|
|
|
|
|
async showInventorySelectModal(planId) {
|
|
|
this.materialModal.currentPlanId = planId
|
|
|
this.materialModal.visible = true
|
|
|
this.materialModal.pageIndex = 1
|
|
|
this.materialModal.selectedMaterialIds = new Set()
|
|
|
|
|
|
// 获取当前计划的已关联物资ID
|
|
|
try {
|
|
|
const formData = new FormData()
|
|
|
formData.append('page', 1)
|
|
|
formData.append('page_size', 9999) // 获取所有关联数据
|
|
|
formData.append('filter[0][key]', 'material_infos_plan_id')
|
|
|
formData.append('filter[0][op]', 'eq')
|
|
|
formData.append('filter[0][value]', planId)
|
|
|
|
|
|
const res = await getStocktakingPlanLinkList(formData)
|
|
|
if (res && res.list && res.list.data) {
|
|
|
// 从关联数据中提取已选中的 inventorys_id
|
|
|
console.log('API返回的关联数据:', res.list.data)
|
|
|
res.list.data.forEach(link => {
|
|
|
if (link.inventorys_id) {
|
|
|
// 确保ID类型一致,转换为字符串
|
|
|
const inventorysId = String(link.inventorys_id)
|
|
|
this.materialModal.selectedMaterialIds.add(inventorysId)
|
|
|
// 保存关联记录的详细数据
|
|
|
this.materialModal.selectedMaterialData.set(inventorysId, {
|
|
|
id: link.id, // 关联记录的ID
|
|
|
inventorys_id: link.inventorys_id
|
|
|
})
|
|
|
console.log('添加选中ID:', inventorysId, '关联记录ID:', link.id)
|
|
|
}
|
|
|
})
|
|
|
console.log('已选中的物资ID:', this.materialModal.selectedMaterialIds)
|
|
|
console.log('选中物资的关联数据:', this.materialModal.selectedMaterialData)
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('获取计划关联数据失败:', error)
|
|
|
}
|
|
|
|
|
|
this.searchMaterials()
|
|
|
},
|
|
|
isSelected(item) {
|
|
|
const itemId = String(item.id)
|
|
|
const selected = this.materialModal.selectedMaterialIds.has(itemId)
|
|
|
console.log(
|
|
|
`检查物资 ${item.id} (类型: ${typeof item.id}, 转换后: ${itemId}) 是否选中:`,
|
|
|
selected
|
|
|
)
|
|
|
console.log('当前选中集合:', this.materialModal.selectedMaterialIds)
|
|
|
return selected
|
|
|
},
|
|
|
async toggleMaterialSelection(item, index) {
|
|
|
console.log('toggleMaterialSelection called', item, index)
|
|
|
const itemId = String(item.id)
|
|
|
|
|
|
// 更新选中状态
|
|
|
if (!this.isSelected(item)) {
|
|
|
console.log('Adding to plan:', itemId)
|
|
|
this.materialModal.selectedMaterialIds.add(itemId)
|
|
|
// 新添加的物资没有关联记录ID
|
|
|
this.materialModal.selectedMaterialData.set(itemId, {
|
|
|
id: null, // 新添加的没有ID
|
|
|
inventorys_id: item.id
|
|
|
})
|
|
|
item.selected = true
|
|
|
} else {
|
|
|
console.log('Removing from plan:', itemId)
|
|
|
|
|
|
// 检查是否是之前已选择的(有关联记录ID)
|
|
|
const materialData = this.materialModal.selectedMaterialData.get(itemId)
|
|
|
if (materialData && materialData.id) {
|
|
|
// 有关联记录ID,需要确认删除
|
|
|
const confirmed = await this.$confirm('确认要将此物资移出计划吗?', '确认移出', {
|
|
|
confirmButtonText: '确定',
|
|
|
cancelButtonText: '取消',
|
|
|
type: 'warning'
|
|
|
})
|
|
|
.then(() => true)
|
|
|
.catch(() => false)
|
|
|
|
|
|
if (confirmed) {
|
|
|
try {
|
|
|
// 调用删除接口
|
|
|
await deleteStocktakingPlanLink({ id: materialData.id })
|
|
|
this.$Message.success('已移出计划')
|
|
|
|
|
|
// 从选中状态中移除
|
|
|
this.materialModal.selectedMaterialIds.delete(itemId)
|
|
|
this.materialModal.selectedMaterialData.delete(itemId)
|
|
|
item.selected = false
|
|
|
} catch (error) {
|
|
|
this.$Message.error('移出计划失败')
|
|
|
console.error('删除关联记录失败:', error)
|
|
|
return // 如果删除失败,不更新界面状态
|
|
|
}
|
|
|
} else {
|
|
|
// 用户取消了删除操作,不做任何处理
|
|
|
return
|
|
|
}
|
|
|
} else {
|
|
|
// 没有关联记录ID,直接移除
|
|
|
this.materialModal.selectedMaterialIds.delete(itemId)
|
|
|
this.materialModal.selectedMaterialData.delete(itemId)
|
|
|
item.selected = false
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 更新表格选中状态
|
|
|
this.$nextTick(() => {
|
|
|
this.updateTableSelection()
|
|
|
})
|
|
|
},
|
|
|
async searchMaterials() {
|
|
|
this.materialModal.loading = true
|
|
|
this.materialModal.isInitialLoad = true // 重置标志
|
|
|
try {
|
|
|
const res = await index({
|
|
|
page_size: this.materialModal.pageSize,
|
|
|
page: this.materialModal.pageIndex,
|
|
|
table_name: 'inventorys',
|
|
|
sort_type: 'DESC',
|
|
|
sort_name: 'wuzibianma',
|
|
|
fenlei: this.materialModal.fenlei,
|
|
|
wuzizhuangtai: this.materialModal.wuzizhuangtai,
|
|
|
chanquanxinxi: this.materialModal.chanquanxinxi,
|
|
|
chubeifangshi: this.materialModal.chubeifangshi,
|
|
|
suozaicangku: this.materialModal.suozaicangku,
|
|
|
start_rukuriqi: this.materialModal.rukuriqi ? this.materialModal.rukuriqi[0] : '',
|
|
|
end_rukuriqi: this.materialModal.rukuriqi ? this.materialModal.rukuriqi[1] : '',
|
|
|
start_shengchanriqi: this.materialModal.shengchanriqi
|
|
|
? this.materialModal.shengchanriqi[0]
|
|
|
: '',
|
|
|
end_shengchanriqi: this.materialModal.shengchanriqi
|
|
|
? this.materialModal.shengchanriqi[1]
|
|
|
: '',
|
|
|
dengjifenlei: this.materialModal.dengjifenlei,
|
|
|
filter: [
|
|
|
{
|
|
|
key: 'zichanmingcheng',
|
|
|
op: 'like',
|
|
|
value: this.materialModal.keyword
|
|
|
}
|
|
|
]
|
|
|
})
|
|
|
|
|
|
if (res && res.list) {
|
|
|
this.materialList = res.list.data.map(item => {
|
|
|
const itemId = String(item.id)
|
|
|
const isSelected = this.materialModal.selectedMaterialIds.has(itemId)
|
|
|
return {
|
|
|
...item,
|
|
|
selected: isSelected,
|
|
|
_checked: isSelected, // 尝试不同的选中标记
|
|
|
checked: isSelected // 再尝试另一种标记
|
|
|
}
|
|
|
})
|
|
|
this.materialModal.total = res.list.total
|
|
|
this.handleDataChange()
|
|
|
|
|
|
// 选中状态已在 showInventorySelectModal 中初始化
|
|
|
console.log('当前选中的物资ID:', this.materialModal.selectedMaterialIds)
|
|
|
console.log(
|
|
|
'物资列表数据:',
|
|
|
this.materialList.map(item => ({
|
|
|
id: item.id,
|
|
|
idType: typeof item.id,
|
|
|
selected: item.selected,
|
|
|
name: item.zichanmingcheng
|
|
|
}))
|
|
|
)
|
|
|
|
|
|
// 强制重新渲染表格
|
|
|
this.materialModal.tableKey++
|
|
|
|
|
|
// 手动设置表格的选中状态
|
|
|
this.$nextTick(() => {
|
|
|
// 延迟一点时间确保DOM完全渲染
|
|
|
setTimeout(() => {
|
|
|
this.updateTableSelection()
|
|
|
// 强制更新组件以确保按钮状态正确
|
|
|
this.$forceUpdate()
|
|
|
}, 100)
|
|
|
})
|
|
|
|
|
|
// 同时尝试通过事件触发选中状态
|
|
|
this.$nextTick(() => {
|
|
|
setTimeout(() => {
|
|
|
this.triggerSelectionByEvent()
|
|
|
}, 200)
|
|
|
})
|
|
|
|
|
|
// 最后尝试直接操作DOM
|
|
|
this.$nextTick(() => {
|
|
|
setTimeout(() => {
|
|
|
this.forceCheckboxSelection()
|
|
|
}, 400)
|
|
|
})
|
|
|
}
|
|
|
} 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()
|
|
|
},
|
|
|
resetMaterialSearch() {
|
|
|
this.materialModal.keyword = ''
|
|
|
this.materialModal.fenlei = ''
|
|
|
this.materialModal.wuzizhuangtai = ''
|
|
|
this.materialModal.chanquanxinxi = ''
|
|
|
this.materialModal.chubeifangshi = ''
|
|
|
this.materialModal.suozaicangku = ''
|
|
|
this.materialModal.rukuriqi = ''
|
|
|
this.materialModal.shengchanriqi = ''
|
|
|
this.materialModal.dengjifenlei = ''
|
|
|
this.materialModal.pageIndex = 1
|
|
|
this.searchMaterials()
|
|
|
},
|
|
|
handleMaterialPageChange(page) {
|
|
|
console.log('页面切换到:', 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
|
|
|
}
|
|
|
|
|
|
// 构建新的数据结构
|
|
|
const materialInfosPlanLinks = []
|
|
|
this.materialModal.selectedMaterialData.forEach((data, inventorysId) => {
|
|
|
materialInfosPlanLinks.push({
|
|
|
id: data.id, // 如果是新添加的为null,已存在的有ID
|
|
|
inventorys_id: data.inventorys_id
|
|
|
})
|
|
|
})
|
|
|
|
|
|
console.log('提交的数据结构:', materialInfosPlanLinks)
|
|
|
|
|
|
// 构建参数对象
|
|
|
const params = {
|
|
|
id: planId,
|
|
|
material_infos_plan_links: materialInfosPlanLinks
|
|
|
}
|
|
|
|
|
|
console.log('提交物资关联参数:', params)
|
|
|
|
|
|
try {
|
|
|
await saveStocktakingPlan(params)
|
|
|
this.$Message.success('物资关联成功')
|
|
|
this.handleMaterialModalClose()
|
|
|
this.searchPlans()
|
|
|
} catch (e) {
|
|
|
this.$Message.error('物资关联失败')
|
|
|
console.error('提交失败:', e)
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 处理物资选择弹窗关闭
|
|
|
handleMaterialModalClose() {
|
|
|
this.materialModal.visible = false
|
|
|
// 清除所有数据和状态
|
|
|
this.materialModal.currentPlanId = ''
|
|
|
this.materialModal.selectedMaterialIds = new Set()
|
|
|
this.materialModal.selectedMaterialData = new Map()
|
|
|
this.materialModal.pageIndex = 1
|
|
|
this.materialModal.keyword = ''
|
|
|
this.materialModal.fenlei = ''
|
|
|
this.materialModal.wuzizhuangtai = ''
|
|
|
this.materialModal.chanquanxinxi = ''
|
|
|
this.materialModal.chubeifangshi = ''
|
|
|
this.materialModal.suozaicangku = ''
|
|
|
this.materialModal.rukuriqi = ''
|
|
|
this.materialModal.shengchanriqi = ''
|
|
|
this.materialModal.dengjifenlei = ''
|
|
|
this.materialModal.isUpdatingSelection = false
|
|
|
this.materialModal.tableKey = 0
|
|
|
this.materialList = []
|
|
|
this.materialModal.total = 0
|
|
|
console.log('物资选择弹窗已关闭,数据已清除')
|
|
|
},
|
|
|
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 = ''
|
|
|
},
|
|
|
getPlanProgress(row) {
|
|
|
// 根据状态值判断进度
|
|
|
const status = row.status
|
|
|
if (status === 0) {
|
|
|
// 未开始
|
|
|
return 0
|
|
|
} else if (status === 1) {
|
|
|
// 进行中
|
|
|
return 50
|
|
|
} else if (status === 2) {
|
|
|
// 已完成
|
|
|
return 100
|
|
|
}
|
|
|
return 0
|
|
|
},
|
|
|
viewPlanDetail(row) {
|
|
|
this.planDetailModal.visible = true
|
|
|
this.planDetailModal.data = row
|
|
|
this.planDetailModal.pageIndex = 1
|
|
|
this.loadPlanDetailData()
|
|
|
},
|
|
|
async loadPlanDetailData() {
|
|
|
this.planDetailModal.loading = true
|
|
|
try {
|
|
|
const params = {
|
|
|
page: this.planDetailModal.pageIndex,
|
|
|
page_size: this.planDetailModal.pageSize,
|
|
|
'filter[0][key]': 'material_infos_plan_id',
|
|
|
'filter[0][op]': 'eq',
|
|
|
'filter[0][value]': this.planDetailModal.data.id
|
|
|
}
|
|
|
|
|
|
console.log('获取计划详情参数:', params)
|
|
|
const res = await getStocktakingPlanLinkList(qs.stringify(params))
|
|
|
if (res && res.list) {
|
|
|
this.planDetailModal.inventoryList = res.list.data
|
|
|
this.planDetailModal.total = res.list.total
|
|
|
console.log('计划详情数据:', res.list.data)
|
|
|
}
|
|
|
} catch (e) {
|
|
|
this.$Message.error('获取盘点物资列表失败')
|
|
|
console.error('获取计划详情失败:', e)
|
|
|
} finally {
|
|
|
this.planDetailModal.loading = false
|
|
|
}
|
|
|
},
|
|
|
handlePlanDetailPageChange(page) {
|
|
|
this.planDetailModal.pageIndex = page
|
|
|
this.loadPlanDetailData()
|
|
|
},
|
|
|
handlePlanDetailPageSizeChange(size) {
|
|
|
this.planDetailModal.pageSize = size
|
|
|
this.planDetailModal.pageIndex = 1
|
|
|
this.loadPlanDetailData()
|
|
|
},
|
|
|
showSummaryModal(row) {
|
|
|
// 从原始列表数据中查找完整的计划信息
|
|
|
console.log('this.planList', this.historyPlanList)
|
|
|
const planData = this.historyPlanList.find(plan => plan.id === row.id)
|
|
|
if (!planData) {
|
|
|
this.$Message.error('未找到计划数据')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
this.summaryModal.data = {
|
|
|
id: planData.id,
|
|
|
name: planData.name,
|
|
|
planStart: planData.start_date,
|
|
|
planEnd: planData.end_date,
|
|
|
actualStart: planData.act_start_date || '-',
|
|
|
actualEnd: planData.act_end_date || '-',
|
|
|
planCount: planData.chart_total || '-',
|
|
|
actualCount: planData.chart_done || '-'
|
|
|
}
|
|
|
this.summaryModal.signImageId = planData.sign_id || null
|
|
|
this.summaryModal.signImage = planData.sign ? planData.sign.url : null
|
|
|
this.summaryModal.visible = true
|
|
|
},
|
|
|
beforeSignUpload(file) {
|
|
|
// 可加图片类型/大小校验
|
|
|
return true
|
|
|
},
|
|
|
handleSignUpload(response) {
|
|
|
// 假设 response.url 是图片地址
|
|
|
this.summaryModal.signImageId = response.id
|
|
|
this.summaryModal.signImage = response.url
|
|
|
},
|
|
|
handleMoreCommand(command, row) {
|
|
|
if (command === 'edit') {
|
|
|
this.showPlanModal('edit', row)
|
|
|
} else if (command === 'delete') {
|
|
|
this.deletePlan(row.id)
|
|
|
} else if (command === 'view') {
|
|
|
this.viewPlanDetail(row)
|
|
|
}
|
|
|
},
|
|
|
async handleSummarySubmit() {
|
|
|
if (!this.summaryModal.signImageId) {
|
|
|
this.$Message.warning('请上传签字图片')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
const params = {
|
|
|
id: this.summaryModal.data.id,
|
|
|
sign_id: this.summaryModal.signImageId
|
|
|
}
|
|
|
|
|
|
await saveStocktakingPlan(params)
|
|
|
this.$Message.success('提交成功')
|
|
|
this.summaryModal.visible = false
|
|
|
this.searchHistoryPlans() // 刷新列表
|
|
|
} catch (error) {
|
|
|
this.$Message.error('提交失败:' + (error.message || '未知错误'))
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 获取仓库
|
|
|
async getCangku() {
|
|
|
const res = await baseFormIndex({
|
|
|
page_size: 999,
|
|
|
page: 1,
|
|
|
table_name: 'materialstorages',
|
|
|
sort_name: 'sort',
|
|
|
sort_type: 'ASC',
|
|
|
filter: [
|
|
|
{
|
|
|
key: 'shifouzili',
|
|
|
op: 'eq',
|
|
|
value: '是'
|
|
|
}
|
|
|
]
|
|
|
})
|
|
|
this.cangkuList = res.data
|
|
|
},
|
|
|
|
|
|
// 获取数据字典
|
|
|
async getData() {
|
|
|
const res = await getparameteritemMore({
|
|
|
'number[0]': 'materials_status', // 物资状态
|
|
|
'number[1]': 'material_reserve', // 储备方式
|
|
|
'number[2]': 'materials_property', // 产权信息
|
|
|
'number[3]': 'dengjifenlei' // 等级分类
|
|
|
})
|
|
|
for (var k in this.paraOptions) {
|
|
|
res.map(item => {
|
|
|
if (item.number === k) {
|
|
|
this.paraOptions[k] = item.detail
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
|
|
|
async getFenlei() {
|
|
|
const res = await getFenleilist({
|
|
|
tree: 1,
|
|
|
sort_type: 'ASC',
|
|
|
sort_name: 'sort'
|
|
|
})
|
|
|
this.fenleiList = this.removeEmptyChildren(res)
|
|
|
},
|
|
|
|
|
|
changeFenlei(e, row) {
|
|
|
console.log('e', e)
|
|
|
if (e) {
|
|
|
this.materialModal.fenlei = e[e.length - 1]
|
|
|
} else {
|
|
|
this.materialModal.fenlei = ''
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 移除children=[]
|
|
|
removeEmptyChildren(node) {
|
|
|
if (Array.isArray(node)) {
|
|
|
return node.map(child => {
|
|
|
if (child.children) {
|
|
|
// 递归处理子节点
|
|
|
child.children = this.removeEmptyChildren(child.children)
|
|
|
// 若处理后 children 为空,删除该属性
|
|
|
if (child.children.length === 0) {
|
|
|
delete child.children
|
|
|
}
|
|
|
}
|
|
|
return child
|
|
|
})
|
|
|
}
|
|
|
return []
|
|
|
},
|
|
|
|
|
|
// filterSpans
|
|
|
getSpanArr(data) {
|
|
|
// 生成合并规则数组(保持不变)
|
|
|
this.spanArr = []
|
|
|
this.position = 0
|
|
|
|
|
|
data.forEach((item, index) => {
|
|
|
if (index === 0) {
|
|
|
this.spanArr.push(1)
|
|
|
this.position = 0
|
|
|
} else {
|
|
|
if (
|
|
|
item.wuzibianma ===
|
|
|
data[this.position].wuzibianma_material_infos_wuzibianma_relation.wuzibianma
|
|
|
) {
|
|
|
this.spanArr[this.position] += 1
|
|
|
this.spanArr.push(0)
|
|
|
} else {
|
|
|
this.spanArr.push(1)
|
|
|
this.position = index
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
|
|
|
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
|
|
|
// 通过列索引范围判断是否需要合并
|
|
|
if (columnIndex >= this.mergeColumnRange.start && columnIndex <= this.mergeColumnRange.end) {
|
|
|
const _row = this.spanArr[rowIndex]
|
|
|
const _col = _row > 0 ? 1 : 0
|
|
|
return {
|
|
|
rowspan: _row,
|
|
|
colspan: _col
|
|
|
}
|
|
|
}
|
|
|
// 其他列保持默认
|
|
|
return {
|
|
|
rowspan: 1,
|
|
|
colspan: 1
|
|
|
}
|
|
|
},
|
|
|
|
|
|
handleDataChange() {
|
|
|
this.getSpanArr(this.materialList)
|
|
|
},
|
|
|
|
|
|
selectionChange(e) {
|
|
|
console.log('selectionChange', e, '是否正在程序更新:', this.materialModal.isUpdatingSelection)
|
|
|
|
|
|
// 如果是程序更新选中状态,忽略此事件
|
|
|
if (this.materialModal.isUpdatingSelection) {
|
|
|
console.log('忽略程序触发的选中变化事件')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// 更新选中状态集合 - 支持跨页选择
|
|
|
const planId = this.materialModal.currentPlanId
|
|
|
if (planId) {
|
|
|
// 获取当前页面所有行的ID
|
|
|
const currentPageIds = this.materialList.map(item => String(item.id))
|
|
|
|
|
|
// 从选中集合中移除当前页面的所有ID
|
|
|
currentPageIds.forEach(id => {
|
|
|
this.materialModal.selectedMaterialIds.delete(id)
|
|
|
})
|
|
|
|
|
|
// 添加新选中的项(来自当前页面)
|
|
|
e.forEach(item => {
|
|
|
const itemId = String(item.id)
|
|
|
this.materialModal.selectedMaterialIds.add(itemId)
|
|
|
// 如果不存在关联数据,添加新的(表示新选中的)
|
|
|
if (!this.materialModal.selectedMaterialData.has(itemId)) {
|
|
|
this.materialModal.selectedMaterialData.set(itemId, {
|
|
|
id: null, // 新选中的没有关联记录ID
|
|
|
inventorys_id: item.id
|
|
|
})
|
|
|
}
|
|
|
console.log('通过表格选中添加ID:', itemId)
|
|
|
})
|
|
|
|
|
|
// 更新当前页面数据的selected状态
|
|
|
this.materialList.forEach(item => {
|
|
|
const itemId = String(item.id)
|
|
|
item.selected = this.materialModal.selectedMaterialIds.has(itemId)
|
|
|
})
|
|
|
|
|
|
console.log('更新后的选中集合:', this.materialModal.selectedMaterialIds)
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 为 xy-table 提供选中状态判断
|
|
|
getSelectedRows() {
|
|
|
return this.materialList.filter(item =>
|
|
|
this.materialModal.selectedMaterialIds.has(String(item.id))
|
|
|
)
|
|
|
},
|
|
|
|
|
|
// 通过事件方式触发选中状态
|
|
|
triggerSelectionByEvent() {
|
|
|
console.log('尝试通过事件方式触发选中状态')
|
|
|
const selectedRows = this.getSelectedRows()
|
|
|
console.log(
|
|
|
'应该选中的行:',
|
|
|
selectedRows.map(row => ({ id: row.id, name: row.zichanmingcheng }))
|
|
|
)
|
|
|
|
|
|
if (selectedRows.length > 0) {
|
|
|
// 模拟选中事件
|
|
|
this.materialModal.isUpdatingSelection = true
|
|
|
this.selectionChange(selectedRows)
|
|
|
setTimeout(() => {
|
|
|
this.materialModal.isUpdatingSelection = false
|
|
|
}, 100)
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 通过DOM操作直接勾选复选框
|
|
|
forceCheckboxSelection() {
|
|
|
console.log('尝试通过DOM操作直接勾选复选框')
|
|
|
this.$nextTick(() => {
|
|
|
setTimeout(() => {
|
|
|
// 查找所有复选框
|
|
|
const checkboxes = document.querySelectorAll('.el-table .el-checkbox__input')
|
|
|
console.log('找到复选框数量:', checkboxes.length)
|
|
|
|
|
|
this.materialList.forEach((row, index) => {
|
|
|
const itemId = String(row.id)
|
|
|
if (this.materialModal.selectedMaterialIds.has(itemId)) {
|
|
|
// 找到对应的复选框(跳过表头的复选框)
|
|
|
const checkbox = checkboxes[index + 1]
|
|
|
if (checkbox && !checkbox.classList.contains('is-checked')) {
|
|
|
console.log('直接点击复选框:', itemId)
|
|
|
checkbox.click()
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
}, 300)
|
|
|
})
|
|
|
},
|
|
|
|
|
|
// 更新表格选中状态
|
|
|
updateTableSelection(retryCount = 0) {
|
|
|
console.log(
|
|
|
`开始更新表格选中状态 (重试次数: ${retryCount}),当前选中ID:`,
|
|
|
this.materialModal.selectedMaterialIds
|
|
|
)
|
|
|
|
|
|
// 尝试多种方式获取表格引用
|
|
|
let table = null
|
|
|
|
|
|
if (this.$refs.materialTable) {
|
|
|
console.log('materialTable ref 存在')
|
|
|
|
|
|
// 方式1:直接通过 $refs.table
|
|
|
if (this.$refs.materialTable.$refs && this.$refs.materialTable.$refs.table) {
|
|
|
table = this.$refs.materialTable.$refs.table
|
|
|
console.log('通过 $refs.table 找到表格')
|
|
|
}
|
|
|
// 方式2:通过子组件
|
|
|
else if (
|
|
|
this.$refs.materialTable.$children &&
|
|
|
this.$refs.materialTable.$children.length > 0
|
|
|
) {
|
|
|
for (let child of this.$refs.materialTable.$children) {
|
|
|
if (child.$refs && child.$refs.table) {
|
|
|
table = child.$refs.table
|
|
|
console.log('通过子组件找到表格')
|
|
|
break
|
|
|
}
|
|
|
// 深度查找
|
|
|
if (child.$children && child.$children.length > 0) {
|
|
|
for (let grandChild of child.$children) {
|
|
|
if (grandChild.$refs && grandChild.$refs.table) {
|
|
|
table = grandChild.$refs.table
|
|
|
console.log('通过孙子组件找到表格')
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
if (table) break
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// 方式3:通过 $el 查找
|
|
|
else if (this.$refs.materialTable.$el) {
|
|
|
const tableEl = this.$refs.materialTable.$el.querySelector('.el-table')
|
|
|
if (tableEl && tableEl.__vue__) {
|
|
|
table = tableEl.__vue__
|
|
|
console.log('通过 DOM 元素找到表格')
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (table && typeof table.clearSelection === 'function') {
|
|
|
console.log('找到表格组件,开始设置选中状态')
|
|
|
|
|
|
// 设置标志,表示正在程序更新选中状态
|
|
|
this.materialModal.isUpdatingSelection = true
|
|
|
|
|
|
// 清除所有选中状态
|
|
|
table.clearSelection()
|
|
|
|
|
|
// 设置选中状态
|
|
|
let selectedCount = 0
|
|
|
this.materialList.forEach(row => {
|
|
|
const rowId = String(row.id)
|
|
|
if (this.materialModal.selectedMaterialIds.has(rowId)) {
|
|
|
console.log('设置行选中:', rowId, row.zichanmingcheng)
|
|
|
table.toggleRowSelection(row, true)
|
|
|
selectedCount++
|
|
|
}
|
|
|
})
|
|
|
|
|
|
console.log(`当前页面设置了 ${selectedCount} 行选中状态`)
|
|
|
|
|
|
// 重置标志
|
|
|
setTimeout(() => {
|
|
|
this.materialModal.isUpdatingSelection = false
|
|
|
console.log('表格选中状态更新完成,重置标志')
|
|
|
}, 200)
|
|
|
} else {
|
|
|
console.log('未找到表格组件引用或表格方法不可用')
|
|
|
|
|
|
// 最多重试3次
|
|
|
if (retryCount < 3) {
|
|
|
console.log(`将在500ms后进行第${retryCount + 1}次重试`)
|
|
|
setTimeout(() => {
|
|
|
this.updateTableSelection(retryCount + 1)
|
|
|
}, 500)
|
|
|
} else {
|
|
|
console.error('重试3次后仍未找到表格组件,放弃更新选中状态')
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</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: 16px;
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
.plan-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;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.inventory-list-section {
|
|
|
margin-bottom: 30px;
|
|
|
|
|
|
h3 {
|
|
|
margin: 0 0 15px 0;
|
|
|
font-size: 16px;
|
|
|
color: #17233d;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.summary-table {
|
|
|
width: 100%;
|
|
|
border-collapse: collapse;
|
|
|
font-size: 16px;
|
|
|
background: #fff;
|
|
|
margin-bottom: 16px;
|
|
|
}
|
|
|
.summary-table td {
|
|
|
border: 1px solid #333;
|
|
|
padding: 14px 16px;
|
|
|
min-width: 120px;
|
|
|
background: #fff;
|
|
|
color: #222;
|
|
|
font-weight: normal;
|
|
|
}
|
|
|
.summary-table .sign-cell {
|
|
|
height: 80px;
|
|
|
min-height: 80px;
|
|
|
line-height: 80px;
|
|
|
}
|
|
|
.summary-table td:first-child {
|
|
|
background: #fff;
|
|
|
color: #111;
|
|
|
font-weight: bold;
|
|
|
width: 160px;
|
|
|
}
|
|
|
|
|
|
.selector-item {
|
|
|
margin-right: 10px;
|
|
|
margin-bottom: 5px;
|
|
|
}
|
|
|
</style>
|