feat(saledelivery): 完善销售出库单功能实现

This commit is contained in:
zxy 2026-05-18 11:08:45 +08:00
parent 590b1c9189
commit 1154f82d65
6 changed files with 94 additions and 31 deletions

View File

@ -1,13 +1,19 @@
package com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledelivery.vo; package com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledelivery.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledeliverydetail.vo.SaleDeliveryDetailSaveReqVO; import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledeliverydetail.vo.SaleDeliveryDetailSaveReqVO;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import static com.ningxia.yunxi.chemmes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - 销售出库单主新增/修改 Request VO") @Schema(description = "管理后台 - 销售出库单主新增/修改 Request VO")
@Data @Data
public class SaleDeliverySaveReqVO { public class SaleDeliverySaveReqVO {
@ -18,7 +24,10 @@ public class SaleDeliverySaveReqVO {
@Schema(description = "销售出库单号(SC+年份+月份+3位流水号)") @Schema(description = "销售出库单号(SC+年份+月份+3位流水号)")
private String saleDeliveryNo; private String saleDeliveryNo;
@Schema(description = "单据日期") @Schema(description = "单据日期", example = "2026-01-01")
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate deliveryDate; private LocalDate deliveryDate;
@Schema(description = "客户id", example = "11842") @Schema(description = "客户id", example = "11842")

View File

@ -18,30 +18,14 @@ public interface SaleDeliveryMapper extends BaseMapperX<SaleDeliveryDO> {
default PageResult<SaleDeliveryDO> selectPage(SaleDeliveryPageReqVO reqVO) { default PageResult<SaleDeliveryDO> selectPage(SaleDeliveryPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<SaleDeliveryDO>() return selectPage(reqVO, new LambdaQueryWrapperX<SaleDeliveryDO>()
.betweenIfPresent(SaleDeliveryDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(SaleDeliveryDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(SaleDeliveryDO::getSaleDeliveryNo, reqVO.getSaleDeliveryNo()) .likeIfPresent(SaleDeliveryDO::getSaleDeliveryNo, reqVO.getSaleDeliveryNo())
.betweenIfPresent(SaleDeliveryDO::getDeliveryDate, reqVO.getDeliveryDate()) .betweenIfPresent(SaleDeliveryDO::getDeliveryDate, reqVO.getDeliveryDate())
.eqIfPresent(SaleDeliveryDO::getCustId, reqVO.getCustId())
.likeIfPresent(SaleDeliveryDO::getCustName, reqVO.getCustName())
.eqIfPresent(SaleDeliveryDO::getContact, reqVO.getContact())
.eqIfPresent(SaleDeliveryDO::getConPhone, reqVO.getConPhone())
.eqIfPresent(SaleDeliveryDO::getConAddress, reqVO.getConAddress())
.eqIfPresent(SaleDeliveryDO::getDeliveryStatus, reqVO.getDeliveryStatus()) .eqIfPresent(SaleDeliveryDO::getDeliveryStatus, reqVO.getDeliveryStatus())
.eqIfPresent(SaleDeliveryDO::getDeliveryType, reqVO.getDeliveryType()) .eqIfPresent(SaleDeliveryDO::getDeliveryType, reqVO.getDeliveryType())
.eqIfPresent(SaleDeliveryDO::getRemark, reqVO.getRemark())
.eqIfPresent(SaleDeliveryDO::getSaleOrdId, reqVO.getSaleOrdId()) .eqIfPresent(SaleDeliveryDO::getSaleOrdId, reqVO.getSaleOrdId())
.eqIfPresent(SaleDeliveryDO::getSaleOrdNo, reqVO.getSaleOrdNo()) .likeIfPresent(SaleDeliveryDO::getSaleOrdNo, reqVO.getSaleOrdNo())
.eqIfPresent(SaleDeliveryDO::getDeliveryEmpId, reqVO.getDeliveryEmpId())
.likeIfPresent(SaleDeliveryDO::getDeliveryEmpName, reqVO.getDeliveryEmpName())
.eqIfPresent(SaleDeliveryDO::getTwmStorageId, reqVO.getTwmStorageId())
.eqIfPresent(SaleDeliveryDO::getMaterialId, reqVO.getMaterialId())
.eqIfPresent(SaleDeliveryDO::getMaterialCode, reqVO.getMaterialCode()) .eqIfPresent(SaleDeliveryDO::getMaterialCode, reqVO.getMaterialCode())
.likeIfPresent(SaleDeliveryDO::getMaterialName, reqVO.getMaterialName()) .likeIfPresent(SaleDeliveryDO::getMaterialName, reqVO.getMaterialName())
.eqIfPresent(SaleDeliveryDO::getSpec, reqVO.getSpec())
.eqIfPresent(SaleDeliveryDO::getUnit, reqVO.getUnit())
.eqIfPresent(SaleDeliveryDO::getOrdQty, reqVO.getOrdQty())
.eqIfPresent(SaleDeliveryDO::getRemaimQty, reqVO.getRemaimQty())
.eqIfPresent(SaleDeliveryDO::getSaleOrdDetailId, reqVO.getSaleOrdDetailId())
.eqIfPresent(SaleDeliveryDO::getDeliveriedQty, reqVO.getDeliveriedQty())
.orderByDesc(SaleDeliveryDO::getId)); .orderByDesc(SaleDeliveryDO::getId));
} }

View File

@ -93,6 +93,7 @@ declare module 'vue' {
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterSearch: typeof import('./../components/RouterSearch/index.vue')['default'] RouterSearch: typeof import('./../components/RouterSearch/index.vue')['default']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
SaleDeliveryDetail: typeof import('./../views/biz/saledelivery/SaleDeliveryDetail.vue')['default']
ScriptTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue')['default'] ScriptTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue')['default']
Search: typeof import('./../components/Search/src/Search.vue')['default'] Search: typeof import('./../components/Search/src/Search.vue')['default']
ShortcutDateRangePicker: typeof import('./../components/ShortcutDateRangePicker/index.vue')['default'] ShortcutDateRangePicker: typeof import('./../components/ShortcutDateRangePicker/index.vue')['default']

View File

@ -225,6 +225,14 @@ const handleInput = (value: string) => {
if (props.decimalPlaces === 0) { if (props.decimalPlaces === 0) {
inputValue = inputValue.replace(/\./g, '') inputValue = inputValue.replace(/\./g, '')
displayValue.value = inputValue displayValue.value = inputValue
//
if (inputValue === '') {
emit('update:modelValue', undefined)
} else {
const num = Number(inputValue)
emit('update:modelValue', isNaN(num) ? undefined : num)
}
return return
} }

View File

@ -85,9 +85,11 @@
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="发货数量" prop="deliveriedQty"> <el-form-item label="发货数量" prop="deliveriedQty">
<el-input <MoneyInput
v-model.number="formData.deliveriedQty" v-model="formData.deliveriedQty"
type="number" :decimal-places="2"
:allow-negative="false"
:show-prefix="false"
placeholder="请输入发货数量" placeholder="请输入发货数量"
/> />
</el-form-item> </el-form-item>
@ -171,12 +173,25 @@
</el-table-column> </el-table-column>
<el-table-column label="发货袋数(*)" prop="deliveriedBag" width="120px" align="center"> <el-table-column label="发货袋数(*)" prop="deliveriedBag" width="120px" align="center">
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.deliveriedBag" placeholder="请输入" @input="refreshTable" /> <MoneyInput
v-model="scope.row.deliveriedBag"
:decimal-places="2"
:allow-negative="false"
:show-prefix="false"
placeholder="请输入"
@input="refreshTable"
/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="发货数量(*)" prop="deliveriedQty" width="120px" align="center"> <el-table-column label="发货数量(*)" prop="deliveriedQty" width="120px" align="center">
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.deliveriedQty" placeholder="请输入" @input="refreshTable" /> <MoneyInput
v-model="scope.row.deliveriedQty"
:decimal-places="2"
:allow-negative="false"
:show-prefix="false"
placeholder="请输入"
/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" prop="remark" width="120px" align="center"> <el-table-column label="备注" prop="remark" width="120px" align="center">
@ -220,6 +235,7 @@ import * as CustomerApi from '@/api/biz/customer'
import { watch } from 'vue' import { watch } from 'vue'
import OrderSelectDialog from './OrderSelectDialog.vue' import OrderSelectDialog from './OrderSelectDialog.vue'
import ProStorageInventorySelectDialog from '../prostorageinventory/ProStorageInventorySelectDialog.vue' import ProStorageInventorySelectDialog from '../prostorageinventory/ProStorageInventorySelectDialog.vue'
import MoneyInput from '@/views/biz/components/MoneyInput.vue'
const { t } = useI18n() const { t } = useI18n()
const message = useMessage() const message = useMessage()
@ -252,7 +268,7 @@ const formData = reactive({
saleDeliveryNo: '自动生成', saleDeliveryNo: '自动生成',
deliveryDate: getToday(), deliveryDate: getToday(),
deliveryStatus: '1', deliveryStatus: '1',
deliveryType: undefined, deliveryType: '1',
custId: undefined, custId: undefined,
custName: undefined, custName: undefined,
contact: undefined, contact: undefined,
@ -339,7 +355,7 @@ watch(dialogVisible, (val) => {
const formRules = reactive({ const formRules = reactive({
saleOrdId: [{ required: true, message: '销售订单不能为空', trigger: 'change' }], saleOrdId: [{ required: true, message: '销售订单不能为空', trigger: 'change' }],
ordDate: [{ required: true, message: '单据日期不能为空', trigger: 'change' }], deliveryDate: [{ required: true, message: '单据日期不能为空', trigger: 'change' }],
deliveryStatus: [{ required: true, message: '单据状态不能为空', trigger: 'change' }], deliveryStatus: [{ required: true, message: '单据状态不能为空', trigger: 'change' }],
deliveriedQty: [ deliveriedQty: [
{ required: true, message: '发货数量不能为空', trigger: 'change' }, { required: true, message: '发货数量不能为空', trigger: 'change' },
@ -597,10 +613,35 @@ const validateDeliveryQty = () => {
const remaimQty = parseInt(formData.remaimQty) || 0 const remaimQty = parseInt(formData.remaimQty) || 0
const totalDeliveriedQty = totalQty.value const totalDeliveriedQty = totalQty.value
// 0
for (let i = 0; i < productList.value.length; i++) {
const item = productList.value[i]
const deliveriedBag = parseInt(item.deliveriedBag) || 0
const deliveriedQty = parseInt(item.deliveriedQty) || 0
if (!item.deliveriedBag || deliveriedBag <= 0) {
message.warning(`${i + 1}发货袋数不能为空且必须大于0`)
return false
}
if (!item.deliveriedQty || deliveriedQty <= 0) {
message.warning(`${i + 1}发货数量不能为空且必须大于0`)
return false
}
}
if (totalDeliveriedQty > remaimQty) { if (totalDeliveriedQty > remaimQty) {
message.warning(`发货总数量(${totalDeliveriedQty})不能超过剩余数量(${remaimQty})`) message.warning(`发货总数量(${totalDeliveriedQty})不能超过剩余数量(${remaimQty})`)
return false return false
} }
//
const formDeliveriedQty = parseInt(formData.deliveriedQty) || 0
if (totalDeliveriedQty !== formDeliveriedQty) {
message.warning(`明细表发货合计(${totalDeliveriedQty})必须等于表单发货数量(${formDeliveriedQty})`)
return false
}
return true return true
} }
@ -707,7 +748,7 @@ const resetForm = () => {
saleDeliveryNo: '自动生成', saleDeliveryNo: '自动生成',
deliveryDate: getToday(), deliveryDate: getToday(),
deliveryStatus: '1', deliveryStatus: '1',
deliveryType: undefined, deliveryType: '1',
custId: undefined, custId: undefined,
custName: undefined, custName: undefined,
contact: undefined, contact: undefined,
@ -733,3 +774,9 @@ const resetForm = () => {
formRef.value?.resetFields() formRef.value?.resetFields()
} }
</script> </script>
<style scoped>
:deep(.el-table__footer-wrapper td) {
text-align: center;
}
</style>

View File

@ -116,6 +116,7 @@
<el-button <el-button
link link
type="primary" type="primary"
v-if="scope.row.deliveryStatus === '1'"
@click.stop="openForm('update', scope.row.id)" @click.stop="openForm('update', scope.row.id)"
v-hasPermi="['tso:sale-delivery:update']" v-hasPermi="['tso:sale-delivery:update']"
> >
@ -124,6 +125,7 @@
<el-button <el-button
link link
type="danger" type="danger"
v-if="scope.row.deliveryStatus === '1'"
@click.stop="handleDelete(scope.row.id)" @click.stop="handleDelete(scope.row.id)"
v-hasPermi="['tso:sale-delivery:delete']" v-hasPermi="['tso:sale-delivery:delete']"
> >
@ -132,7 +134,7 @@
<el-button <el-button
link link
type="primary" type="primary"
@click.stop="viewDetail(scope.row)" @click.stop="openDetail(scope.row.id)"
v-hasPermi="['tso:sale-delivery:query']" v-hasPermi="['tso:sale-delivery:query']"
> >
详情 详情
@ -173,15 +175,19 @@
</ContentWrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<SaleDeliveryForm ref="formRef" @success="getList"/> <SaleDeliveryForm ref="formRef" @success="getList" @close="handleQuery"/>
<!-- 详情弹窗 -->
<SaleDeliveryDetail ref="detailRef" @success="getList" @close="handleQuery"/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue' import { ref, reactive } from 'vue'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter, getCurrentMonthRange } from '@/utils/formatTime'
import download from '@/utils/download' import download from '@/utils/download'
import * as SaleDeliveryApi from '@/api/biz/saledelivery' import * as SaleDeliveryApi from '@/api/biz/saledelivery'
import SaleDeliveryForm from './SaleDeliveryForm.vue' import SaleDeliveryForm from './SaleDeliveryForm.vue'
import SaleDeliveryDetail from './SaleDeliveryDetail.vue'
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
defineOptions({ name: 'SaleDelivery' }) defineOptions({ name: 'SaleDelivery' })
@ -196,10 +202,18 @@ const selectedRow = ref(null)
const detailLoading = ref(false) const detailLoading = ref(false)
const detailList = ref([]) const detailList = ref([])
//
const detailRef = ref()
/** 打开详情弹窗 */
const openDetail = (id: number) => {
detailRef.value?.open(id)
}
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
deliveryDate: [], deliveryDate: getCurrentMonthRange(),
saleDeliveryNo: undefined, saleDeliveryNo: undefined,
custName: undefined, custName: undefined,
deliveryStatus: undefined, deliveryStatus: undefined,