Merge remote-tracking branch 'origin/master'

This commit is contained in:
z 2026-05-13 16:32:19 +08:00
commit 2cb4070fe0
13 changed files with 507 additions and 183 deletions

View File

@ -11,6 +11,7 @@ import com.ningxia.yunxi.chemmes.module.biz.controller.admin.customer.vo.Custome
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.customer.vo.CustomerSaveReqVO; import com.ningxia.yunxi.chemmes.module.biz.controller.admin.customer.vo.CustomerSaveReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.customer.CustomerDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.customer.CustomerDO;
import com.ningxia.yunxi.chemmes.module.biz.service.customer.CustomerService; import com.ningxia.yunxi.chemmes.module.biz.service.customer.CustomerService;
import com.ningxia.yunxi.chemmes.module.system.service.user.AdminUserService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -36,6 +37,9 @@ public class CustomerController {
@Resource @Resource
private CustomerService customerService; private CustomerService customerService;
@Resource
private AdminUserService userService;
@PostMapping("/create") @PostMapping("/create")
@Operation(summary = "创建客户主数据") @Operation(summary = "创建客户主数据")
@PreAuthorize("@ss.hasPermission('biz:customer:create')") @PreAuthorize("@ss.hasPermission('biz:customer:create')")
@ -82,12 +86,19 @@ public class CustomerController {
@PreAuthorize("@ss.hasPermission('biz:customer:export')") @PreAuthorize("@ss.hasPermission('biz:customer:export')")
@OperateLog(type = EXPORT) @OperateLog(type = EXPORT)
public void exportCustomerExcel(@Valid CustomerPageReqVO pageReqVO, public void exportCustomerExcel(@Valid CustomerPageReqVO pageReqVO,
HttpServletResponse response) throws IOException { HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CustomerDO> list = customerService.getCustomerPage(pageReqVO).getList(); List<CustomerDO> list = customerService.getCustomerPage(pageReqVO).getList();
// 导出 Excel // 导出 Excel
ExcelUtils.write(response, "客户主数据.xls", "数据", CustomerRespVO.class, ExcelUtils.write(response, "客户主数据.xls", "数据", CustomerRespVO.class,
BeanUtils.toBean(list, CustomerRespVO.class)); BeanUtils.toBean(list, CustomerRespVO.class));
} }
// 客户下拉框
@GetMapping("/dropdown")
@Operation(summary = "获得客户主数据下拉框")
@PreAuthorize("@ss.hasPermission('biz:customer:query')")
public CommonResult<List<CustomerRespVO>> getCustomerSelect(@RequestParam(value = "keyWord", required = false, defaultValue = "") String keyWord) {
return success(BeanUtils.toBean(customerService.getCustomerSelect(keyWord), CustomerRespVO.class));
}
} }

View File

@ -1,5 +1,6 @@
package com.ningxia.yunxi.chemmes.module.biz.controller.admin.order; package com.ningxia.yunxi.chemmes.module.biz.controller.admin.order;
import cn.hutool.core.collection.CollUtil;
import com.ningxia.yunxi.chemmes.framework.common.pojo.CommonResult; 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.PageParam;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult; import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
@ -11,6 +12,8 @@ import com.ningxia.yunxi.chemmes.module.biz.controller.admin.order.vo.OrderRespV
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.order.vo.OrderSaveReqVO; import com.ningxia.yunxi.chemmes.module.biz.controller.admin.order.vo.OrderSaveReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.order.OrderDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.order.OrderDO;
import com.ningxia.yunxi.chemmes.module.biz.service.order.OrderService; import com.ningxia.yunxi.chemmes.module.biz.service.order.OrderService;
import com.ningxia.yunxi.chemmes.module.system.dal.dataobject.user.AdminUserDO;
import com.ningxia.yunxi.chemmes.module.system.service.user.AdminUserService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -28,7 +31,6 @@ import static com.ningxia.yunxi.chemmes.framework.common.pojo.CommonResult.succe
import static com.ningxia.yunxi.chemmes.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static com.ningxia.yunxi.chemmes.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 销售订单主") @Tag(name = "管理后台 - 销售订单主")
@RestController @RestController
@RequestMapping("/tso/order") @RequestMapping("/tso/order")
@ -38,6 +40,9 @@ public class TsoOrderController {
@Resource @Resource
private OrderService orderService; private OrderService orderService;
@Resource
private AdminUserService userService;
@PostMapping("/create") @PostMapping("/create")
@Operation(summary = "创建销售订单主") @Operation(summary = "创建销售订单主")
@PreAuthorize("@ss.hasPermission('tso:order:create')") @PreAuthorize("@ss.hasPermission('tso:order:create')")
@ -77,7 +82,16 @@ public class TsoOrderController {
@PreAuthorize("@ss.hasPermission('tso:order:query')") @PreAuthorize("@ss.hasPermission('tso:order:query')")
public CommonResult<PageResult<OrderRespVO>> getOrderPage(@Valid OrderPageReqVO pageReqVO) { public CommonResult<PageResult<OrderRespVO>> getOrderPage(@Valid OrderPageReqVO pageReqVO) {
PageResult<OrderDO> pageResult = orderService.getOrderPage(pageReqVO); PageResult<OrderDO> pageResult = orderService.getOrderPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, OrderRespVO.class)); PageResult<OrderRespVO> respPageResult = BeanUtils.toBean(pageResult, OrderRespVO.class);
if (CollUtil.isNotEmpty(respPageResult.getList())) {
respPageResult.getList().forEach(item -> {
AdminUserDO userEntity = userService.getUser(item.getSaleMan());
if (userEntity != null) {
item.setSaleManName(userEntity.getUsername());
}
});
}
return success(respPageResult);
} }
@GetMapping("/export-excel") @GetMapping("/export-excel")
@ -85,12 +99,12 @@ public class TsoOrderController {
@PreAuthorize("@ss.hasPermission('tso:order:export')") @PreAuthorize("@ss.hasPermission('tso:order:export')")
@OperateLog(type = EXPORT) @OperateLog(type = EXPORT)
public void exportOrderExcel(@Valid OrderPageReqVO pageReqVO, public void exportOrderExcel(@Valid OrderPageReqVO pageReqVO,
HttpServletResponse response) throws IOException { HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<OrderDO> list = orderService.getOrderPage(pageReqVO).getList(); List<OrderDO> list = orderService.getOrderPage(pageReqVO).getList();
// 导出 Excel // 导出 Excel
ExcelUtils.write(response, "销售订单主.xls", "数据", OrderRespVO.class, ExcelUtils.write(response, "销售订单主.xls", "数据", OrderRespVO.class,
BeanUtils.toBean(list, OrderRespVO.class)); BeanUtils.toBean(list, OrderRespVO.class));
} }
} }

