1424 lines
48 KiB
Vue
1424 lines
48 KiB
Vue
<template>
|
||
<div class="page-container">
|
||
<section class="panel">
|
||
<div class="list-toolbar">
|
||
<div class="list-toolbar-row db-list-toolbar-row">
|
||
<ListToolbarDateShortcuts
|
||
variant="pills"
|
||
:active="activeDateShortcut"
|
||
aria-label="日期快捷筛选"
|
||
@change="handleDateShortcut"
|
||
/>
|
||
<ListQuickSearchInput
|
||
v-model="searchKeyword"
|
||
placeholder="车牌号 / 货物 / 计划号 / 出入库单号"
|
||
@search="handleQuickSearch"
|
||
@clear="handleQuickSearchClear"
|
||
/>
|
||
<el-button class="btn-main" type="primary" @click="handleAdd">
|
||
<el-icon><Plus /></el-icon>
|
||
新增地磅单
|
||
</el-button>
|
||
<ListToolbarOverflow :items="toolbarOverflowItems">
|
||
<template #export>
|
||
<ListExportDropdown
|
||
:export-fn="(payload) => weighingApi.export(payload)"
|
||
:get-filter-params="getWeighingExportFilterParams"
|
||
:has-selection="true"
|
||
:selected-ids="selectedRows.map((r) => String(r.id))"
|
||
:visible-columns="weighingExportColumns"
|
||
filename="地磅单列表.xlsx"
|
||
/>
|
||
</template>
|
||
</ListToolbarOverflow>
|
||
|
||
<ListToolbarEndActions
|
||
:loading="loading"
|
||
:column-defs="listColumnDefs"
|
||
v-model:visible-columns="visibleColumns"
|
||
v-model:check-all="checkAll"
|
||
:is-indeterminate="isIndeterminate"
|
||
@refresh="loadData"
|
||
@reset-columns="handleResetColumns"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="list-body list-wrap">
|
||
<div ref="weighingTableWrapRef" class="table-scroll bx-list-table-wrap">
|
||
<ListTable
|
||
ref="weighingTableRef"
|
||
table-key="db-weighing"
|
||
class="list-table"
|
||
:height="tableHeight"
|
||
:data="paginatedData"
|
||
v-loading="loading"
|
||
stripe
|
||
row-key="id"
|
||
@selection-change="handleSelectionChange"
|
||
@row-dblclick="handleView"
|
||
@header-dragend="() => weighingStatsRowRef?.resync?.()"
|
||
>
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column type="index" label="序号" width="60" align="center" header-align="center" />
|
||
<el-table-column
|
||
v-if="isColumnVisible('inboundOutboundId')"
|
||
prop="inboundOutboundId"
|
||
label="出入库单ID"
|
||
min-width="100"
|
||
align="center"
|
||
header-align="center"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="{ row }">
|
||
{{ row.inboundOutboundId || '—' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('inventoryDate')"
|
||
prop="inventoryDate"
|
||
label="日期"
|
||
min-width="120"
|
||
align="center"
|
||
header-align="center"
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">日期</span>
|
||
<ColumnHeaderFilter
|
||
prop="inventoryDate"
|
||
label="日期"
|
||
type="datetime"
|
||
:is-active="isFilterActive('inventoryDate')"
|
||
:model-value="filters.inventoryDate || null"
|
||
@update:model-value="(v) => setFilterState('inventoryDate', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
<template #default="{ row }">
|
||
{{ getRecordDate(row) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('planNo')"
|
||
prop="planNo"
|
||
label="计划单号"
|
||
min-width="140"
|
||
align="center"
|
||
header-align="center"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">计划单号</span>
|
||
<ColumnHeaderFilter
|
||
prop="planNo"
|
||
label="计划单号"
|
||
type="text"
|
||
:is-active="isFilterActive('planNo')"
|
||
:model-value="filters.planNo || null"
|
||
@update:model-value="(v) => setFilterState('planNo', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="batchNo"
|
||
label="出入库单号"
|
||
min-width="140"
|
||
align="center"
|
||
header-align="center"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">出入库单号</span>
|
||
<ColumnHeaderFilter
|
||
prop="batchNo"
|
||
label="出入库单号"
|
||
type="text"
|
||
:is-active="isFilterActive('batchNo')"
|
||
:model-value="filters.batchNo || null"
|
||
@update:model-value="(v) => setFilterState('batchNo', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
<template #default="{ row }">
|
||
<el-link type="primary" @click="handleView(row)">{{ row.batchNo }}</el-link>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('vehicleNo')"
|
||
prop="vehicleNo"
|
||
label="车牌号"
|
||
min-width="120"
|
||
align="center"
|
||
header-align="center"
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">车牌号</span>
|
||
<ColumnHeaderFilter
|
||
prop="vehicleNo"
|
||
label="车牌号"
|
||
type="text"
|
||
:is-active="isFilterActive('vehicleNo')"
|
||
:model-value="filters.vehicleNo || null"
|
||
@update:model-value="(v) => setFilterState('vehicleNo', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('cargoName')"
|
||
prop="cargoName"
|
||
label="货物名称"
|
||
min-width="150"
|
||
align="center"
|
||
header-align="center"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">货物名称</span>
|
||
<ColumnHeaderFilter
|
||
prop="cargoName"
|
||
label="货物名称"
|
||
type="text"
|
||
:is-active="isFilterActive('cargoName')"
|
||
:model-value="filters.cargoName || null"
|
||
@update:model-value="(v) => setFilterState('cargoName', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('specification')"
|
||
prop="specification"
|
||
label="规格"
|
||
min-width="120"
|
||
align="center"
|
||
header-align="center"
|
||
show-overflow-tooltip
|
||
/>
|
||
<el-table-column
|
||
v-if="isColumnVisible('businessType')"
|
||
prop="businessType"
|
||
label="业务类型"
|
||
min-width="100"
|
||
align="center"
|
||
header-align="center"
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">业务类型</span>
|
||
<ColumnHeaderFilter
|
||
prop="businessType"
|
||
label="业务类型"
|
||
type="enum"
|
||
:is-active="isFilterActive('businessType')"
|
||
:model-value="filters.businessType || null"
|
||
:options="enumOptionsFor('businessType')"
|
||
@update:model-value="(v) => setFilterState('businessType', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
<template #default="{ row }">
|
||
<el-tag :type="row.businessType === '入库' ? 'success' : 'warning'" size="small">
|
||
{{ row.businessType }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('docStatus')"
|
||
label="单据状态"
|
||
min-width="100"
|
||
align="center"
|
||
header-align="center"
|
||
>
|
||
<template #default="{ row }">
|
||
<el-tag :type="row.isVoided === 1 ? 'danger' : 'success'" size="small">
|
||
{{ row.isVoided === 1 ? '已作废' : '正常' }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('supplier')"
|
||
prop="supplier"
|
||
label="供货单位"
|
||
min-width="150"
|
||
align="center"
|
||
header-align="center"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">供货单位</span>
|
||
<ColumnHeaderFilter
|
||
prop="supplier"
|
||
label="供货单位"
|
||
type="text"
|
||
:is-active="isFilterActive('supplier')"
|
||
:model-value="filters.supplier || null"
|
||
@update:model-value="(v) => setFilterState('supplier', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('consignee')"
|
||
prop="consignee"
|
||
label="收货单位"
|
||
min-width="150"
|
||
align="center"
|
||
header-align="center"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">收货单位</span>
|
||
<ColumnHeaderFilter
|
||
prop="consignee"
|
||
label="收货单位"
|
||
type="text"
|
||
:is-active="isFilterActive('consignee')"
|
||
:model-value="filters.consignee || null"
|
||
@update:model-value="(v) => setFilterState('consignee', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('currentQuantity')"
|
||
label="本次量(吨)"
|
||
min-width="110"
|
||
align="right"
|
||
header-align="center"
|
||
>
|
||
<template #default="{ row }">
|
||
{{ formatKgToTon(row.currentQuantity) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('netWeight')"
|
||
label="净重(吨)"
|
||
min-width="110"
|
||
align="right"
|
||
header-align="center"
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">净重(吨)</span>
|
||
<ColumnHeaderFilter
|
||
prop="netWeight"
|
||
label="净重(吨)"
|
||
type="number"
|
||
:is-active="isFilterActive('netWeight')"
|
||
:model-value="filters.netWeight || null"
|
||
@update:model-value="(v) => setFilterState('netWeight', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
<template #default="{ row }">
|
||
{{ formatKgToTon(row.netWeight) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('grossWeight')"
|
||
prop="grossWeight"
|
||
label="毛重(kg)"
|
||
min-width="110"
|
||
align="right"
|
||
header-align="center"
|
||
>
|
||
<template #header>
|
||
<div class="col-header-with-filter">
|
||
<span class="col-header-with-filter__label">毛重(kg)</span>
|
||
<ColumnHeaderFilter
|
||
prop="grossWeight"
|
||
label="毛重(kg)"
|
||
type="number"
|
||
:is-active="isFilterActive('grossWeight')"
|
||
:model-value="filters.grossWeight || null"
|
||
@update:model-value="(v) => setFilterState('grossWeight', v)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('grossWeightTime')"
|
||
prop="grossWeightTime"
|
||
label="毛重时间"
|
||
min-width="160"
|
||
align="center"
|
||
header-align="center"
|
||
/>
|
||
<el-table-column
|
||
v-if="isColumnVisible('tareWeight')"
|
||
prop="tareWeight"
|
||
label="皮重(kg)"
|
||
min-width="110"
|
||
align="right"
|
||
header-align="center"
|
||
/>
|
||
<el-table-column
|
||
v-if="isColumnVisible('tareWeightTime')"
|
||
prop="tareWeightTime"
|
||
label="皮重时间"
|
||
min-width="160"
|
||
align="center"
|
||
header-align="center"
|
||
/>
|
||
<el-table-column
|
||
v-if="isColumnVisible('deduction')"
|
||
prop="deduction"
|
||
label="扣杂(kg)"
|
||
min-width="110"
|
||
align="right"
|
||
header-align="center"
|
||
>
|
||
<template #default="{ row }">
|
||
{{ row.deduction || 0 }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
v-if="isColumnVisible('createByName')"
|
||
prop="createByName"
|
||
label="创建人"
|
||
min-width="100"
|
||
align="center"
|
||
header-align="center"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="{ row }">
|
||
{{ row.createByName || row.createBy || '—' }}
|
||
</template>
|
||
</el-table-column>
|
||
</ListTable>
|
||
</div>
|
||
<div ref="weighingBelowRef">
|
||
<ListColumnStatsRow
|
||
ref="weighingStatsRowRef"
|
||
storage-key="db-weighing-column-stats"
|
||
:columns="weighingStatsRowColumns"
|
||
:data="paginatedData"
|
||
:table-wrap-el="weighingTableWrapRef"
|
||
:get-cell-value="getWeighingStatCellValue"
|
||
/>
|
||
|
||
<div class="pagination list-pager-bar">
|
||
<div class="list-pager-selected">
|
||
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
|
||
</div>
|
||
|
||
<el-pagination
|
||
v-show="total > 0"
|
||
v-model:current-page="currentPage"
|
||
v-model:page-size="pageSize"
|
||
:total="total"
|
||
:page-sizes="[10, 20, 100, 500]"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
@current-change="handlePageChange"
|
||
@size-change="handleSizeChange"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<el-dialog
|
||
v-model="modalOpen"
|
||
:title="editingRecord ? '修改地磅单' : '新增地磅单'"
|
||
width="800px"
|
||
align-center
|
||
destroy-on-close
|
||
:close-on-click-modal="false"
|
||
class="dialog-form-layout"
|
||
modal-class="order-module-dialog-mask"
|
||
@close="handleModalClose"
|
||
>
|
||
<el-form
|
||
ref="formRef"
|
||
:model="formData"
|
||
:rules="formRules"
|
||
label-position="right"
|
||
label-width="100px"
|
||
class="db-dialog-form form-grid-2"
|
||
scroll-to-error
|
||
>
|
||
<el-form-item label="车牌号" prop="vehicleNo">
|
||
<PlVehicleSelect v-model="formData.vehicleNo" />
|
||
</el-form-item>
|
||
<el-form-item label="产品" prop="productId">
|
||
<DbCrmProductSelect
|
||
v-model:product-id="formData.productId"
|
||
v-model:product-name="formData.cargoName"
|
||
v-model:specification="formData.specification"
|
||
placeholder="请搜索并选择 CRM 产品"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="规格">
|
||
<el-input
|
||
v-model="formData.specification"
|
||
readonly
|
||
placeholder="选择产品后自动带出"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="业务类型" prop="businessType">
|
||
<el-radio-group v-model="formData.businessType">
|
||
<el-radio value="入库">入库</el-radio>
|
||
<el-radio value="出库">出库</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="计划号" prop="planNo">
|
||
<el-input v-model="formData.planNo" placeholder="请输入计划号" />
|
||
</el-form-item>
|
||
<el-form-item label="出入库单号" prop="batchNo">
|
||
<el-input v-model="formData.batchNo" placeholder="出入库单号" readonly />
|
||
</el-form-item>
|
||
<el-form-item label="本次量(吨)">
|
||
<el-input :value="currentQuantityInTons" placeholder="本次量(吨)" readonly />
|
||
</el-form-item>
|
||
<el-form-item label="供货单位" prop="supplier">
|
||
<DbCrmCustomerSelect
|
||
v-model="formData.supplier"
|
||
:allow-create="false"
|
||
placeholder="请从客户管理中搜索选择"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="扣杂(kg)" prop="deduction">
|
||
<el-input-number
|
||
v-model="formData.deduction"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
placeholder="请输入扣杂"
|
||
style="width: 100%"
|
||
@change="calculateNetWeight"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="收货单位" prop="consignee">
|
||
<DbCrmCustomerSelect
|
||
v-model="formData.consignee"
|
||
:allow-create="false"
|
||
placeholder="请从客户管理中搜索选择"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="毛重(kg)" prop="grossWeight">
|
||
<el-input-number
|
||
v-model="formData.grossWeight"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
placeholder="请输入毛重"
|
||
style="width: 100%"
|
||
@change="handleGrossWeightChange"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="毛重时间" prop="grossWeightTime">
|
||
<el-date-picker
|
||
v-model="formData.grossWeightTime"
|
||
type="datetime"
|
||
placeholder="选择时间"
|
||
style="width: 100%"
|
||
format="YYYY-MM-DD HH:mm:ss"
|
||
value-format="YYYY-MM-DD HH:mm:ss"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="皮重(kg)" prop="tareWeight">
|
||
<el-input-number
|
||
v-model="formData.tareWeight"
|
||
:min="0"
|
||
:precision="2"
|
||
:controls="false"
|
||
placeholder="请输入皮重"
|
||
style="width: 100%"
|
||
@change="handleTareWeightChange"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="皮重时间" prop="tareWeightTime">
|
||
<el-date-picker
|
||
v-model="formData.tareWeightTime"
|
||
type="datetime"
|
||
placeholder="选择时间"
|
||
style="width: 100%"
|
||
format="YYYY-MM-DD HH:mm:ss"
|
||
value-format="YYYY-MM-DD HH:mm:ss"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="净重(kg)">
|
||
<el-input
|
||
:value="formData.grossWeight > 0 && formData.tareWeight > 0 ? netWeight.toFixed(2) : '--'"
|
||
disabled
|
||
/>
|
||
<el-alert v-if="isNetInvalid" type="error" :closable="false" style="margin-top: 8px">
|
||
净重必须大于0
|
||
</el-alert>
|
||
</el-form-item>
|
||
<el-form-item label="备注" prop="remarks" class="form-col-span-2">
|
||
<el-input v-model="formData.remarks" type="textarea" :rows="3" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="handleModalClose">
|
||
<el-icon><Close /></el-icon>
|
||
取消
|
||
</el-button>
|
||
<el-button type="primary" :loading="saving" :disabled="isNetInvalid" @click="handleSubmit">
|
||
<el-icon><Check /></el-icon>
|
||
确定
|
||
</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<DbRelatedViewDialog
|
||
v-model:visible="relatedModalOpen"
|
||
:loading="loadingRelated"
|
||
:sections="relatedViewSections"
|
||
>
|
||
<template v-if="viewingRecord" #hero-actions>
|
||
<el-button
|
||
v-if="viewingRecord.isVoided === 0"
|
||
class="btn-main"
|
||
type="primary"
|
||
@click="handleEdit(viewingRecord)"
|
||
>
|
||
<el-icon><Edit /></el-icon>
|
||
编辑
|
||
</el-button>
|
||
<el-button
|
||
v-if="viewingRecord.isVoided === 0"
|
||
class="btn-ghost"
|
||
:disabled="!isWeighingRecordToday(viewingRecord)"
|
||
:title="isWeighingRecordToday(viewingRecord) ? '' : '仅支持作废当天的地磅单'"
|
||
@click="handleViewVoid"
|
||
>
|
||
<el-icon><Close /></el-icon>
|
||
作废
|
||
</el-button>
|
||
</template>
|
||
</DbRelatedViewDialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
/**
|
||
* UI 模式 P02 · 复杂业务列表
|
||
* 参考:/#/crm/quote · src/views/crm/quote/index.vue
|
||
*/
|
||
import {
|
||
Check,
|
||
Close,
|
||
Edit,
|
||
Plus
|
||
} from '@element-plus/icons-vue'
|
||
import DbCrmCustomerSelect from '@/components/business/db/DbCrmCustomerSelect.vue'
|
||
import ListToolbarDateShortcuts from '@/components/core/list-toolbar-date-shortcuts/index.vue'
|
||
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
|
||
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
|
||
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
|
||
import ListToolbarOverflow from '@/components/business/list-toolbar-overflow/index.vue'
|
||
import type { ListToolbarOverflowItem } from '@/components/business/list-toolbar-overflow/types'
|
||
import {
|
||
overflowClearFiltersItem,
|
||
overflowEditItem,
|
||
overflowExportPlaceholder
|
||
} from '@/utils/list-toolbar-overflow-items'
|
||
import ListColumnStatsRow from '@/components/business/list-column-stats-row/index.vue'
|
||
import ColumnHeaderFilter from '@/components/business/column-header-filter/index.vue'
|
||
import type { StatsRowColumn } from '@/utils/list-column-stats'
|
||
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
|
||
import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
|
||
import { useServerColumnFilters } from '@/composables/useServerColumnFilters'
|
||
import { useColumnSort } from '@/composables/useColumnSort'
|
||
import type { DatetimeColumnFilterState } from '@/composables/useClientColumnFilters'
|
||
import DbCrmProductSelect from '@/components/business/db/DbCrmProductSelect.vue'
|
||
import DbRelatedViewDialog from '@/components/business/db/DbRelatedViewDialog.vue'
|
||
import { buildWeighingRelatedSections } from '@/components/business/db/db-related-view'
|
||
import PlVehicleSelect from '@/components/business/pl/PlVehicleSelect.vue'
|
||
import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
|
||
import { syncViewRowFromFlatList } from '@/utils/view-dialog-refresh'
|
||
import { $modal } from '@/utils/modal'
|
||
import { inventoryApi } from '@/api/db/inventory'
|
||
import { planApi } from '@/api/db/plans'
|
||
import {
|
||
weighingApi,
|
||
type WeighingCreate,
|
||
type WeighingRecord,
|
||
type WeighingRelatedData
|
||
} from '@/api/db/weighing'
|
||
import { dbWeighingColumnFilterDefs } from './columnFilterDefs'
|
||
|
||
defineOptions({ name: 'DbWeighing' })
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
|
||
const listColumnDefs = [
|
||
{ prop: 'inboundOutboundId', label: '出入库单ID' },
|
||
{ prop: 'inventoryDate', label: '日期' },
|
||
{ prop: 'planNo', label: '计划单号' },
|
||
{ prop: 'vehicleNo', label: '车牌号' },
|
||
{ prop: 'cargoName', label: '货物名称' },
|
||
{ prop: 'specification', label: '规格' },
|
||
{ prop: 'businessType', label: '业务类型' },
|
||
{ prop: 'docStatus', label: '单据状态' },
|
||
{ prop: 'supplier', label: '供货单位' },
|
||
{ prop: 'consignee', label: '收货单位' },
|
||
{ prop: 'currentQuantity', label: '本次量(吨)' },
|
||
{ prop: 'netWeight', label: '净重(吨)' },
|
||
{ prop: 'grossWeight', label: '毛重(kg)' },
|
||
{ prop: 'grossWeightTime', label: '毛重时间' },
|
||
{ prop: 'tareWeight', label: '皮重(kg)' },
|
||
{ prop: 'tareWeightTime', label: '皮重时间' },
|
||
{ prop: 'deduction', label: '扣杂(kg)' },
|
||
{ prop: 'createByName', label: '创建人' }
|
||
]
|
||
|
||
const {
|
||
visibleColumns,
|
||
checkAll,
|
||
isIndeterminate,
|
||
isColumnVisible,
|
||
resetToDefault
|
||
} = usePersistedListColumns('db-weighing-list-columns-v2', listColumnDefs)
|
||
|
||
const pad2 = (n: number) => String(n).padStart(2, '0')
|
||
const formatYmd = (d: Date) =>
|
||
`${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`
|
||
const formatYmdHms = (d: Date) =>
|
||
`${formatYmd(d)} ${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`
|
||
|
||
const todayYmd = () => formatYmd(new Date())
|
||
|
||
const {
|
||
filters,
|
||
activeFilterProps,
|
||
setFilterState,
|
||
clearFilter,
|
||
isFilterActive,
|
||
toQueryParams,
|
||
resetToInitial,
|
||
watchVisibleColumns
|
||
} = useServerColumnFilters(dbWeighingColumnFilterDefs, {
|
||
initialFilters: {
|
||
inventoryDate: { from: todayYmd(), to: todayYmd() }
|
||
},
|
||
onChange: () => {
|
||
syncActiveShortcutFromDateFilter()
|
||
handleSearch()
|
||
}
|
||
})
|
||
|
||
function enumOptionsFor(prop: string) {
|
||
const def = dbWeighingColumnFilterDefs.find((d) => d.prop === prop)
|
||
return (def?.enumOptions || []).map((o) => ({ ...o, count: 0 }))
|
||
}
|
||
|
||
const hasClearableColumnFilters = computed(() =>
|
||
activeFilterProps.value.some((p) => p !== 'inventoryDate')
|
||
)
|
||
|
||
function handleClearColumnFilters() {
|
||
for (const prop of [...activeFilterProps.value]) {
|
||
if (prop === 'inventoryDate') continue
|
||
clearFilter(prop)
|
||
}
|
||
}
|
||
|
||
const toolbarOverflowItems = computed<ListToolbarOverflowItem[]>(() => [
|
||
overflowEditItem({
|
||
disabled: selectedRows.value.length !== 1 || selectedRows.value[0]?.isVoided === 1,
|
||
onClick: () => handleToolbarEdit()
|
||
}),
|
||
{
|
||
key: 'void',
|
||
label: '作废',
|
||
icon: Close,
|
||
disabled:
|
||
selectedRows.value.length !== 1 ||
|
||
selectedRows.value[0]?.isVoided === 1 ||
|
||
!isWeighingRecordToday(selectedRows.value[0]),
|
||
onClick: () => handleToolbarVoid()
|
||
},
|
||
overflowExportPlaceholder(),
|
||
overflowClearFiltersItem(hasClearableColumnFilters.value, handleClearColumnFilters)
|
||
])
|
||
|
||
function handleResetColumns() {
|
||
resetToDefault()
|
||
resetToInitial()
|
||
activeDateShortcut.value = 'today'
|
||
}
|
||
|
||
const visibleFilterProps = computed(() => {
|
||
const props: string[] = ['batchNo']
|
||
for (const def of listColumnDefs) {
|
||
if (isColumnVisible(def.prop)) props.push(def.prop)
|
||
}
|
||
return props
|
||
})
|
||
watchVisibleColumns(visibleFilterProps)
|
||
|
||
const weighingExportColumns = computed(() => {
|
||
const cols = [...visibleColumns.value]
|
||
if (!cols.includes('planNo')) cols.unshift('planNo')
|
||
return cols
|
||
})
|
||
|
||
function getWeighingExportFilterParams() {
|
||
return { ...buildListQueryParams() }
|
||
}
|
||
|
||
|
||
const weighingTableWrapRef = ref<HTMLElement | null>(null)
|
||
const weighingBelowRef = ref<HTMLElement | null>(null)
|
||
const weighingTableRef = ref<any>(null)
|
||
const weighingStatsRowRef = ref<{ resync?: () => void } | null>(null)
|
||
|
||
const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
|
||
tableEl: weighingTableWrapRef,
|
||
pagerEl: weighingBelowRef,
|
||
tableRef: weighingTableRef
|
||
})
|
||
|
||
const weighingStatsRowColumns = computed((): StatsRowColumn[] => {
|
||
const cols: StatsRowColumn[] = [
|
||
{ key: '__selection', interactive: false },
|
||
{ key: '__index', interactive: false }
|
||
]
|
||
if (isColumnVisible('inboundOutboundId')) {
|
||
cols.push({ key: 'inboundOutboundId', interactive: true, prop: 'inboundOutboundId' })
|
||
}
|
||
if (isColumnVisible('inventoryDate')) {
|
||
cols.push({ key: 'inventoryDate', interactive: true, prop: 'inventoryDate' })
|
||
}
|
||
if (isColumnVisible('planNo')) {
|
||
cols.push({ key: 'planNo', interactive: true, prop: 'planNo' })
|
||
}
|
||
cols.push({ key: 'batchNo', interactive: true, prop: 'batchNo' })
|
||
for (const def of listColumnDefs) {
|
||
if (def.prop === 'inventoryDate' || def.prop === 'planNo' || def.prop === 'inboundOutboundId') {
|
||
continue
|
||
}
|
||
if (!isColumnVisible(def.prop)) continue
|
||
cols.push({
|
||
key: def.prop,
|
||
interactive: true,
|
||
prop: def.prop
|
||
})
|
||
}
|
||
return cols
|
||
})
|
||
|
||
const loading = ref(false)
|
||
const saving = ref(false)
|
||
const allData = ref<WeighingRecord[]>([])
|
||
const total = ref(0)
|
||
const currentPage = ref(1)
|
||
const pageSize = ref(20)
|
||
const searchKeyword = ref('')
|
||
const selectedRows = ref<WeighingRecord[]>([])
|
||
const activeDateShortcut = ref<'all' | 'today' | 'tomorrow' | 'yesterday' | 'week' | ''>('today')
|
||
|
||
const modalOpen = ref(false)
|
||
const editingRecord = ref<WeighingRecord | null>(null)
|
||
const relatedModalOpen = ref(false)
|
||
const loadingRelated = ref(false)
|
||
const relatedData = ref<WeighingRelatedData>({ plan: null, inventory: null })
|
||
const viewingRecord = ref<WeighingRecord | null>(null)
|
||
|
||
async function refreshRelatedViewIfOpen(editedId?: string) {
|
||
if (!relatedModalOpen.value || !viewingRecord.value?.id) return
|
||
if (editedId && viewingRecord.value.id !== editedId) return
|
||
syncViewRowFromFlatList(viewingRecord, allData.value)
|
||
relatedData.value = await weighingApi.getRelated(viewingRecord.value.id)
|
||
}
|
||
|
||
const relatedViewSections = computed(() =>
|
||
buildWeighingRelatedSections(relatedData.value, viewingRecord.value)
|
||
)
|
||
|
||
const formRef = ref()
|
||
const formData = reactive<
|
||
WeighingCreate & {
|
||
cargoName: string
|
||
specification: string
|
||
}
|
||
>({
|
||
vehicleNo: '',
|
||
planNo: '',
|
||
productId: '',
|
||
cargoName: '',
|
||
specification: '',
|
||
businessType: '入库',
|
||
batchNo: '',
|
||
supplier: '',
|
||
consignee: '',
|
||
grossWeight: 0,
|
||
grossWeightTime: '',
|
||
tareWeight: 0,
|
||
tareWeightTime: '',
|
||
deduction: 0,
|
||
netWeight: 0,
|
||
remarks: '',
|
||
purchasePlanId: undefined,
|
||
inboundOutboundId: undefined,
|
||
weighingPoint: '山峡化工',
|
||
plannedQuantity: undefined,
|
||
currentQuantity: undefined,
|
||
executedQuantity: undefined
|
||
})
|
||
|
||
const formRules = {
|
||
vehicleNo: [{ required: true, message: '请选择车牌号', trigger: 'change' }],
|
||
productId: [{ required: true, message: '请选择 CRM 产品', trigger: 'change' }],
|
||
supplier: [{ required: true, message: '请选择供货单位', trigger: 'change' }],
|
||
consignee: [{ required: true, message: '请选择收货单位', trigger: 'change' }]
|
||
}
|
||
|
||
const netWeight = computed(() => {
|
||
const gross = formData.grossWeight ?? 0
|
||
const tare = formData.tareWeight ?? 0
|
||
const deduction = formData.deduction ?? 0
|
||
return Number((gross - tare - deduction).toFixed(2))
|
||
})
|
||
|
||
const currentQuantityInTons = computed(() => {
|
||
if (formData.currentQuantity !== undefined && formData.currentQuantity !== null) {
|
||
return (formData.currentQuantity / 1000).toFixed(2)
|
||
}
|
||
return '--'
|
||
})
|
||
|
||
const isNetInvalid = computed(() => {
|
||
const hasGrossWeight = formData.grossWeight && formData.grossWeight > 0
|
||
const hasTareWeight = formData.tareWeight && formData.tareWeight > 0
|
||
if (hasGrossWeight && hasTareWeight) {
|
||
return netWeight.value <= 0
|
||
}
|
||
return false
|
||
})
|
||
|
||
const paginatedData = computed(() => {
|
||
const start = (currentPage.value - 1) * pageSize.value
|
||
const end = start + pageSize.value
|
||
return allData.value.slice(start, end)
|
||
})
|
||
|
||
const formatKgToTon = (kg: number | undefined | null) => {
|
||
if (kg === undefined || kg === null) return '--'
|
||
return (kg / 1000).toFixed(2)
|
||
}
|
||
|
||
const getRecordDate = (row: WeighingRecord) =>
|
||
row.inventoryDate ||
|
||
(row.grossWeightTime ? row.grossWeightTime.split(' ')[0] : '') ||
|
||
(row.createdAt ? row.createdAt.split(' ')[0] : '') ||
|
||
''
|
||
|
||
const {
|
||
sortField,
|
||
sortOrder,
|
||
setSort,
|
||
clearSort,
|
||
isSortActive,
|
||
toQueryParams: toSortQueryParams,
|
||
watchVisibleColumns: watchSortVisibleColumns
|
||
} = useColumnSort({
|
||
onChange: () => {
|
||
currentPage.value = 1
|
||
allData.value = sortWeighingRows(allData.value)
|
||
}
|
||
})
|
||
|
||
provide('crmColumnSort', {
|
||
setSort,
|
||
clearSort,
|
||
isSortActive,
|
||
sortField,
|
||
sortOrder
|
||
})
|
||
|
||
watchSortVisibleColumns(visibleFilterProps)
|
||
|
||
function sortWeighingRows(rows: WeighingRecord[]) {
|
||
const order = sortOrder.value || 'desc'
|
||
return [...rows].sort((a, b) => {
|
||
const av = getRecordDate(a)
|
||
const bv = getRecordDate(b)
|
||
if (!av && !bv) return 0
|
||
if (!av) return order === 'asc' ? -1 : 1
|
||
if (!bv) return order === 'asc' ? 1 : -1
|
||
const cmp = av.localeCompare(bv)
|
||
return order === 'asc' ? cmp : -cmp
|
||
})
|
||
}
|
||
|
||
function getWeighingStatCellValue(row: WeighingRecord, prop: string): unknown {
|
||
if (prop === 'inventoryDate') return getRecordDate(row)
|
||
if (prop === 'currentQuantity' || prop === 'netWeight') {
|
||
return (Number((row as any)[prop]) || 0) / 1000
|
||
}
|
||
if (prop === 'docStatus') {
|
||
return row.isVoided === 1 ? '已作废' : '正常'
|
||
}
|
||
if (prop === 'createByName') {
|
||
return row.createByName || row.createBy || ''
|
||
}
|
||
return (row as any)?.[prop]
|
||
}
|
||
|
||
const buildListQueryParams = (): Record<string, any> => {
|
||
const params: Record<string, any> = { ...toQueryParams(), ...toSortQueryParams() }
|
||
const keyword = searchKeyword.value.trim()
|
||
if (keyword) params.q = keyword
|
||
|
||
// UI 净重列单位为吨,库字段为 kg
|
||
for (const key of ['netWeight', 'netWeightMin', 'netWeightMax'] as const) {
|
||
if (params[key] != null && params[key] !== '') {
|
||
const n = Number(params[key])
|
||
if (!Number.isNaN(n)) params[key] = String(n * 1000)
|
||
}
|
||
}
|
||
return params
|
||
}
|
||
|
||
function syncActiveShortcutFromDateFilter() {
|
||
const s = filters.inventoryDate as DatetimeColumnFilterState | undefined
|
||
if (!s?.from || !s?.to) {
|
||
activeDateShortcut.value = !s?.from && !s?.to ? 'all' : ''
|
||
return
|
||
}
|
||
const today = new Date()
|
||
const todayStr = formatYmd(today)
|
||
if (s.from === todayStr && s.to === todayStr) {
|
||
activeDateShortcut.value = 'today'
|
||
return
|
||
}
|
||
const tomorrow = new Date(today)
|
||
tomorrow.setDate(today.getDate() + 1)
|
||
const tomorrowStr = formatYmd(tomorrow)
|
||
if (s.from === tomorrowStr && s.to === tomorrowStr) {
|
||
activeDateShortcut.value = 'tomorrow'
|
||
return
|
||
}
|
||
const yesterday = new Date(today)
|
||
yesterday.setDate(today.getDate() - 1)
|
||
const yesterdayStr = formatYmd(yesterday)
|
||
if (s.from === yesterdayStr && s.to === yesterdayStr) {
|
||
activeDateShortcut.value = 'yesterday'
|
||
return
|
||
}
|
||
const weekday = today.getDay() === 0 ? 7 : today.getDay()
|
||
const monday = new Date(today)
|
||
monday.setDate(today.getDate() - (weekday - 1))
|
||
const sunday = new Date(monday)
|
||
sunday.setDate(monday.getDate() + 6)
|
||
if (s.from === formatYmd(monday) && s.to === formatYmd(sunday)) {
|
||
activeDateShortcut.value = 'week'
|
||
return
|
||
}
|
||
activeDateShortcut.value = ''
|
||
}
|
||
|
||
const calculateNetWeight = () => {
|
||
formData.netWeight = netWeight.value
|
||
}
|
||
|
||
const handleGrossWeightChange = () => {
|
||
calculateNetWeight()
|
||
if (formData.grossWeight && formData.grossWeight > 0 && !formData.grossWeightTime) {
|
||
formData.grossWeightTime = formatYmdHms(new Date())
|
||
}
|
||
}
|
||
|
||
const handleTareWeightChange = () => {
|
||
calculateNetWeight()
|
||
if (formData.tareWeight && formData.tareWeight > 0 && !formData.tareWeightTime) {
|
||
formData.tareWeightTime = formatYmdHms(new Date())
|
||
}
|
||
}
|
||
|
||
const loadData = async () => {
|
||
loading.value = true
|
||
try {
|
||
const records = await weighingApi.list(buildListQueryParams())
|
||
|
||
allData.value = sortWeighingRows(records)
|
||
total.value = allData.value.length
|
||
|
||
const maxPage = Math.ceil(total.value / pageSize.value) || 1
|
||
if (currentPage.value > maxPage) {
|
||
currentPage.value = 1
|
||
}
|
||
} catch (error) {
|
||
console.error('加载地磅单列表失败', error)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
const handleSearch = () => {
|
||
currentPage.value = 1
|
||
loadData()
|
||
}
|
||
|
||
const handleQuickSearch = () => {
|
||
handleSearch()
|
||
}
|
||
|
||
const handleQuickSearchClear = () => {
|
||
searchKeyword.value = ''
|
||
handleQuickSearch()
|
||
}
|
||
|
||
const handleSelectionChange = (selection: WeighingRecord[]) => {
|
||
selectedRows.value = selection
|
||
}
|
||
|
||
const handleDateShortcut = (type: 'all' | 'today' | 'tomorrow' | 'yesterday' | 'week') => {
|
||
activeDateShortcut.value = type
|
||
const today = new Date()
|
||
|
||
if (type === 'all') {
|
||
setFilterState('inventoryDate', null)
|
||
return
|
||
}
|
||
if (type === 'today') {
|
||
const d = formatYmd(today)
|
||
setFilterState('inventoryDate', { from: d, to: d })
|
||
return
|
||
}
|
||
if (type === 'tomorrow') {
|
||
const t = new Date(today)
|
||
t.setDate(t.getDate() + 1)
|
||
const d = formatYmd(t)
|
||
setFilterState('inventoryDate', { from: d, to: d })
|
||
return
|
||
}
|
||
if (type === 'yesterday') {
|
||
const y = new Date(today)
|
||
y.setDate(y.getDate() - 1)
|
||
const d = formatYmd(y)
|
||
setFilterState('inventoryDate', { from: d, to: d })
|
||
return
|
||
}
|
||
if (type === 'week') {
|
||
const weekday = today.getDay() === 0 ? 7 : today.getDay()
|
||
const monday = new Date(today)
|
||
monday.setDate(today.getDate() - (weekday - 1))
|
||
const sunday = new Date(monday)
|
||
sunday.setDate(monday.getDate() + 6)
|
||
setFilterState('inventoryDate', { from: formatYmd(monday), to: formatYmd(sunday) })
|
||
}
|
||
}
|
||
|
||
const handlePageChange = (page: number) => {
|
||
currentPage.value = page
|
||
}
|
||
|
||
const handleSizeChange = (size: number) => {
|
||
pageSize.value = size
|
||
currentPage.value = 1
|
||
}
|
||
|
||
const resetFormData = () => {
|
||
Object.assign(formData, {
|
||
vehicleNo: '',
|
||
planNo: '',
|
||
productId: '',
|
||
cargoName: '',
|
||
specification: '',
|
||
businessType: '入库',
|
||
batchNo: '',
|
||
supplier: '',
|
||
consignee: '',
|
||
grossWeight: 0,
|
||
grossWeightTime: '',
|
||
tareWeight: 0,
|
||
tareWeightTime: '',
|
||
deduction: 0,
|
||
netWeight: 0,
|
||
remarks: '',
|
||
purchasePlanId: undefined,
|
||
inboundOutboundId: undefined,
|
||
weighingPoint: '山峡化工',
|
||
plannedQuantity: undefined,
|
||
currentQuantity: undefined,
|
||
executedQuantity: undefined
|
||
})
|
||
}
|
||
|
||
const handleAdd = () => {
|
||
editingRecord.value = null
|
||
resetFormData()
|
||
modalOpen.value = true
|
||
}
|
||
|
||
const handleView = async (record: WeighingRecord) => {
|
||
viewingRecord.value = record
|
||
loadingRelated.value = true
|
||
try {
|
||
relatedData.value = await weighingApi.getRelated(record.id)
|
||
relatedModalOpen.value = true
|
||
} catch (error) {
|
||
console.error('加载关联数据失败', error)
|
||
} finally {
|
||
loadingRelated.value = false
|
||
}
|
||
}
|
||
|
||
function handleToolbarEdit() {
|
||
if (selectedRows.value.length !== 1) {
|
||
$modal.msgWarning('请先勾选一条地磅单')
|
||
return
|
||
}
|
||
handleEdit(selectedRows.value[0])
|
||
}
|
||
|
||
function handleToolbarVoid() {
|
||
if (selectedRows.value.length !== 1) {
|
||
$modal.msgWarning('请先勾选一条地磅单')
|
||
return
|
||
}
|
||
handleVoid(selectedRows.value[0])
|
||
}
|
||
|
||
async function handleViewVoid() {
|
||
if (!viewingRecord.value) return
|
||
await handleVoid(viewingRecord.value)
|
||
relatedModalOpen.value = false
|
||
}
|
||
|
||
const handleEdit = (record: WeighingRecord) => {
|
||
editingRecord.value = record
|
||
Object.assign(formData, {
|
||
vehicleNo: record.vehicleNo,
|
||
planNo: record.planNo || '',
|
||
productId: record.productId || '',
|
||
cargoName: record.cargoName,
|
||
specification: record.specification || '',
|
||
businessType: record.businessType,
|
||
batchNo: record.batchNo || '',
|
||
supplier: record.supplier,
|
||
consignee: record.consignee,
|
||
grossWeight: record.grossWeight,
|
||
grossWeightTime: record.grossWeightTime,
|
||
tareWeight: record.tareWeight,
|
||
tareWeightTime: record.tareWeightTime,
|
||
deduction: record.deduction || 0,
|
||
netWeight: record.netWeight,
|
||
remarks: record.remarks || '',
|
||
purchasePlanId: record.purchasePlanId,
|
||
inboundOutboundId: record.inboundOutboundId,
|
||
weighingPoint: record.weighingPoint || '山峡化工',
|
||
plannedQuantity: record.plannedQuantity,
|
||
currentQuantity: record.currentQuantity,
|
||
executedQuantity: record.executedQuantity
|
||
})
|
||
modalOpen.value = true
|
||
}
|
||
|
||
const isWeighingRecordToday = (row: WeighingRecord) => {
|
||
return getRecordDate(row) === formatYmd(new Date())
|
||
}
|
||
|
||
const handleVoid = async (row: WeighingRecord) => {
|
||
if (!isWeighingRecordToday(row)) {
|
||
$modal.msgWarning('仅支持作废当天的地磅单,历史数据不可作废')
|
||
return
|
||
}
|
||
try {
|
||
const { promptOptionalCancelReason } = await import('@/utils/prompt-cancel-reason')
|
||
const reason = await promptOptionalCancelReason({
|
||
title: '作废地磅单',
|
||
message: '确定要作废这条地磅单吗?作废原因选填。'
|
||
})
|
||
if (reason === null) return
|
||
await weighingApi.void(row.id, { voidReason: reason || undefined })
|
||
$modal.msgSuccess('作废成功')
|
||
loadData()
|
||
} catch {
|
||
// 用户取消或请求失败
|
||
}
|
||
}
|
||
|
||
const handleSubmit = async () => {
|
||
if (!formRef.value || isNetInvalid.value) return
|
||
|
||
await formRef.value.validate(async (valid: boolean) => {
|
||
if (!valid) return
|
||
saving.value = true
|
||
try {
|
||
let editedId: string | undefined
|
||
const hasGrossWeight = formData.grossWeight && formData.grossWeight > 0
|
||
const hasTareWeight = formData.tareWeight && formData.tareWeight > 0
|
||
if (hasGrossWeight && hasTareWeight) {
|
||
formData.netWeight = netWeight.value
|
||
} else {
|
||
formData.netWeight = 0
|
||
}
|
||
|
||
const submitData: WeighingCreate = {
|
||
vehicleNo: formData.vehicleNo,
|
||
planNo: formData.planNo || undefined,
|
||
productId: formData.productId,
|
||
businessType: formData.businessType,
|
||
batchNo: formData.batchNo || undefined,
|
||
supplier: formData.supplier,
|
||
consignee: formData.consignee,
|
||
grossWeight: formData.grossWeight ?? 0,
|
||
grossWeightTime: formData.grossWeightTime || '',
|
||
tareWeight: formData.tareWeight ?? 0,
|
||
tareWeightTime: formData.tareWeightTime || '',
|
||
deduction: formData.deduction ?? 0,
|
||
netWeight: formData.netWeight,
|
||
plannedQuantity: formData.plannedQuantity || undefined,
|
||
executedQuantity: formData.executedQuantity || undefined,
|
||
purchasePlanId: formData.purchasePlanId || undefined,
|
||
inboundOutboundId: formData.inboundOutboundId || undefined,
|
||
weighingPoint: formData.weighingPoint || '山峡化工',
|
||
remarks: formData.remarks || undefined,
|
||
currentQuantity: formData.currentQuantity || undefined
|
||
}
|
||
|
||
if (editingRecord.value) {
|
||
editedId = editingRecord.value.id
|
||
await weighingApi.update(editingRecord.value.id, {
|
||
productId: formData.productId,
|
||
vehicleNo: submitData.vehicleNo,
|
||
planNo: submitData.planNo,
|
||
businessType: submitData.businessType,
|
||
batchNo: submitData.batchNo,
|
||
supplier: submitData.supplier,
|
||
consignee: submitData.consignee,
|
||
grossWeight: submitData.grossWeight,
|
||
grossWeightTime: submitData.grossWeightTime,
|
||
tareWeight: submitData.tareWeight,
|
||
tareWeightTime: submitData.tareWeightTime,
|
||
deduction: submitData.deduction,
|
||
netWeight: submitData.netWeight,
|
||
plannedQuantity: submitData.plannedQuantity,
|
||
executedQuantity: submitData.executedQuantity,
|
||
purchasePlanId: submitData.purchasePlanId,
|
||
inboundOutboundId: submitData.inboundOutboundId,
|
||
weighingPoint: submitData.weighingPoint,
|
||
remarks: submitData.remarks,
|
||
currentQuantity: submitData.currentQuantity
|
||
})
|
||
$modal.msgSuccess('更新成功')
|
||
} else {
|
||
await weighingApi.create(submitData)
|
||
$modal.msgSuccess('保存成功')
|
||
}
|
||
modalOpen.value = false
|
||
await loadData()
|
||
if (editedId) await refreshRelatedViewIfOpen(editedId)
|
||
} catch (error) {
|
||
console.error('保存地磅单失败', error)
|
||
} finally {
|
||
saving.value = false
|
||
}
|
||
})
|
||
}
|
||
|
||
const handleModalClose = () => {
|
||
modalOpen.value = false
|
||
editingRecord.value = null
|
||
formRef.value?.resetFields()
|
||
}
|
||
|
||
|
||
const clearInventoryRouteQuery = () => {
|
||
if (!route.query.inboundOutboundId) return
|
||
router.replace({ path: route.path })
|
||
}
|
||
|
||
const openAddFromInventoryQuery = async () => {
|
||
const inboundOutboundId = route.query.inboundOutboundId as string
|
||
if (!inboundOutboundId) return
|
||
|
||
const query = { ...route.query }
|
||
|
||
try {
|
||
// 先清空上一张地磅单残留(毛/皮/净重、备注等),再填出入库带入字段
|
||
resetFormData()
|
||
editingRecord.value = null
|
||
|
||
const inventory = await inventoryApi.get(inboundOutboundId)
|
||
|
||
formData.vehicleNo = (query.vehicleNo as string) || inventory.vehicleNo
|
||
formData.productId = (query.productId as string) || inventory.productId || ''
|
||
formData.cargoName = inventory.product
|
||
formData.specification = inventory.specification || ''
|
||
const businessType = (query.businessType as '入库' | '出库') || inventory.businessType
|
||
formData.businessType = businessType
|
||
|
||
if (query.supplier) {
|
||
formData.supplier = query.supplier as string
|
||
} else {
|
||
formData.supplier =
|
||
businessType === '入库' ? inventory.partnerCompany : '安庆市山峡化工有限公司'
|
||
}
|
||
|
||
if (query.consignee) {
|
||
formData.consignee = query.consignee as string
|
||
} else {
|
||
formData.consignee =
|
||
businessType === '入库' ? '安庆市山峡化工有限公司' : inventory.partnerCompany
|
||
}
|
||
|
||
formData.planNo = (query.planNo as string) || inventory.planNo || ''
|
||
formData.batchNo = (query.batchNo as string) || inventory.batchNo || ''
|
||
formData.inboundOutboundId = inboundOutboundId
|
||
formData.weighingPoint = '山峡化工'
|
||
|
||
const quantityInTons = query.quantity ? Number(query.quantity) : inventory.quantity
|
||
formData.currentQuantity = quantityInTons * 1000
|
||
|
||
const purchasePlanId = (query.purchasePlanId as string) || inventory.purchasePlanId || ''
|
||
if (purchasePlanId) {
|
||
formData.purchasePlanId = purchasePlanId
|
||
try {
|
||
const plan = await planApi.get(purchasePlanId)
|
||
formData.plannedQuantity = plan.plannedQuantity * 1000
|
||
} catch (error) {
|
||
console.error('获取计划数据失败', error)
|
||
}
|
||
}
|
||
|
||
modalOpen.value = true
|
||
clearInventoryRouteQuery()
|
||
} catch (error) {
|
||
console.error('加载出入库单数据失败', error)
|
||
}
|
||
}
|
||
|
||
useDbInstantSearch(
|
||
() => ({
|
||
q: searchKeyword.value,
|
||
columnFilters: { ...toQueryParams() }
|
||
}),
|
||
handleSearch
|
||
)
|
||
|
||
onMounted(() => {
|
||
loadData()
|
||
// keepAlive:false 时不会触发 onActivated,须在挂载时处理出入库跳转 query
|
||
openAddFromInventoryQuery()
|
||
nextTick(() => recalcTableHeight())
|
||
})
|
||
|
||
onActivated(() => {
|
||
openAddFromInventoryQuery()
|
||
nextTick(() => recalcTableHeight())
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.bx-list-table-wrap {
|
||
min-width: 0;
|
||
}
|
||
</style>
|