feat(biz): 添加采购订单模块功能

This commit is contained in:
zxy 2026-05-18 15:36:48 +08:00
parent 454c7267aa
commit dc19a31ba5
12 changed files with 1453 additions and 61 deletions

View File

@ -1,34 +1,31 @@
package com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder;
import com.ningxia.yunxi.chemmes.framework.common.pojo.CommonResult;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageParam;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.common.util.object.BeanUtils;
import com.ningxia.yunxi.chemmes.framework.excel.core.util.ExcelUtils;
import com.ningxia.yunxi.chemmes.framework.operatelog.core.annotations.OperateLog;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo.PurOrderPageReqVO;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo.PurOrderRespVO;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo.PurOrderSaveReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purorder.PurOrderDO;
import com.ningxia.yunxi.chemmes.module.biz.service.purorder.PurOrderService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageParam;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.common.pojo.CommonResult;
import com.ningxia.yunxi.chemmes.framework.common.util.object.BeanUtils;
import static com.ningxia.yunxi.chemmes.framework.common.pojo.CommonResult.success;
import com.ningxia.yunxi.chemmes.framework.excel.core.util.ExcelUtils;
import com.ningxia.yunxi.chemmes.framework.operatelog.core.annotations.OperateLog;
import static com.ningxia.yunxi.chemmes.framework.operatelog.core.enums.OperateTypeEnum.*;
import static com.ningxia.yunxi.chemmes.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 采购订单主")
@ -68,9 +65,9 @@ public class PurOrderController {
@Operation(summary = "获得采购订单主")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('tsc:pur-order:query')")
public CommonResult<PurOrderRespVO> getPurOrder(@RequestParam("id") Integer id) {
PurOrderDO purOrder = purOrderService.getPurOrder(id);
return success(BeanUtils.toBean(purOrder, PurOrderRespVO.class));
public CommonResult<PurOrderSaveReqVO> getPurOrder(@RequestParam("id") Integer id) {
PurOrderSaveReqVO purOrder = purOrderService.getPurOrderWithItems(id);
return success(BeanUtils.toBean(purOrder, PurOrderSaveReqVO.class));
}
@GetMapping("/page")

View File

@ -1,14 +1,13 @@
package com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.Data;
import java.time.LocalDate;
import java.util.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 采购订单主 Response VO")
@Data
@ -29,6 +28,7 @@ public class PurOrderRespVO {
@Schema(description = "订单日期")
@ExcelProperty("订单日期")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate purDate;
@Schema(description = "附件信息")
@ -93,6 +93,7 @@ public class PurOrderRespVO {
@Schema(description = "审核时间")
@ExcelProperty("审核时间")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDateTime auditTime;
}
}

View File

@ -1,14 +1,16 @@
package com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorderitem.vo.PurOrderItemSaveReqVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.time.LocalDate;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 采购订单主新增/修改 Request VO")
@Data
@ -21,6 +23,8 @@ public class PurOrderSaveReqVO {
private String purOrdNo;
@Schema(description = "订单日期")
@JsonFormat(pattern = "yyyy-MM-dd")
@JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate purDate;
@Schema(description = "附件信息")
@ -70,6 +74,10 @@ public class PurOrderSaveReqVO {
private String auditId;
@Schema(description = "审核时间")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDateTime auditTime;
@Schema(description = "采购订单子表列表")
private List<PurOrderItemSaveReqVO> itemList;
}

View File

@ -1,13 +1,11 @@
package com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorderitem.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.Data;
import java.time.LocalDate;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import java.math.BigDecimal;
import java.time.LocalDate;
@Schema(description = "管理后台 - 采购订单子新增/修改 Request VO")
@Data
@ -38,6 +36,7 @@ public class PurOrderItemSaveReqVO {
private BigDecimal deliveryQty;
@Schema(description = "要求到货日期")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate reqDeliveryDate;
@Schema(description = "采购申请单id", example = "11168")

View File

@ -1,16 +1,10 @@
package com.ningxia.yunxi.chemmes.module.biz.dal.mysql.purorder;
import java.util.*;
import cn.hutool.core.util.ObjectUtil;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.mapper.BaseMapperX;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo.PurOrderPageReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purorder.PurOrderDO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purorderitem.PurOrderItemDO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.techproc.TechProcDO;
import org.apache.ibatis.annotations.Mapper;
/**
@ -41,4 +35,11 @@ public interface PurOrderMapper extends BaseMapperX<PurOrderDO> {
.orderByDesc(PurOrderDO::getId));
}
}
default String selectMaxPurOrdNo() {
PurOrderDO purOrder = selectOne(new LambdaQueryWrapperX<PurOrderDO>()
.orderByDesc(PurOrderDO::getPurOrdNo)
.last("LIMIT 1"));
return purOrder != null ? purOrder.getPurOrdNo() : null;
}
}

View File

@ -1,17 +1,16 @@
package com.ningxia.yunxi.chemmes.module.biz.dal.mysql.purorderitem;
import java.util.*;
import cn.hutool.core.util.ObjectUtil;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.ningxia.yunxi.chemmes.framework.mybatis.core.mapper.BaseMapperX;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorderitem.vo.PurOrderItemPageReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purorder.PurOrderDO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purorderitem.PurOrderItemDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 采购订单子 Mapper
*
@ -36,4 +35,8 @@ public interface PurOrderItemMapper extends BaseMapperX<PurOrderItemDO> {
}
}
default List<PurOrderItemDO> selectByPurId(Integer purId) {
return selectList(PurOrderItemDO::getPurId, purId);
}
}

View File

@ -1,13 +1,12 @@
package com.ningxia.yunxi.chemmes.module.biz.service.purorder;
import java.util.*;
import javax.validation.*;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageParam;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo.PurOrderPageReqVO;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo.PurOrderSaveReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purorder.PurOrderDO;
import javax.validation.Valid;
/**
* 采购订单主 Service 接口
*
@ -53,4 +52,12 @@ public interface PurOrderService {
*/
PageResult<PurOrderDO> getPurOrderPage(PurOrderPageReqVO pageReqVO);
/**
* 获得采购订单主及子表
*
* @param id 编号
* @return 采购订单主及子表
*/
PurOrderSaveReqVO getPurOrderWithItems(Integer id);
}

View File

@ -1,20 +1,21 @@
package com.ningxia.yunxi.chemmes.module.biz.service.purorder;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.common.util.object.BeanUtils;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo.PurOrderPageReqVO;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorder.vo.PurOrderSaveReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purorder.PurOrderDO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.purorderitem.PurOrderItemDO;
import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.purorder.PurOrderMapper;
import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.purorderitem.PurOrderItemMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import static com.ningxia.yunxi.chemmes.module.infra.enums.ErrorCodeConstants.*;
import java.util.*;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageParam;
import com.ningxia.yunxi.chemmes.framework.common.util.object.BeanUtils;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import static com.ningxia.yunxi.chemmes.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -29,23 +30,48 @@ public class PurOrderServiceImpl implements PurOrderService {
@Resource
private PurOrderMapper purOrderMapper;
@Resource
private PurOrderItemMapper purOrderItemMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Integer createPurOrder(PurOrderSaveReqVO createReqVO) {
// 插入
PurOrderDO purOrder = BeanUtils.toBean(createReqVO, PurOrderDO.class);
purOrder.setPurOrdNo(generatePurOrdNo());
purOrderMapper.insert(purOrder);
createPurOrderItemList(purOrder.getId(), createReqVO.getItemList());
// 返回
return purOrder.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updatePurOrder(PurOrderSaveReqVO updateReqVO) {
// 校验存在
validatePurOrderExists(updateReqVO.getId());
// 更新
PurOrderDO updateObj = BeanUtils.toBean(updateReqVO, PurOrderDO.class);
purOrderMapper.updateById(updateObj);
updatePurOrderItemList(updateReqVO.getId(), updateReqVO.getItemList());
}
@Override
public PurOrderSaveReqVO getPurOrderWithItems(Integer id) {
PurOrderDO purOrder = purOrderMapper.selectById(id);
if (purOrder == null) {
throw exception("采购订单不存在");
}
PurOrderSaveReqVO purOrderVO = BeanUtils.toBean(purOrder, PurOrderSaveReqVO.class);
List<PurOrderItemDO> itemList = purOrderItemMapper.selectByPurId(id);
purOrderVO.setItemList(BeanUtils.toBean(itemList, com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorderitem.vo.PurOrderItemSaveReqVO.class));
return purOrderVO;
}
@Override
@ -72,4 +98,41 @@ public class PurOrderServiceImpl implements PurOrderService {
return purOrderMapper.selectPage(pageReqVO);
}
private String generatePurOrdNo() {
String ym = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
String maxPurOrdNo = purOrderMapper.selectMaxPurOrdNo();
if (maxPurOrdNo == null || maxPurOrdNo.length() < 11
|| !maxPurOrdNo.substring(4, 10).equals(ym)) {
return "CGDD" + ym + "001";
} else {
String prefix = maxPurOrdNo.substring(0, 10);
int sequence = Integer.parseInt(maxPurOrdNo.substring(10));
sequence++;
return prefix + String.format("%03d", sequence);
}
}
private void createPurOrderItemList(Integer purId, List<com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorderitem.vo.PurOrderItemSaveReqVO> list) {
if (list == null || list.isEmpty()) {
return;
}
List<PurOrderItemDO> purOrderItems = BeanUtils.toBean(list, PurOrderItemDO.class);
purOrderItems.forEach(item ->
item.setPurId(purId)
.setId(null)
);
purOrderItemMapper.insertBatch(purOrderItems);
}
private void updatePurOrderItemList(Integer purId, List<com.ningxia.yunxi.chemmes.module.biz.controller.admin.purorderitem.vo.PurOrderItemSaveReqVO> list) {
deletePurOrderItemByPurId(purId);
createPurOrderItemList(purId, list);
}
private void deletePurOrderItemByPurId(Integer purId) {
purOrderItemMapper.delete(PurOrderItemDO::getPurId, purId);
}
}

View File

@ -31,9 +31,12 @@ declare module 'vue' {
DocAlert: typeof import('./../components/DocAlert/index.vue')['default']
Echart: typeof import('./../components/Echart/src/Echart.vue')['default']
Editor: typeof import('./../components/Editor/src/Editor.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
@ -52,11 +55,14 @@ declare module 'vue' {
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
@ -69,6 +75,7 @@ declare module 'vue' {
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload']
Error: typeof import('./../components/Error/src/Error.vue')['default']
@ -83,11 +90,17 @@ declare module 'vue' {
InputPassword: typeof import('./../components/InputPassword/src/InputPassword.vue')['default']
InputWithColor: typeof import('./../components/InputWithColor/index.vue')['default']
MagicCubeEditor: typeof import('./../components/MagicCubeEditor/index.vue')['default']
MaterialSelectDialog: typeof import('./../views/biz/material/MaterialSelectDialog.vue')['default']
Pagination: typeof import('./../components/Pagination/index.vue')['default']
ProcessDesigner: typeof import('./../components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue')['default']
ProcessPalette: typeof import('./../components/bpmnProcessDesigner/package/palette/ProcessPalette.vue')['default']
ProcessViewer: typeof import('./../components/bpmnProcessDesigner/package/designer/ProcessViewer.vue')['default']
PropertiesPanel: typeof import('./../components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue')['default']
Purorder: typeof import('./../views/biz/purorder/index.vue')['default']
PurOrderDetail: typeof import('./../views/biz/purorder/PurOrderDetail.vue')['default']
PurOrderForm: typeof import('./../views/biz/purorder/PurOrderForm.vue')['default']
Purorderitem: typeof import('./../views/biz/purorderitem/index.vue')['default']
PurOrderItemForm: typeof import('./../views/biz/purorderitem/PurOrderItemForm.vue')['default']
Qrcode: typeof import('./../components/Qrcode/src/Qrcode.vue')['default']
ReceiveTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
@ -112,6 +125,7 @@ declare module 'vue' {
VerifyPoints: typeof import('./../components/Verifition/src/Verify/VerifyPoints.vue')['default']
VerifySlide: typeof import('./../components/Verifition/src/Verify/VerifySlide.vue')['default']
VerticalButtonGroup: typeof import('./../components/VerticalButtonGroup/index.vue')['default']
'Workspace.xml': typeof import('./../../.idea/workspace.xml.tmp')['default']
XButton: typeof import('./../components/XButton/src/XButton.vue')['default']
XTextButton: typeof import('./../components/XButton/src/XTextButton.vue')['default']
}

View File

@ -0,0 +1,195 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1200px">
<el-form
ref="formRef"
:model="formData"
label-width="100px"
v-loading="formLoading"
>
<!-- 基础信息 -->
<el-card class="mb-4">
<template #header>
<span>基础信息</span>
</template>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="采购单号">
<el-input v-model="formData.purOrdNo" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="订单日期">
<el-input v-model="formData.purDate" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="单据类型">
<el-input :value="formData.billType === '1' ? '标准采购申请' : '设备采购申请'" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="单据状态">
<el-tag v-if="formData.purStatus === '1'" type="info">已创建</el-tag>
<el-tag v-else-if="formData.purStatus === '2'" type="warning">已确认</el-tag>
<el-tag v-else-if="formData.purStatus === '3'" type="success">已审批</el-tag>
<el-tag v-else-if="formData.purStatus === '4'" type="danger">已驳回</el-tag>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" class="mt-4">
<el-col :span="6">
<el-form-item label="供应商">
<el-input v-model="formData.supplierName" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="采购部门">
<el-input v-model="formData.purDeptName" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="采购人员">
<el-input v-model="formData.purEmpName" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="验收方式">
<el-input :value="formData.acceptMeth === '1' ? '数量验收' : '金额验收'" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" class="mt-4">
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="formData.remark" type="textarea" :rows="2" disabled />
</el-form-item>
</el-col>
</el-row>
</el-card>
<!-- 采购物料信息 -->
<el-card>
<template #header>
<span>采购物料信息</span>
</template>
<el-table :data="itemList" show-summary border :summary-method="getSummary">
<el-table-column label="序号" type="index" width="60px" align="center" />
<el-table-column label="物料编码" prop="materialCode" width="120px" align="center" />
<el-table-column label="物料名称" prop="materialName" width="150px" align="center" />
<el-table-column label="规格型号" prop="spec" width="100px" align="center" />
<el-table-column label="单位" prop="unit" width="80px" align="center" />
<el-table-column label="采购数量" prop="purQty" width="100px" align="center" />
<el-table-column label="要求交货日期" prop="reqDeliveryDate" width="130px" align="center" />
<el-table-column label="采购单价" prop="priceTax" width="100px" align="center">
<template #default="scope">
<span style="color: #409EFF;">{{ scope.row.priceTax }}</span>
</template>
</el-table-column>
<el-table-column label="税率(%)" prop="taxRatio" width="80px" align="center">
<template #default="scope">
{{ scope.row.taxRatio }}%
</template>
</el-table-column>
<el-table-column label="采购总价" prop="totalPrice" width="100px" align="center" />
<el-table-column label="备注" prop="remark" width="100px" align="center" />
</el-table>
</el-card>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import * as PurOrderApi from '@/api/biz/purorder'
import * as PurOrderItemApi from '@/api/biz/purorderitem'
const message = useMessage()
const dialogVisible = ref(false)
const dialogTitle = ref('采购订单详情')
const formLoading = ref(false)
const formData = reactive({
id: undefined,
purOrdNo: undefined,
purDate: undefined,
billType: undefined,
purStatus: undefined,
supplierName: undefined,
purDeptName: undefined,
purEmpName: undefined,
acceptMeth: undefined,
remark: undefined,
})
const itemList = ref<any[]>([])
//
const open = async (id: number) => {
formLoading.value = true
dialogVisible.value = true
try {
const data = await PurOrderApi.getPurOrder(id)
Object.assign(formData, data)
//
const itemData = await PurOrderItemApi.getPurOrderItemPage({
pageNo: 1,
pageSize: 100,
purId: id
})
itemList.value = (itemData.list || []).map((item: any) => ({
materialCode: item.materialCode,
materialName: item.materialName,
spec: item.spec,
unit: item.unit,
purQty: item.purQty,
reqDeliveryDate: item.reqDeliveryDate,
priceTax: item.priceTax,
taxRatio: item.taxRatio,
totalPrice: item.totalPrice,
remark: item.remark,
}))
} catch (error) {
message.error('获取详情失败')
} finally {
formLoading.value = false
}
}
//
const getSummary = (param: any) => {
const { columns, data } = param
const sums: any[] = []
columns.forEach((column: any, index: number) => {
if (index === 0) {
sums[index] = '合计'
return
}
if (['purQty', 'totalPrice'].includes(column.property)) {
const values = data.map((item: any) => Number(item[column.property]) || 0)
sums[index] = values.reduce((prev: number, curr: number) => prev + curr, 0)
} else {
sums[index] = ''
}
})
return sums
}
defineExpose({
open,
})
</script>
<style scoped>
:deep(.el-table__footer-wrapper td) {
text-align: center;
}
</style>

View File

@ -0,0 +1,698 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1500px">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<!-- 基础信息 -->
<el-card class="mb-4">
<template #header>
<span>基本信息</span>
</template>
<!-- 第一行单据类型申请类型采购日期 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="单据类型" prop="billType">
<el-select v-model="formData.billType" placeholder="请选择" style="width: 100%">
<el-option label="标准采购申请" value="1" />
<el-option label="设备采购申请" value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申请类型" prop="applyType">
<el-select v-model="formData.applyType" placeholder="请选择" style="width: 100%">
<el-option label="采购申请" value="1" />
<el-option label="紧急采购" value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="采购日期" prop="purDate">
<el-date-picker
v-model="formData.purDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择采购日期"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行采购部门采购人员单据状态 -->
<el-row :gutter="20" class="mt-4">
<el-col :span="8">
<el-form-item label="采购部门" prop="purDeptId">
<el-tree-select
v-model="formData.purDeptId"
:data="deptList"
:props="{ label: 'name', value: 'id', children: 'children' }"
placeholder="请选择采购部门"
filterable
check-strictly
style="width: 100%"
@change="handleDeptChange"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="采购人员" prop="purEmpId">
<el-select
v-model="formData.purEmpId"
placeholder="请选择采购人员"
filterable
remote
reserve-keyword
:remote-method="searchUsers"
:loading="userSelectLoading"
style="width: 100%"
@change="handleUserChange"
>
<el-option
v-for="user in userList"
:key="user.id"
:label="user.nickname + ' (' + user.username + ')'"
:value="user.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="单据状态" prop="purStatus">
<el-select v-model="formData.purStatus" placeholder="请选择" style="width: 100%" disabled>
<el-option label="已创建" value="1" />
<el-option label="已确认" value="2" />
<el-option label="已审批" value="3" />
<el-option label="已驳回" value="4" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行供应商名称采购单号备注 -->
<el-row :gutter="20" class="mt-4">
<el-col :span="12">
<el-form-item label="供应商名称" prop="supplierId">
<el-select
v-model="formData.supplierId"
placeholder="请选择供应商"
filterable
:loading="supplierLoading"
style="width: 100%"
@change="handleSupplierChange"
>
<el-option
v-for="supplier in supplierOptions"
:key="supplier.id"
:label="supplier.supplierName"
:value="supplier.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" >
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
:rows="1"
placeholder="请输入"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行验收方式 -->
<el-row :gutter="20" class="mt-4">
<el-col :span="8">
<el-form-item label="验收方式" prop="acceptMeth">
<el-select v-model="formData.acceptMeth" placeholder="请选择" style="width: 100%">
<el-option label="数量验收" value="1" />
<el-option label="金额验收" value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="采购单号" prop="purOrdNo">
<el-input v-model="formData.purOrdNo" placeholder="保存时自动生成" disabled />
</el-form-item>
</el-col>
</el-row>
</el-card>
<!-- 明细信息 -->
<el-card>
<template #header>
<div class="flex items-center">
<span>明细信息</span>
<el-button type="primary" @click="openMaterialSelect" class="ml-4">新增</el-button>
</div>
</template>
<el-table :data="itemList" show-summary border :summary-method="getSummary" row-key="materialId">
<el-table-column label="序号" type="index" width="60px" align="center" />
<el-table-column label="物料编码(*)" prop="materialCode" width="120px" align="center">
<template #default="scope">
{{ scope.row.materialCode}}
</template>
</el-table-column>
<el-table-column label="物料名称" prop="materialName" width="150px" align="center">
<template #default="scope">
{{ scope.row.materialName}}
</template>
</el-table-column>
<el-table-column label="规格型号" prop="spec" width="100px" align="center">
<template #default="scope">
{{ scope.row.spec}}
</template>
</el-table-column>
<el-table-column label="采购数量(*)" prop="purQty" width="120px" align="center">
<template #default="scope">
<MoneyInput
v-model="scope.row.purQty"
:decimal-places="0"
:allow-negative="false"
:show-prefix="false"
placeholder="请输入"
@change="() => calculateTotal(scope.row)"
/>
</template>
</el-table-column>
<el-table-column label="要求到货日期" prop="reqDeliveryDate" width="180px" align="center">
<template #default="scope">
<el-date-picker
v-model="scope.row.reqDeliveryDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择日期"
style="width: 140px"
:clearable="true"
/>
</template>
</el-table-column>
<el-table-column label="含税单价" prop="priceTax" width="150px" align="center">
<template #default="scope">
<MoneyInput
v-model="scope.row.priceTax"
:decimal-places="2"
:allow-negative="false"
:show-prefix="false"
placeholder="请输入"
@change="() => calculateTotal(scope.row)"
/>
</template>
</el-table-column>
<el-table-column label="含税总价" prop="totalPrice" width="150px" align="center">
<template #default="scope">
<el-input :model-value="formatTotalPrice(scope.row)" placeholder="自动计算" readonly />
</template>
</el-table-column>
<el-table-column label="单位" prop="unit" width="80px" align="center">
<template #default="scope">
{{ getUnitName(scope.row.unit) }}
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" width="150px" align="center">
<template #default="scope">
<el-input v-model="scope.row.remark" placeholder="请输入" />
</template>
</el-table-column>
<el-table-column label="操作" width="80px" align="center">
<template #default="scope" >
<el-button link type="danger" @click="removeItem(scope.$index)" >删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="submitAudit" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
<!-- 物料选择弹窗 -->
<MaterialSelectDialog
ref="materialSelectRef"
@confirm="handleMaterialSelect"
/>
</template>
<script setup lang="ts">
import { ref, reactive, computed, nextTick } from 'vue'
import * as PurOrderApi from '@/api/biz/purorder'
import * as PurOrderItemApi from '@/api/biz/purorderitem'
import MaterialSelectDialog from '../material/MaterialSelectDialog.vue'
import * as SupplierApi from '@/api/biz/supplier'
import { getDeptSimpleName, getSimpleDeptList } from '@/api/system/dept'
import * as UserApi from '@/api/system/user'
import { handleTree } from '@/utils/tree'
import MoneyInput from '../components/MoneyInput.vue'
import { getDictOptions } from '@/utils/dict'
const { t } = useI18n()
const message = useMessage()
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formLoading = ref(false)
const formType = ref('')
//
const supplierOptions = ref<any[]>([])
const supplierLoading = ref(false)
//
const deptList = ref<any[]>([])
//
const userList = ref<any[]>([])
const userSelectLoading = ref(false)
//
const materialSelectRef = ref()
//
const unitOptions = ref<any[]>([])
//
const getUnitName = (unit: string | number) => {
if (!unit && unit !== 0) return '-'
const item = unitOptions.value.find(opt => String(opt.value) === String(unit))
return item ? item.label : unit
}
const formData = reactive({
id: undefined,
purOrdNo: undefined,
purDate: undefined,
billType: '1',
applyType: '1',
purStatus: '1',
supplierId: undefined,
supplierName: undefined,
supplierNo: undefined,
purDeptName: undefined,
purDeptId: undefined,
purEmpName: undefined,
purEmpId: undefined,
acceptMeth: '1',
remark: undefined,
})
const itemList = ref<any[]>([])
const formRules = reactive({
purDate: [{ required: true, message: '订单日期不能为空', trigger: 'change' }],
billType: [{ required: true, message: '单据类型不能为空', trigger: 'change' }],
supplierId: [{ required: true, message: '供应商不能为空', trigger: 'change' }],
purDeptId: [{ required: true, message: '采购部门不能为空', trigger: 'change' }],
purEmpId: [{ required: true, message: '采购人员不能为空', trigger: 'change' }],
})
const formRef = ref()
const emit = defineEmits(['success', 'close'])
//
const loadSupplierList = async () => {
supplierLoading.value = true
try {
// supplier_type in ('1','2','3'')
const params: any = {
supplierTypeList: ['1','2','3']
}
const data = await SupplierApi.dropdown(params)
supplierOptions.value = data || []
} catch (error) {
console.error('加载供应商失败:', error)
supplierOptions.value = []
} finally {
supplierLoading.value = false
}
}
//
const loadDeptList = async () => {
try {
const data = await getSimpleDeptList()
deptList.value = handleTree(data)
} catch (error) {
console.error('加载部门失败:', error)
deptList.value = []
}
}
//
const searchUsers = async (query: string) => {
userSelectLoading.value = true
try {
const params: any = {
pageNo: 1,
pageSize: 50,
status: 0
}
if (query) {
params.nickname = query
}
if (formData.purDeptId) {
params.deptId = formData.purDeptId
}
const data = await UserApi.getUserPage(params)
userList.value = data.list || []
} finally {
userSelectLoading.value = false
}
}
//
const getUserNameById = async (userId: number) => {
try {
const user = await UserApi.getUser(userId)
return user.data?.nickname || user.nickname || ''
} catch {
return ''
}
}
//
const open = async (type: string, id?: number) => {
//
if (dialogVisible.value) {
return
}
formType.value = type
dialogTitle.value = type === 'create' ? '新增采购订单' : '编辑采购订单'
//
await loadSupplierList()
await loadDeptList()
//
unitOptions.value = getDictOptions('mat_unit')
//
Object.assign(formData, {
id: undefined,
purOrdNo: undefined,
purDate: getToday(),
billType: '1',
applyType: '1',
purStatus: '1',
supplierId: undefined,
supplierName: undefined,
supplierNo: undefined,
purDeptName: undefined,
purDeptId: undefined,
purEmpName: undefined,
purEmpId: undefined,
acceptMeth: '1',
remark: undefined,
itemList: [],
})
itemList.value = []
if (type === 'update' && id) {
formLoading.value = true
try {
const data = await PurOrderApi.getPurOrder(id)
//
if (data.purDeptId) {
await searchUsers('')
} else {
await searchUsers('')
}
//
if (data.purEmpId && !userList.value.find((u: any) => u.id === data.purEmpId)) {
try {
const user = await UserApi.getUser(data.purEmpId)
if (user) {
userList.value.unshift(user.data || user)
}
} catch (e) {
console.error('加载当前采购人员失败', e)
}
}
//
Object.assign(formData, data)
//
const itemData = data.itemList
console.log('明细数据:', itemData)
itemList.value = (itemData || []).map((item: any) => ({
id: item.id,
materialId: item.materialId,
materialCode: item.materialCode,
materialName: item.materialName,
spec: item.spec,
unit: item.unit,
purQty: item.purQty,
// YYYY-MM-DD
reqDeliveryDate: item.reqDeliveryDate,
priceTax: item.priceTax,
taxRatio: item.taxRatio,
totalPrice: item.totalPrice,
remark: item.remark,
}))
console.log('itemList.value:', itemList.value)
} finally {
formLoading.value = false
}
}
dialogVisible.value = true
}
//
const getToday = () => {
const today = new Date()
const year = today.getFullYear()
const month = String(today.getMonth() + 1).padStart(2, '0')
const day = String(today.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
//
const handleSupplierChange = (supplierId: number) => {
if (!supplierId) {
formData.supplierName = undefined
formData.supplierNo = undefined
return
}
const supplier = supplierOptions.value.find(s => s.id === supplierId)
if (supplier) {
formData.supplierName = supplier.supplierName
formData.supplierNo = supplier.supplierNo
}
}
//
const handleDeptChange = (deptId: number) => {
const dept = deptList.value.find(d => d.id === deptId)
if (dept) {
formData.purDeptName = dept.name
}
formData.purEmpId = undefined
formData.purEmpName = undefined
searchUsers('')
}
//
const handleUserChange = (userId: number) => {
if (userId) {
const user = userList.value.find(u => u.id === userId)
if (user) {
formData.purEmpName = user.nickname
}
} else {
formData.purEmpName = undefined
}
}
//
const openMaterialSelect = () => {
materialSelectRef.value?.open()
}
//
const handleMaterialSelect = (materials: any[]) => {
const existingIds = itemList.value.map(item => item.materialId).filter(Boolean)
materials.forEach(material => {
if (!existingIds.includes(material.id)) {
itemList.value.push({
materialId: material.id,
materialCode: material.matCode,
materialName: material.matName,
spec: material.spec,
unit: material.unit,
purQty: undefined,
reqDeliveryDate: undefined,
priceTax: undefined,
taxRatio: 13,
totalPrice: undefined,
remark: undefined,
})
}
})
}
//
const removeItem = (index: number) => {
itemList.value.splice(index, 1)
}
//
const calculateTotal = (row: any) => {
if (!row) return
const qty = Number(row.purQty) || 0
const price = Number(row.priceTax) || 0
const total = qty * price
row.totalPrice = total > 0 ? total.toFixed(2) : '0.00'
}
//
const formatTotalPrice = (row: any) => {
if (!row) return ''
const qty = Number(row.purQty) || 0
const price = Number(row.priceTax) || 0
const total = qty * price
return total > 0 ? total.toFixed(2) : row.totalPrice || ''
}
//
const getSummary = (param: any) => {
const { columns, data } = param
const sums: any[] = []
columns.forEach((column: any, index: number) => {
if (index === 0) {
sums[index] = '合计'
return
}
if (['purQty', 'totalPrice'].includes(column.property)) {
const values = data.map((item: any) => Number(item[column.property]) || 0)
sums[index] = values.reduce((prev: number, curr: number) => prev + curr, 0)
} else {
sums[index] = ''
}
})
return sums
}
//
const submitForm = async () => {
//
if (itemList.value.length === 0) {
message.warning('请至少添加一条采购物料')
return
}
for (let i = 0; i < itemList.value.length; i++) {
const item = itemList.value[i]
if (!item.materialCode) {
message.warning(`${i + 1}行:物料编码不能为空`)
return
}
if (!item.purQty || Number(item.purQty) <= 0) {
message.warning(`${i + 1}采购数量不能为空且必须大于0`)
return
}
if (!item.priceTax || Number(item.priceTax) <= 0) {
message.warning(`${i + 1}采购单价不能为空且必须大于0`)
return
}
}
//
await formRef.value.validate()
formLoading.value = true
try {
const data = {
...formData,
itemList: itemList.value
}
if (formType.value === 'create') {
await PurOrderApi.createPurOrder(data)
message.success(t('common.createSuccess'))
} else {
await PurOrderApi.updatePurOrder(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
emit('success')
} finally {
formLoading.value = false
}
}
//
const submitAudit = async () => {
//
if (itemList.value.length === 0) {
message.warning('请至少添加一条采购物料')
return
}
for (let i = 0; i < itemList.value.length; i++) {
const item = itemList.value[i]
if (!item.materialCode) {
message.warning(`${i + 1}行:物料编码不能为空`)
return
}
if (!item.purQty || Number(item.purQty) <= 0) {
message.warning(`${i + 1}采购数量不能为空且必须大于0`)
return
}
if (!item.priceTax || Number(item.priceTax) <= 0) {
message.warning(`${i + 1}采购单价不能为空且必须大于0`)
return
}
}
//
await formRef.value.validate()
formLoading.value = true
try {
const data = {
...formData,
purStatus: '2', //
itemList: itemList.value
}
if (formType.value === 'create') {
await PurOrderApi.createPurOrder(data)
message.success('确认成功')
} else {
await PurOrderApi.updatePurOrder(data)
message.success('确认成功')
}
dialogVisible.value = false
emit('success')
} finally {
formLoading.value = false
}
}
defineExpose({
open
})
</script>
<style scoped>
:deep(.el-table__footer-wrapper td) {
text-align: center;
}
</style>

View File

@ -0,0 +1,406 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="100px"
>
<el-form-item label="订单日期" prop="purDate">
<el-date-picker
v-model="queryParams.purDate"
value-format="YYYY-MM-DD"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="单据状态" prop="purStatus">
<el-select
v-model="queryParams.purStatus"
placeholder="请选择"
clearable
class="!w-150px"
>
<el-option label="已创建" value="1" />
<el-option label="已确认" value="2" />
<el-option label="已审批" value="3" />
<el-option label="已驳回" value="4" />
</el-select>
</el-form-item>
<el-form-item label="采购单号" prop="purOrdNo">
<el-input
v-model="queryParams.purOrdNo"
placeholder="请输入"
clearable
@keyup.enter="handleQuery"
class="!w-180px"
/>
</el-form-item>
<el-form-item label="供应商名称" prop="supplierName">
<el-input
v-model="queryParams.supplierName"
placeholder="请输入"
clearable
@keyup.enter="handleQuery"
class="!w-180px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置
</el-button>
<el-button
type="primary"
@click="openForm('create')"
v-hasPermi="['biz:pur-order:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
@click="handleAudit"
v-hasPermi="['biz:pur-order:audit']"
>
审批
</el-button>
<el-button
type="danger"
@click="handleReject"
v-hasPermi="['biz:pur-order:reject']"
>
驳回
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 采购单信息 -->
<ContentWrap>
<div style="font-weight: bold; margin-bottom: 8px;">采购单信息</div>
<div style="display: flex; gap: 16px;">
<!-- 主表 -->
<div style="flex: 1;">
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
highlight-current-row
@row-click="handleRowClick"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="50px" align="center" />
<el-table-column label="序号" align="center" type="index" width="60px"/>
<el-table-column label="单据类型" align="center" prop="billType" width="160px">
<template #default="scope">
{{ scope.row.billType === '1' ? '标准采购申请' : '设备采购申请' }}
</template>
</el-table-column>
<el-table-column label="采购订单号" align="center" prop="purOrdNo" width="160px" />
<el-table-column label="订单日期" align="center" prop="purDate" width="140px" />
<el-table-column label="供应商名称" align="center" prop="supplierName" width="300px" />
<el-table-column label="单据状态" align="center" prop="purStatus" width="120px">
<template #default="scope">
<el-tag v-if="scope.row.purStatus === '1'" type="info">已创建</el-tag>
<el-tag v-else-if="scope.row.purStatus === '2'" type="warning">已确认</el-tag>
<el-tag v-else-if="scope.row.purStatus === '3'" type="success">已审批</el-tag>
<el-tag v-else-if="scope.row.purStatus === '4'" type="danger">已驳回</el-tag>
</template>
</el-table-column>
<el-table-column label="收货状态" align="center" prop="deliveryStatus" width="120px">
<template #default="scope">
{{ scope.row.deliveryStatus === '1' ? '未收货' : scope.row.deliveryStatus === '2' ? '部分收货' : '全部收货' }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="200px">
<template #default="scope">
<el-button
link
type="primary"
v-if="scope.row.purStatus === '1'"
@click.stop="openForm('update', scope.row.id)"
v-hasPermi="['biz:pur-order:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
v-if="scope.row.purStatus === '1'"
@click.stop="handleDelete(scope.row.id)"
v-hasPermi="['biz:pur-order:delete']"
>
删除
</el-button>
<el-button
link
type="primary"
@click.stop="openDetail(scope.row.id)"
v-hasPermi="['biz:pur-order:query']"
>
详情
</el-button>
</template>
</el-table-column>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
style="margin-top: 8px;"
/>
</div>
</div>
</ContentWrap>
<!-- 采购物料信息 -->
<ContentWrap>
<div style="font-weight: bold; margin-bottom: 8px;">采购物料信息</div>
<el-table
v-loading="itemLoading"
:data="itemList"
:stripe="true"
:show-overflow-tooltip="true"
show-summary
:summary-method="getItemSummary"
border
>
<el-table-column label="序号" align="center" type="index" width="60px"/>
<el-table-column label="物料编码" align="center" prop="materialCode" width="120px" />
<el-table-column label="物料名称" align="center" prop="materialName" width="200px" />
<el-table-column label="收货状态" align="center" prop="deliveryStatus" width="100px">
<template #default="scope">
{{ scope.row.deliveryStatus === '1' ? '未收货' : scope.row.deliveryStatus === '2' ? '部分收货' : '全部收货' }}
</template>
</el-table-column>
<el-table-column label="收货数量" align="center" prop="deliveryQty" width="100px" />
<el-table-column label="单位" align="center" prop="unit" width="80px" >
<template #default="scope">
{{ getUnitName(scope.row.unit) }}
</template>
</el-table-column>
<el-table-column label="规格型号" align="center" prop="spec" width="120px" />
<el-table-column label="采购数量" align="center" prop="purQty" width="100px" />
<el-table-column label="要求交货日期" align="center" prop="reqDeliveryDate" width="150px" />
<el-table-column label="采购单价" align="center" prop="priceTax" width="100px">
<template #default="scope">
<span style="color: #409EFF;">{{ scope.row.priceTax }}</span>
</template>
</el-table-column>
<el-table-column label="采购总价" align="center" prop="totalPrice" width="100px" />
<el-table-column label="税率" align="center" prop="taxRatio" width="80px">
<template #default="scope">
{{ scope.row.taxRatio || 0 }}%
</template>
</el-table-column>
</el-table>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<PurOrderForm ref="formRef" @success="getList" @close="handleQuery"/>
<!-- 详情弹窗 -->
<PurOrderDetail ref="detailRef" />
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { dateFormatter, getCurrentMonthRange } from '@/utils/formatTime'
import download from '@/utils/download'
import * as PurOrderApi from '@/api/biz/purorder'
import * as PurOrderItemApi from '@/api/biz/purorderitem'
import PurOrderForm from './PurOrderForm.vue'
import PurOrderDetail from './PurOrderDetail.vue'
import { getDictOptions } from '@/utils/dict'
defineOptions({ name: 'PurOrder' })
const message = useMessage()
const { t } = useI18n()
const loading = ref(false)
const itemLoading = ref(false)
const list = ref([])
const itemList = ref([])
const total = ref(0)
const selectedIds = ref<number[]>([])
const currentRow = ref<any>(null)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
purDate: getCurrentMonthRange(),
purStatus: undefined,
purOrdNo: undefined,
supplierName: undefined,
})
const queryFormRef = ref()
//
const formRef = ref()
//
const detailRef = ref()
//
const unitOptions = ref<any[]>([])
//
const getUnitName = (unit: string | number) => {
if (!unit && unit !== 0) return '-'
const item = unitOptions.value.find(opt => String(opt.value) === String(unit))
return item ? item.label : unit
}
//
unitOptions.value = getDictOptions('mat_unit')
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await PurOrderApi.getPurOrderPage(queryParams)
list.value = data.list
total.value = data.total
//
if (list.value.length > 0) {
await loadItemList(list.value[0].id)
} else {
itemList.value = []
}
} finally {
loading.value = false
}
}
/** 加载采购订单明细 */
const loadItemList = async (purId: number) => {
itemLoading.value = true
try {
//
const data = await PurOrderApi.getPurOrder(purId)
// itemList
itemList.value = data.itemList || data.purOrderItemList || []
} finally {
itemLoading.value = false
}
}
/** 行点击事件 */
const handleRowClick = (row: any) => {
currentRow.value = row
loadItemList(row.id)
}
/** 选择变化事件 */
const handleSelectionChange = (selection: any[]) => {
selectedIds.value = selection.map(item => item.id)
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
queryParams.purDate = getCurrentMonthRange()
handleQuery()
}
/** 添加/修改操作 */
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 打开详情 */
const openDetail = (id: number) => {
detailRef.value.open(id)
}
/** 删除操作 */
const handleDelete = async (id: number) => {
try {
await message.confirm('确认删除该采购订单吗?')
await PurOrderApi.deletePurOrder(id)
message.success('删除成功')
getList()
} catch (error) {
//
}
}
/** 审批操作 */
const handleAudit = async () => {
if (selectedIds.value.length === 0) {
message.warning('请选择要审批的采购订单')
return
}
try {
await message.confirm('确认审批选中的采购订单吗?')
// API
message.success('审批成功')
getList()
} catch (error) {
//
}
}
/** 驳回操作 */
const handleReject = async () => {
if (selectedIds.value.length === 0) {
message.warning('请选择要驳回的采购订单')
return
}
try {
await message.confirm('确认驳回选中的采购订单吗?')
// API
message.success('驳回成功')
getList()
} catch (error) {
//
}
}
/** 子表合计方法 */
const getItemSummary = (param: any) => {
const { columns, data } = param
const sums: any[] = []
columns.forEach((column: any, index: number) => {
if (index === 0) {
sums[index] = '合计'
return
}
//
if (['deliveryQty', 'purQty', 'totalPrice'].includes(column.property)) {
const values = data.map((item: any) => Number(item[column.property]) || 0)
sums[index] = values.reduce((prev: number, curr: number) => prev + curr, 0)
} else {
sums[index] = ''
}
})
return sums
}
/** 初始化 */
onMounted(() => {
getList()
})
</script>
<style scoped>
:deep(.el-table__footer-wrapper td) {
text-align: center;
}
</style>