View File

@ -2,6 +2,7 @@ package com.ningxia.yunxi.chemmes.module.biz.controller.admin.order.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@ -21,6 +22,7 @@ public class OrderRespVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间") @ExcelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime; private LocalDateTime createTime;
@Schema(description = "销售订单编号(SO+年份+月份+3位流水号)") @Schema(description = "销售订单编号(SO+年份+月份+3位流水号)")
@ -29,6 +31,7 @@ public class OrderRespVO {
@Schema(description = "下单日期") @Schema(description = "下单日期")
@ExcelProperty("下单日期") @ExcelProperty("下单日期")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate ordDate; private LocalDate ordDate;
@Schema(description = "客户id", example = "23476") @Schema(description = "客户id", example = "23476")
@ -51,6 +54,8 @@ public class OrderRespVO {
@ExcelProperty("业务员id") @ExcelProperty("业务员id")
private Long saleMan; private Long saleMan;
private String saleManName;
@Schema(description = "业务部门id", example = "11778") @Schema(description = "业务部门id", example = "11778")
@ExcelProperty("业务部门id") @ExcelProperty("业务部门id")
private Long saleDeptId; private Long saleDeptId;
@ -73,6 +78,7 @@ public class OrderRespVO {
@Schema(description = "要求交货日期") @Schema(description = "要求交货日期")
@ExcelProperty("要求交货日期") @ExcelProperty("要求交货日期")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate reqDeliveryDate; private LocalDate reqDeliveryDate;
@Schema(description = "是否急单(0 是 1 否)") @Schema(description = "是否急单(0 是 1 否)")
@ -109,6 +115,7 @@ public class OrderRespVO {
@Schema(description = "审核时间") @Schema(description = "审核时间")
@ExcelProperty("审核时间") @ExcelProperty("审核时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime auditTime; private LocalDateTime auditTime;
@Schema(description = "附件信息") @Schema(description = "附件信息")

View File

@ -7,6 +7,8 @@ import com.ningxia.yunxi.chemmes.module.biz.controller.admin.customer.vo.Custome
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.customer.CustomerDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.customer.CustomerDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/** /**
* 客户主数据 Mapper * 客户主数据 Mapper
* *
@ -32,4 +34,9 @@ public interface CustomerMapper extends BaseMapperX<CustomerDO> {
return selectOne(CustomerDO::getCustName, custName); return selectOne(CustomerDO::getCustName, custName);
} }
default List<CustomerDO> selectCustomerSelect(String keyWord) {
return selectList(new LambdaQueryWrapperX<CustomerDO>()
.eq(CustomerDO::getEnabledStatus, 0));
// .likeIfPresent(CustomerDO::getCustName, keyWord));
}
} }

View File

@ -1,55 +1,57 @@
package com.ningxia.yunxi.chemmes.module.biz.service.customer; package com.ningxia.yunxi.chemmes.module.biz.service.customer;
import java.util.*; import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import javax.validation.*; import com.ningxia.yunxi.chemmes.module.biz.controller.admin.customer.vo.CustomerPageReqVO;
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.customer.vo.*; import com.ningxia.yunxi.chemmes.module.biz.controller.admin.customer.vo.CustomerSaveReqVO;
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.customer.CustomerDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.customer.CustomerDO;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageParam; import javax.validation.Valid;
import java.util.List;
/**
* 客户主数据 Service 接口 /**
* * 客户主数据 Service 接口
* @author 管理员 *
*/ * @author 管理员
public interface CustomerService { */
public interface CustomerService {
/**
* 创建客户主数据 /**
* * 创建客户主数据
* @param createReqVO 创建信息 *
* @return 编号 * @param createReqVO 创建信息
*/ * @return 编号
Integer createCustomer(@Valid CustomerSaveReqVO createReqVO); */
Integer createCustomer(@Valid CustomerSaveReqVO createReqVO);
/**
* 更新客户主数据 /**
* * 更新客户主数据
* @param updateReqVO 更新信息 *
*/ * @param updateReqVO 更新信息
void updateCustomer(@Valid CustomerSaveReqVO updateReqVO); */
void updateCustomer(@Valid CustomerSaveReqVO updateReqVO);
/**
* 删除客户主数据 /**
* * 删除客户主数据
* @param id 编号 *
*/ * @param id 编号
void deleteCustomer(Integer id); */
void deleteCustomer(Integer id);
/**
* 获得客户主数据 /**
* * 获得客户主数据
* @param id 编号 *
* @return 客户主数据 * @param id 编号
*/ * @return 客户主数据
CustomerDO getCustomer(Integer id); */
CustomerDO getCustomer(Integer id);
/**
* 获得客户主数据分页 /**
* * 获得客户主数据分页
* @param pageReqVO 分页查询 *
* @return 客户主数据分页 * @param pageReqVO 分页查询
*/ * @return 客户主数据分页
PageResult<CustomerDO> getCustomerPage(CustomerPageReqVO pageReqVO); */
PageResult<CustomerDO> getCustomerPage(CustomerPageReqVO pageReqVO);
}
List<CustomerDO> getCustomerSelect(String keyWord);
}

View File

@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
import static com.ningxia.yunxi.chemmes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.ningxia.yunxi.chemmes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.ningxia.yunxi.chemmes.module.biz.enums.ErrorCodeConstants.CUSTOMER_CODE_DUPLICATE; import static com.ningxia.yunxi.chemmes.module.biz.enums.ErrorCodeConstants.CUSTOMER_CODE_DUPLICATE;
@ -104,4 +105,8 @@ public class CustomerServiceImpl implements CustomerService {
return customerMapper.selectPage(pageReqVO); return customerMapper.selectPage(pageReqVO);
} }
@Override
public List<CustomerDO> getCustomerSelect(String keyWord) {
return customerMapper.selectCustomerSelect(keyWord);
}
} }

View File

@ -66,7 +66,10 @@ public class OrderServiceImpl implements OrderService {
return; return;
} }
List<OrderItemDO> orderItems = BeanUtils.toBean(list, OrderItemDO.class); List<OrderItemDO> orderItems = BeanUtils.toBean(list, OrderItemDO.class);
orderItems.forEach(item -> item.setSaleOrdId(saleOrdId)); orderItems.forEach(item ->
item.setSaleOrdId(saleOrdId)
.setId(null)
);
orderItemMapper.insertBatch(orderItems); orderItemMapper.insertBatch(orderItems);
} }

View File

@ -1,56 +1,59 @@
import request from '@/config/axios' import request from '@/config/axios'
export interface CustomerVO { export interface CustomerVO {
id: number id: number
custNo: string custNo: string
custName: string custName: string
custSimName: string custSimName: string
industryClass: string industryClass: string
coopStatus: string coopStatus: string
enterpriseType: string enterpriseType: string
custReg: string custReg: string
creditRate: string creditRate: string
contact1: string contact1: string
conPhone1: string conPhone1: string
conAddress1: string conAddress1: string
contact2: string contact2: string
conPhone2: string conPhone2: string
conAddress2: string conAddress2: string
comTaxNumber: string comTaxNumber: string
accountRegion: string accountRegion: string
accountBank: string accountBank: string
accountNo: string accountNo: string
payMeth: string payMeth: string
remark: string remark: string
enabledStatus: string enabledStatus: string
} }
// 查询客户主数据分页 // 查询客户主数据分页
export const getCustomerPage = async (params) => { export const getCustomerPage = async (params) => {
return await request.get({ url: `/biz/customer/page`, params }) return await request.get({ url: `/biz/customer/page`, params })
} }
export const getCustomerSelect = async (params) => {
// 查询客户主数据详情 return await request.get({ url: `/biz/customer/dropdown`, params })
export const getCustomer = async (id: number) => { }
return await request.get({ url: `/biz/customer/get?id=` + id })
} // 查询客户主数据详情
export const getCustomer = async (id: number) => {
// 新增客户主数据 return await request.get({ url: `/biz/customer/get?id=` + id })
export const createCustomer = async (data: CustomerVO) => { }
return await request.post({ url: `/biz/customer/create`, data })
} // 新增客户主数据
export const createCustomer = async (data: CustomerVO) => {
// 修改客户主数据 return await request.post({ url: `/biz/customer/create`, data })
export const updateCustomer = async (data: CustomerVO) => { }
return await request.put({ url: `/biz/customer/update`, data })
} // 修改客户主数据
export const updateCustomer = async (data: CustomerVO) => {
// 删除客户主数据 return await request.put({ url: `/biz/customer/update`, data })
export const deleteCustomer = async (id: number) => { }
return await request.delete({ url: `/biz/customer/delete?id=` + id })
} // 删除客户主数据
export const deleteCustomer = async (id: number) => {
// 导出客户主数据 Excel return await request.delete({ url: `/biz/customer/delete?id=` + id })
export const exportCustomer = async (params) => { }
return await request.download({ url: `/biz/customer/export-excel`, params })
} // 导出客户主数据 Excel
export const exportCustomer = async (params) => {
return await request.download({ url: `/biz/customer/export-excel`, params })
}

View File

@ -44,7 +44,8 @@ export const deleteDept = async (id: number) => {
export const getDeptSimpleName = async (id: number) => { export const getDeptSimpleName = async (id: number) => {
const dept = await getDept(id); const dept = await getDept(id);
return dept.name; // 兼容CommonResult格式
return dept.data?.name || dept.name;
} }
// 查询部门数据 // 查询部门数据

View File

@ -345,7 +345,20 @@ export function getLast1Year(): [dayjs.ConfigType, dayjs.ConfigType] {
} }
/** /**
* * 1 00:00:00 23:59:59
* @returns [, ]
*/
export function getCurrentMonthRange(): [string, string] {
const now = dayjs()
return [
now.startOf('month').format('YYYY-MM-DD HH:mm:ss'),
now.endOf('day').format('YYYY-MM-DD HH:mm:ss')
]
}
/**
*
*
* @param beginDate * @param beginDate
* @param endDate * @param endDate
*/ */

View File

@ -7,9 +7,9 @@
<el-form-item label="部门名称" prop="name"> <el-form-item label="部门名称" prop="name">
<el-input v-model="formData.name" class="!w-250px" placeholder="请输入部门名称" /> <el-input v-model="formData.name" class="!w-250px" placeholder="请输入部门名称" />
</el-form-item> </el-form-item>
<!-- <el-form-item label="显示排序" prop="sort"> <el-form-item label="显示排序" prop="sort">
<el-input-number v-model="formData.sort" :min="0 " controls-position="right" class="!w-250px" /> <el-input-number v-model="formData.sort" :min="0 " controls-position="right" class="!w-250px" />
</el-form-item> --> </el-form-item>
<el-form-item label="负责人" prop="leaderUserId"> <el-form-item label="负责人" prop="leaderUserId">
<UserSelect v-model="formData.leaderUserId" @update:new-value="handleSelectedUser" /> <UserSelect v-model="formData.leaderUserId" @update:new-value="handleSelectedUser" />
</el-form-item> </el-form-item>
@ -28,7 +28,7 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<!-- <el-button type="primary" @click="submitForm"> </el-button> --> <el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
</Dialog> </Dialog>
@ -57,7 +57,7 @@ const formData: any = ref({
title: '', title: '',
parentId: undefined, parentId: undefined,
name: undefined, name: undefined,
sort: undefined, sort: 0,
leaderUserId: undefined, leaderUserId: undefined,
phone: undefined, phone: undefined,
email: undefined, email: undefined,
@ -158,7 +158,7 @@ const resetForm = () => {
title: '', title: '',
parentId: undefined, parentId: undefined,
name: undefined, name: undefined,
sort: undefined, sort: 0,
leaderUserId: undefined, leaderUserId: undefined,
phone: undefined, phone: undefined,
email: undefined, email: undefined,

View File

@ -36,7 +36,7 @@
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="订单状态" prop="ordStatus" > <el-form-item label="订单状态" prop="ordStatus" >
<el-select v-model="formData.ordStatus" placeholder="请选择订单状态"> <el-select v-model="formData.ordStatus" placeholder="请选择订单状态" disabled>
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.ORDER_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.ORDER_STATUS)"
:key="dict.value" :key="dict.value"
@ -62,24 +62,67 @@
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="6"> <el-col :span="6">
<el-form-item label="业务部门" prop="saleDeptId" > <el-form-item label="业务部门" prop="saleDeptId" >
<el-input v-model="formData.saleDeptId" placeholder="请输入业务部门" /> <el-input
v-model="formData.saleDeptName"
placeholder="请选择业务部门"
readonly
@click="openDeptSelect"
>
<template #suffix>
<Icon icon="ep:caret-bottom" />
</template>
</el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="业务人员" prop="saleMan" > <el-form-item label="业务人员" prop="saleMan" >
<el-input v-model="formData.saleMan" placeholder="请输入业务人员" /> <el-select
v-model="formData.saleMan"
placeholder="请选择业务人员"
filterable
remote
reserve-keyword
:remote-method="searchUsers"
:loading="userSelectLoading"
@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-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="客户名称" prop="custName" > <el-form-item label="客户名称" prop="custId" >
<el-input v-model="formData.custName" placeholder="请输入客户名称" /> <el-select
v-model="formData.custId"
filterable
:loading="customerLoading"
placeholder="请选择客户名称"
@change="handleCustomerChange"
>
<el-option
v-for="customer in customerOptions"
:key="customer.id"
:label="customer.custName"
:value="customer.id"
/>
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="是否急单" prop="isUrgent" > <el-form-item label="是否急单" prop="isUrgent" >
<el-radio-group v-model="formData.isUrgent"> <el-radio-group v-model="formData.isUrgent">
<el-radio label="Y"></el-radio> <el-radio
<el-radio label="N"></el-radio> v-for="dict in getStrDictOptions(DICT_TYPE.SYSTEM_IS_CELL)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -115,13 +158,19 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="是否变更" prop="isChange"> <el-form-item label="是否变更" prop="isChange" >
<el-radio-group v-model="formData.isChange" disabled> <el-radio-group v-model="formData.isChange" disabled>
<el-radio label="Y"></el-radio> <el-radio
<el-radio label="N"></el-radio> v-for="dict in getStrDictOptions(DICT_TYPE.SYSTEM_IS_CELL)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 第四行税率含税总金额发货状态 --> <!-- 第四行税率含税总金额发货状态 -->
<el-row :gutter="20"> <el-row :gutter="20">
@ -172,11 +221,22 @@
<Icon icon="ep:plus" /> 新增 <Icon icon="ep:plus" /> 新增
</el-button> </el-button>
</div> </div>
<el-table :data="productList" border :show-overflow-tooltip="true" v-bind="{ emptyText: '加载中...' }"> <el-table
ref="productTableRef"
:data="productList"
stripe
style="width: 100%; display: block;"
:show-overflow-tooltip="true"
>
<el-table-column label="序号" align="center" type="index" width="60px" /> <el-table-column label="序号" align="center" type="index" width="60px" />
<el-table-column label="产品名称(*)" align="center" prop="materialName" /> <el-table-column label="产品名称(*)" align="center" prop="materialName" />
<el-table-column label="规格型号(*)" align="center" prop="spec" /> <el-table-column label="规格型号(*)" align="center" prop="spec" />
<el-table-column label="单位(*)" align="center" prop="unit" /> <el-table-column label="单位(*)" align="center" prop="unit" >
<template #default="scope">
<dict-tag :type="DICT_TYPE.UNIT" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column label="订单数量(*)" align="center" prop="ordQty"> <el-table-column label="订单数量(*)" align="center" prop="ordQty">
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.ordQty" placeholder="请输入" /> <el-input v-model="scope.row.ordQty" placeholder="请输入" />
@ -231,20 +291,29 @@
</div> </div>
<!-- 物料选择弹窗 --> <!-- 物料选择弹窗 -->
<MaterialSelect <MaterialSelect
v-if="materialSelectVisible" v-if="materialSelectVisible"
ref="materialSelectRef" ref="materialSelectRef"
@confirm="handleMaterialSelect" @confirm="handleMaterialSelect"
/> />
<!-- 部门选择弹窗 -->
<el-dialog v-model="deptSelectVisible" title="选择部门" width="400px" append-to-body>
<DeptTree ref="deptTreeRef" @node-click="handleDeptNodeClick" />
</el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, watch, nextTick } from 'vue' import { ref, reactive, nextTick } from 'vue'
import * as OrderApi from '@/api/biz/tsoorder/' import * as OrderApi from '@/api/biz/tsoorder/'
import * as CustomerApi from '@/api/biz/customer'
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict' import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { getDeptSimpleName } from '@/api/system/dept'
import * as UserApi from '@/api/system/user'
import { Icon } from '@/components/Icon' import { Icon } from '@/components/Icon'
import MaterialSelect from '@/views/biz/material/MaterialSelect.vue' import MaterialSelect from '@/views/biz/material/MaterialSelect.vue'
import DeptTree from '@/views/system/user/DeptTree.vue'
const { t } = useI18n() const { t } = useI18n()
const message = useMessage() const message = useMessage()
@ -256,6 +325,11 @@ const formType = ref('')
const formRef = ref() const formRef = ref()
const materialSelectRef = ref() const materialSelectRef = ref()
const materialSelectVisible = ref(false) const materialSelectVisible = ref(false)
const productTableRef = ref()
const deptSelectVisible = ref(false)
const deptTreeRef = ref()
const userList = ref<UserApi.UserVO[]>([])
const userSelectLoading = ref(false)
// //
const purposeOptions = ref([]) const purposeOptions = ref([])
@ -268,6 +342,7 @@ const initDictOptions = () => {
// //
initDictOptions() initDictOptions()
const formData = reactive({ const formData = reactive({
id: undefined, id: undefined,
saleOrdNo: '自动生成', saleOrdNo: '自动生成',
@ -277,7 +352,9 @@ const formData = reactive({
contact: undefined, contact: undefined,
conPhone: undefined, conPhone: undefined,
saleMan: undefined, saleMan: undefined,
saleManName: undefined,
saleDeptId: undefined, saleDeptId: undefined,
saleDeptName: undefined,
ordType: undefined, ordType: undefined,
paymentTerms: undefined, paymentTerms: undefined,
taxRate: undefined, taxRate: undefined,
@ -285,7 +362,7 @@ const formData = reactive({
reqDeliveryDate: undefined, reqDeliveryDate: undefined,
isUrgent: '0', isUrgent: '0',
isChange: '0', isChange: '0',
ordStatus: 0, ordStatus: 1,
contractNo: undefined, contractNo: undefined,
packReq: undefined, packReq: undefined,
qualityReq: undefined, qualityReq: undefined,
@ -293,17 +370,22 @@ const formData = reactive({
auditId: undefined, auditId: undefined,
auditTime: undefined, auditTime: undefined,
attFile: undefined, attFile: undefined,
proStatus: undefined, proStatus: 0,
deliveryStatus: 0, deliveryStatus: 1,
}) })
// //
const productList = ref([]) const productList = ref([])
//
const customerOptions = ref<any[]>([])
const customerLoading = ref(false)
const formRules = reactive({ const formRules = reactive({
ordType: [{ required: true, message: '订单类型不能为空', trigger: 'change' }], ordType: [{ required: true, message: '订单类型不能为空', trigger: 'change' }],
ordStatus: [{ required: true, message: '订单状态不能为空', trigger: 'change' }], ordStatus: [{ required: true, message: '订单状态不能为空', trigger: 'change' }],
ordDate: [{ required: true, message: '下单日期不能为空', trigger: 'change' }], ordDate: [{ required: true, message: '下单日期不能为空', trigger: 'change' }],
custId: [{ required: true, message: '客户名称不能为空', trigger: 'change' }],
saleDeptId: [{ required: true, message: '业务部门不能为空', trigger: 'change' }], saleDeptId: [{ required: true, message: '业务部门不能为空', trigger: 'change' }],
saleMan: [{ required: true, message: '业务人员不能为空', trigger: 'change' }], saleMan: [{ required: true, message: '业务人员不能为空', trigger: 'change' }],
isUrgent: [{ required: true, message: '是否加急不能为空', trigger: 'change' }], isUrgent: [{ required: true, message: '是否加急不能为空', trigger: 'change' }],
@ -313,6 +395,21 @@ const formRules = reactive({
const emit = defineEmits(['success', 'close']) const emit = defineEmits(['success', 'close'])
/** 详情偶发多包一层:根上无 items但 data.items 为数组时取内层 */
const unwrapOrderDetail = (raw: Record<string, any>) => {
if (!raw || typeof raw !== 'object') {
return raw
}
if (Array.isArray(raw.items)) {
return raw
}
const inner = raw.data
if (inner && typeof inner === 'object' && !Array.isArray(inner) && Array.isArray(inner.items)) {
return inner
}
return raw
}
const open = async (type: string, id?: number) => { const open = async (type: string, id?: number) => {
formType.value = type formType.value = type
dialogTitle.value = t('action.' + type) dialogTitle.value = t('action.' + type)
@ -320,40 +417,52 @@ const open = async (type: string, id?: number) => {
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
const data = await OrderApi.getOrder(id) const response = await OrderApi.getOrder(id)
console.log('编辑模式 - 订单数据:', data) console.log('API完整响应:', JSON.stringify(response))
// // responseCommonResultdata
const data = response.data || response
if (!data || typeof data !== 'object') {
console.error('API响应数据格式异常:', response)
return
}
// items
const normalizeValue = (val: any) => { const normalizeValue = (val: any) => {
if (Array.isArray(val) && val.length > 0) { if (Array.isArray(val) && val.length > 0) {
return val[0] return val[0]
} }
return val return val
} }
// dayjs //
const normalizeDate = (val: any) => { const normalizeDate = (val: any) => {
if (!val) return undefined if (!val) return undefined
if (typeof val === 'string') { if (typeof val === 'string') {
//
return val return val
} }
return val return val
} }
// data // data items productList
const cleanedData: any = {} const cleanedData: any = {}
for (const key in data) { for (const key in data) {
if (key === 'items') {
continue
}
if (key === 'ordDate' || key === 'reqDeliveryDate') { if (key === 'ordDate' || key === 'reqDeliveryDate') {
cleanedData[key] = normalizeDate(data[key]) cleanedData[key] = normalizeDate(data[key])
} else if (key === 'ordStatus' || key === 'deliveryStatus' || key === 'proStatus') {
//
cleanedData[key] = Number(normalizeValue(data[key])) || 0
} else { } else {
cleanedData[key] = normalizeValue(data[key]) cleanedData[key] = normalizeValue(data[key])
} }
} }
console.log('清理后的数据:', cleanedData)
Object.assign(formData, cleanedData) Object.assign(formData, cleanedData)
// //
if (formData.ordStatus === undefined || formData.ordStatus === null) { if (formData.ordStatus === undefined || formData.ordStatus === null) {
formData.ordStatus = 0 formData.ordStatus = 0
@ -361,16 +470,41 @@ const open = async (type: string, id?: number) => {
if (formData.deliveryStatus === undefined || formData.deliveryStatus === null) { if (formData.deliveryStatus === undefined || formData.deliveryStatus === null) {
formData.deliveryStatus = 0 formData.deliveryStatus = 0
} }
if (data.items && data.items.length > 0) {
console.log('设置产品明细:', data.items) //
productList.value = [...data.items] if (formData.saleDeptId) {
console.log('productList:', productList.value) try {
const deptName = await getDeptSimpleName(formData.saleDeptId)
formData.saleDeptName = deptName
//
await searchUsers('')
} catch (e) {
console.error('获取部门名称失败', e)
}
} }
//
if (formData.saleMan) {
try {
formData.saleManName = await getUserNameById(formData.saleMan)
} catch (e) {
console.error('获取业务人员名称失败', e)
}
}
//
const rawItems = data?.items
const rows = Array.isArray(rawItems) ? rawItems : (rawItems != null ? [rawItems] : [])
productList.value = rows.length ? rows.map((r) => ({ ...r })) : []
console.log('处理后productList:', productList.value)
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
} }
dialogVisible.value = true dialogVisible.value = true
await nextTick()
productTableRef.value?.doLayout?.()
} }
const handleCancel = async () => { const handleCancel = async () => {
@ -455,15 +589,17 @@ const resetForm = () => {
formData.contact = undefined formData.contact = undefined
formData.conPhone = undefined formData.conPhone = undefined
formData.saleMan = undefined formData.saleMan = undefined
formData.saleManName = undefined
formData.saleDeptId = undefined formData.saleDeptId = undefined
formData.saleDeptName = undefined
formData.ordType = undefined formData.ordType = undefined
formData.paymentTerms = undefined formData.paymentTerms = undefined
formData.taxRate = undefined formData.taxRate = undefined
formData.totalAmount = undefined formData.totalAmount = undefined
formData.reqDeliveryDate = undefined formData.reqDeliveryDate = undefined
formData.isUrgent = 'N' formData.isUrgent = '0'
formData.isChange = 'N' formData.isChange = '0'
formData.ordStatus = 0 formData.ordStatus = 1
formData.contractNo = undefined formData.contractNo = undefined
formData.packReq = undefined formData.packReq = undefined
formData.qualityReq = undefined formData.qualityReq = undefined
@ -471,9 +607,11 @@ const resetForm = () => {
formData.auditId = undefined formData.auditId = undefined
formData.auditTime = undefined formData.auditTime = undefined
formData.attFile = undefined formData.attFile = undefined
formData.proStatus = undefined formData.proStatus = 0
formData.deliveryStatus = 0 formData.deliveryStatus = 1
delete (formData as any).items
productList.value = [] productList.value = []
userList.value = []
} }
// //
@ -485,6 +623,98 @@ const openMaterialSelect = async () => {
materialSelectRef.value?.open(selectedIds) materialSelectRef.value?.open(selectedIds)
} }
//
const openDeptSelect = () => {
deptSelectVisible.value = true
}
//
const handleDeptNodeClick = (dept: any) => {
formData.saleDeptId = dept.id
formData.saleDeptName = dept.name
//
formData.saleMan = undefined
formData.saleManName = undefined
searchUsers('')
deptSelectVisible.value = false
}
//
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.saleDeptId) {
params.deptId = formData.saleDeptId
}
const data = await UserApi.getUserPage(params)
userList.value = data.list || []
} finally {
userSelectLoading.value = false
}
}
//
const handleUserChange = async (userId: number) => {
if (userId) {
const user = userList.value.find(u => u.id === userId)
if (user) {
formData.saleManName = user.nickname
}
} else {
formData.saleManName = undefined
}
}
//
//
const searchCustomers = async () => {
customerLoading.value = true
try {
const response = await CustomerApi.getCustomerSelect({})
const data = response.data || response
customerOptions.value = data || []
} catch (error) {
console.error('加载客户失败:', error)
customerOptions.value = []
} finally {
customerLoading.value = false
}
}
//
const handleCustomerChange = (custId: number) => {
if (!custId) {
formData.custName = undefined
return
}
const customer = customerOptions.value.find(c => c.id === custId)
if (customer) {
formData.custName = customer.custName
}
}
//
searchCustomers()
//
const getUserNameById = async (userId: number) => {
try {
const user = await UserApi.getUser(userId)
return user.data?.nickname || user.nickname || ''
} catch {
return ''
}
}
// //
const handleMaterialSelect = (materials: any[]) => { const handleMaterialSelect = (materials: any[]) => {
const existingIds = productList.value.map(item => item.materialId) const existingIds = productList.value.map(item => item.materialId)
@ -504,6 +734,7 @@ const handleMaterialSelect = (materials: any[]) => {
}) })
} }
}) })
nextTick(() => productTableRef.value?.doLayout?.())
} }
// //
@ -579,6 +810,8 @@ const removeProductItem = (index: number) => {
.form-content { .form-content {
flex: 1; flex: 1;
min-height: 0;
min-width: 0;
overflow-y: auto; overflow-y: auto;
padding: 20px; padding: 20px;
padding-bottom: 80px; /* 预留底部按钮空间 */ padding-bottom: 80px; /* 预留底部按钮空间 */
@ -593,6 +826,10 @@ const removeProductItem = (index: number) => {
border-radius: 8px; border-radius: 8px;
} }
.product-detail {
overflow: visible !important;
}
.section-header { .section-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -611,6 +848,10 @@ const removeProductItem = (index: number) => {
margin: 0; margin: 0;
} }
.order-product-table {
width: 100%;
}
.text-right { .text-right {
text-align: right; text-align: right;
} }

View File

@ -84,40 +84,48 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" border>
<el-table-column label="序号" align="center" type="index" width="60px"/> <el-table-column label="序号" align="center" type="index" width="60px" fixed="left" />
<el-table-column label="订单编号" align="center" prop="saleOrdNo" /> <el-table-column label="订单编号" align="center" prop="saleOrdNo" width="150px" fixed="left" />
<el-table-column label="下单日期" align="center" prop="ordDate" /> <el-table-column label="下单日期" align="center" prop="ordDate" width="110px" fixed="left" />
<el-table-column label="客户名称" align="center" prop="custName" /> <el-table-column label="客户名称" align="center" prop="custName" width="250px" />
<el-table-column label="订单类型" align="center" prop="ordType" /> <el-table-column label="订单类型" align="center" prop="ordType" width="150px">
<el-table-column label="交货日期" align="center" prop="reqDeliveryDate" /> <template #default="scope">
<el-table-column label="是否急单" prop="isUrgent"> <dict-tag :type="DICT_TYPE.ORDER_TYPE" :value="scope.row.ordType" />
</template>
</el-table-column>
<el-table-column label="交货日期" align="center" prop="reqDeliveryDate" width="120px" />
<el-table-column label="是否急单" prop="isUrgent" align="center" width="120px">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_IS_CELL" :value="scope.row.isUrgent" /> <dict-tag :type="DICT_TYPE.SYSTEM_IS_CELL" :value="scope.row.isUrgent" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="合同编号" align="center" prop="contractNo" /> <el-table-column label="合同编号" align="center" prop="contractNo" width="120px" />
<el-table-column label="业务人员" align="center" prop="saleMan" /> <el-table-column label="业务人员" align="center" prop="saleMan" width="120px" />
<el-table-column label="是否变更" align="center" prop="isChangeOrder" /> <el-table-column label="是否变更" align="center" prop="isChange" width="120px">
<el-table-column label="生产状态" prop="proStatus"> <template #default="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_IS_CELL" :value="scope.row.isChange" />
</template>
</el-table-column>
<el-table-column label="生产状态" align="center" prop="proStatus" width="120px">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.PLAN_STATUS" :value="scope.row.proStatus" /> <dict-tag :type="DICT_TYPE.PLAN_STATUS" :value="scope.row.proStatus" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="包装要求" align="center" prop="packReq" /> <el-table-column label="包装要求" align="center" prop="packReq" width="180px" />
<el-table-column label="质量要求" align="center" prop="qualityReq" /> <el-table-column label="质量要求" align="center" prop="qualityReq" width="180px" />
<el-table-column label="订单状态" prop="ordStatus"> <el-table-column label="订单状态" prop="ordStatus" width="120px">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.ORDER_STATUS" :value="scope.row.ordStatus" /> <dict-tag :type="DICT_TYPE.ORDER_STATUS" :value="scope.row.ordStatus" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="发货状态" prop="deliveryStatus"> <el-table-column label="发货状态" align="center" prop="deliveryStatus" width="120px">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.SHIPPING_STATUS" :value="scope.row.deliveryStatus" /> <dict-tag :type="DICT_TYPE.SHIPPING_STATUS" :value="scope.row.deliveryStatus" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center" width="180px" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button <el-button
link link
@ -153,7 +161,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict' import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter, getCurrentMonthRange } from '@/utils/formatTime'
import * as OrderApi from '@/api/biz/tsoorder/index' import * as OrderApi from '@/api/biz/tsoorder/index'
import OrderForm from './OrderForm.vue' import OrderForm from './OrderForm.vue'
@ -173,6 +181,15 @@ const queryParams = reactive({
ordType: undefined, ordType: undefined,
ordStatus: undefined, ordStatus: undefined,
}) })
// 1
const setDefaultDateRange = () => {
queryParams.ordDate = getCurrentMonthRange()
}
//
setDefaultDateRange()
const queryFormRef = ref() // const queryFormRef = ref() //
const exportLoading = ref(false) // const exportLoading = ref(false) //