新增前端订单列表

This commit is contained in:
Mrking 2024-09-23 22:45:07 +08:00
parent c9d84bd169
commit fe4019d27d
15 changed files with 2387 additions and 11 deletions

View File

@ -1,35 +1,193 @@
package cn.hangtag.module.oms.controller.admin.app;
import cn.hangtag.framework.apilog.core.annotation.ApiAccessLog;
import cn.hangtag.framework.common.pojo.CommonResult;
import cn.hangtag.framework.common.pojo.PageParam;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.util.number.NumberUtils;
import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.framework.excel.core.util.ExcelUtils;
import cn.hangtag.framework.security.core.LoginUser;
import cn.hangtag.framework.security.core.util.SecurityFrameworkUtils;
import cn.hangtag.module.oms.controller.admin.productinfo.vo.ProductInfoPageReqVO;
import cn.hangtag.module.oms.controller.admin.productinfo.vo.ProductInfoRespVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderPageReqVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderRemarkReqVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderRespVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderSaveReqVO;
import cn.hangtag.module.oms.convert.saleorder.SaleOrderConvert;
import cn.hangtag.module.oms.dal.dataobject.customer.CustomerDO;
import cn.hangtag.module.oms.dal.dataobject.productinfo.ProductInfoDO;
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
import cn.hangtag.module.oms.service.customer.CustomerService;
import cn.hangtag.module.oms.service.productinfo.ProductInfoService;
import cn.hangtag.module.oms.service.saleorder.SaleOrderService;
import cn.hangtag.module.system.api.user.AdminUserApi;
import cn.hangtag.module.system.api.user.dto.AdminUserRespDTO;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSONObject;
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.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static cn.hangtag.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.hangtag.framework.common.pojo.CommonResult.success;
@Tag(name = "APP - 销售订单")
@RestController
@RequestMapping("/oms/app/saleorder")
@RequestMapping("/oms/app/sale-order")
@Validated
public class AppSaleOrderController{
@Resource
private SaleOrderService saleOrderService;
@Resource
private CustomerService customerService;
@Resource
private AdminUserApi adminUserApi;
@PostMapping("/create")
@Operation(summary = "创建销售订单")
public CommonResult<Long> createSaleOrder(@Valid @RequestBody SaleOrderSaveReqVO createReqVO) {
return success(saleOrderService.createSaleOrder(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新销售订单")
public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody SaleOrderSaveReqVO updateReqVO) {
saleOrderService.updateSaleOrder(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除销售订单")
@Parameter(name = "id", description = "编号", required = true)
public CommonResult<Boolean> deleteSaleOrder(@RequestParam("id") Long id) {
saleOrderService.deleteSaleOrder(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得销售订单")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
public CommonResult<SaleOrderRespVO> getSaleOrder(@RequestParam("id") Long id) {
SaleOrderDO saleOrder = saleOrderService.getSaleOrder(id);
if(saleOrder == null){
return success(null);
}
// 拼接数据
List<cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO> entrys = saleOrderService.getSaleOrderEntryListByParentId(id);
CustomerDO customer = customerService.getCustomer(saleOrder.getCustomerId());
// 1.2 获取修改人
AdminUserRespDTO updater = adminUserApi.getUser(NumberUtils.parseLong(saleOrder.getUpdater()));
AdminUserRespDTO auditor = adminUserApi.getUser(NumberUtils.parseLong(saleOrder.getAuditor()));
return success(SaleOrderConvert.INSTANCE.convert(saleOrder,entrys,customer,updater,auditor));
//return success(BeanUtils.toBean(saleOrder, SaleOrderRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得销售订单分页")
public CommonResult<PageResult<SaleOrderRespVO>> getSaleOrderPage(@Valid SaleOrderPageReqVO pageReqVO) {
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
CustomerDO customer = customerService.getCustomerByUserId(loginUser.getId());
pageReqVO.setCustomerId(customer.getId());
PageResult<SaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, SaleOrderRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出销售订单 Excel")
@ApiAccessLog(operateType = EXPORT)
public void exportSaleOrderExcel(@Valid SaleOrderPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<SaleOrderDO> list = saleOrderService.getSaleOrderPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "销售订单.xls", "数据", SaleOrderRespVO.class,
BeanUtils.toBean(list, SaleOrderRespVO.class));
}
// ==================== 子表销售订单明细 ====================
@GetMapping("/sale-order-entry/list-by-parent-id")
@Operation(summary = "获得销售订单明细列表")
@Parameter(name = "parentId", description = "主表id")
public CommonResult<List<cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO>> getSaleOrderEntryListByParentId(@RequestParam("parentId") Long parentId) {
return success(saleOrderService.getSaleOrderEntryListByParentId(parentId));
}
@GetMapping("/get-count")
@Operation(summary = "获得销售订单 分页 tab count")
public CommonResult<Map<Integer, Long>> getSpuCount() {
return success(saleOrderService.getTabsCount());
}
@PostMapping("/updateSaleOrderBillStatus")
@Operation(summary = "更新销售订单的状态")
public CommonResult<Boolean> updateSaleOrderBillStatus(@RequestParam("ids") List<Long> ids,
@RequestParam("status") String status) {
saleOrderService.updateSaleOrderBillStatus(ids, status);
return success(true);
}
@PutMapping("/generateProduceOrder")
@Operation(summary = "生成生成制单")
public CommonResult<Boolean> generateProduceOrder(@RequestParam("ids") List<Long> ids) {
saleOrderService.generateProduceOrder(ids);
return success(true);
}
@PostMapping("/rejectOrder")
@Operation(summary = "驳回")
public CommonResult<Boolean> rejectOrder(@RequestBody JSONObject jobs) {
Long[] ids = jobs.getObject("ids", new Long[0].getClass());
String reason = jobs.getString("reason");
saleOrderService.updateSaleOrderBillStatus(Arrays.asList(ids), "reject", MapUtil.of("rejectReason",reason));
return success(true);
}
/**
* 生成PDF文档并下载
* @param response HTTP响应
* @throws Exception 异常
*/
@GetMapping("/download")
@PermitAll
public void downloadPdf(HttpServletResponse response) throws Exception{
saleOrderService.generatePdf(response);
}
@PutMapping("/update-remark")
@Operation(summary = "订单备注")
public CommonResult<Boolean> updateOrderRemark(@RequestBody SaleOrderRemarkReqVO reqVO) {
saleOrderService.updateOrderRemark(reqVO);
return success(true);
}
@PutMapping("/update-entrys")
@Operation(summary = "更新销售订单")
public CommonResult<Boolean> updateSaleOrderEntry(@Valid @RequestBody SaleOrderSaveReqVO updateReqVO) {
saleOrderService.updateSaleOrder(updateReqVO);
return success(true);
}
}

View File

@ -103,6 +103,14 @@ public class AuthController {
return success(null);
}
if(user.getDeptId()==999999L){//当前账号等于客户类型
AuthPermissionInfoRespVO convert = AuthConvert.INSTANCE.convert(user, null, null);
String customerMenuJson = ResourceUtil.readUtf8Str("json/customer_menu.json");
List<AuthPermissionInfoRespVO.MenuVO> list = JSONUtil.toList(customerMenuJson, AuthPermissionInfoRespVO.MenuVO.class);
convert.setMenus(list);
return success(convert);
}
// 1.2 获得角色列表
Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
if (CollUtil.isEmpty(roleIds)) {
@ -117,11 +125,6 @@ public class AuthController {
menuList.removeIf(menu -> !CommonStatusEnum.ENABLE.getStatus().equals(menu.getStatus())); // 移除禁用的菜单
AuthPermissionInfoRespVO convert = AuthConvert.INSTANCE.convert(user, roles, menuList);
if(user.getDeptId()==999999L){//当前账号等于客户类型
String customerMenuJson = ResourceUtil.readUtf8Str("json/customer_menu.json");
List<AuthPermissionInfoRespVO.MenuVO> list = JSONUtil.toList(customerMenuJson, AuthPermissionInfoRespVO.MenuVO.class);
convert.setMenus(list);
}
// 2. 拼接结果返回
return success(convert);
}

View File

@ -11,6 +11,19 @@
"keepAlive": true,
"alwaysShow": true,
"children": [
{
"id": 2830,
"parentId": 2828,
"name": "销售订单",
"path": "sale-order",
"component": "oms/saleorder/index",
"componentName": "SaleOrder",
"icon": "",
"visible": true,
"keepAlive": true,
"alwaysShow": true,
"children": null
},
{
"id": 2818,
"parentId": 2804,

View File

@ -4,7 +4,7 @@ import javax.annotation.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2024-09-22T21:13:45+0800",
date = "2024-09-22T22:23:16+0800",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_401 (Oracle Corporation)"
)
public class OAuth2OpenConvertImpl implements OAuth2OpenConvert {

View File

@ -6,7 +6,7 @@ import javax.annotation.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2024-09-22T21:13:46+0800",
date = "2024-09-22T22:23:16+0800",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_401 (Oracle Corporation)"
)
public class SocialUserConvertImpl implements SocialUserConvert {

View File

@ -0,0 +1,108 @@
import request from '@/config/axios'
// 销售订单 VO
export interface SaleOrderVO {
id: number // 客户id
customerId: number // 客户id
bizdate: Date // 业务日期
remark: string // 备注
confirmdate: Date // 确认日期
plansenddate: Date // 计划日期
phone: string // 手机
remarks: string // 备注
emails: string // 邮件列表数据格式xxx@xx.com;xxx@xx.com;
invoiceCode: string // 发票抬头
invoiceName: string // 发票名称
address: string // 地址
currency: string // 货币
invoiceRemarks: string // 发票备注
}
// 销售订单 API
export const SaleOrderApi = {
// 查询销售订单分页
getSaleOrderPage: async (params: any) => {
return await request.get({ url: `/oms/app/sale-order/page`, params })
},
// 查询销售订单详情
getSaleOrder: async (id: number) => {
return await request.get({ url: `/oms/app/sale-order/get?id=` + id })
},
// 新增销售订单
createSaleOrder: async (data: SaleOrderVO) => {
return await request.post({ url: `/oms/sale-order/create`, data })
},
// 修改销售订单
updateSaleOrder: async (data: SaleOrderVO) => {
return await request.put({ url: `/oms/app/sale-order/update`, data })
},
// 删除销售订单
deleteSaleOrder: async (id: number) => {
return await request.delete({ url: `/oms/app/sale-order/delete?id=` + id })
},
// 导出销售订单 Excel
exportSaleOrder: async (params) => {
return await request.download({ url: `/oms/app/sale-order/export-excel`, params })
},
// ==================== 子表(销售订单明细) ====================
// 获得销售订单明细列表
getSaleOrderEntryListByParentId: async (parentId) => {
return await request.get({ url: `/oms/app/sale-order/sale-order-entry/list-by-parent-id?parentId=` + parentId })
},
// 获得 销售 列表 tabsCount
getTabsCount: async () => {
return request.get({ url: '/oms/app/sale-order/get-count' })
},
// 更新订单状态
updateSaleOrderBillStatus: async (ids: number[],status: string) => {
return await request.post({
url: `/oms/sale-order/updateSaleOrderBillStatus`,
params: {
ids: ids.join(','),
status: status
}
})
},
// 更新订单状态
generateProduceOrder: async (ids: number[]) => {
return await request.put({
url: `/oms/sale-order/generateProduceOrder`,
params: {
ids: ids.join(',')
}
})
},
// 驳回
rejectOrder: async (data) => {
return await request.post({
url: `/oms/sale-order/rejectOrder`,
data: data
})
},
// 修改订单地址
updateOrderAddress: async (data: any) => {
return await request.put({ url: `/oms/sale-order/update-address`, data })
},
// 订单备注
updateOrderRemark: async (data: any) => {
return await request.put({ url: `/oms/sale-order/update-remark`, data })
},
// 更新分录数据
updateOrderEntrys: async (data: SaleOrderVO) => {
return await request.put({ url: `/oms/sale-order/update-entrys`, data })
}
}

View File

@ -0,0 +1,392 @@
<template>
<ContentWrap>
<el-row :gutter="10">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="150px"
v-loading="formLoading"
>
<div>
<el-card header="基本资料" style="width: 100%; margin-top: 20px" shadow="never" class="box-card">
<template #header>
<CardTitle title="基本资料" />
</template>
<el-row>
<el-col :span="12">
<el-form-item label="订单号码" prop="orderNo">
<el-input v-model="formData.orderNo" disabled="true" placeholder="*** New ***" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="订单状态" prop="orderstatus">
<el-input v-model="formData.orderstatus" disabled="true" placeholder="INITIAL" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="用户名称" prop="username">
<el-input v-model="formData.username" disabled="true" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="创建日期 (GMT+8)" prop="bizdate">
<el-date-picker
prefix-icon=""
style="width: 760px"
v-model="formData.bizdate"
type="date"
value-format="YYYY-MM-DD"
disabled="true"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="客户号" prop="id">
<el-input v-model="formData.id" disabled="true" placeholder="" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="企业公司" prop="company">
<el-input v-model="formData.company" disabled="true" placeholder="" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="品牌" prop="brandId">
<el-select v-model="formData.brandId" filterable placeholder="请选择品牌">
<el-option
v-for="brand in brandList"
:key="brand.id"
:label="brand.name"
:value="brand.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="要求交货日期" prop="plansenddate">
<el-date-picker
style="width: 760px"
v-model="formData.plansenddate"
type="date"
:size="size"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="联系人" prop="contacts">
<el-input v-model="formData.contacts" placeholder="" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机" prop="phone">
<el-input v-model="formData.phone" placeholder="" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="电话" prop="tel">
<el-input v-model="formData.tel" placeholder="" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="传真" prop="fax">
<el-input v-model="formData.fax" placeholder="" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="合同号" prop="contract">
<el-input v-model="formData.contractNo" placeholder="" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="零售商单号" prop="retailerno">
<el-input v-model="formData.retailerNo" placeholder="" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item label="备注" prop="remarks">
<el-input v-model="formData.remarks"
:autosize="{ minRows: 4, maxRows: 6 }"
type="textarea"
placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-card>
<el-card header="电子邮件通知" style="width: 100%; margin-top: 20px" shadow="never">
<template #header>
<CardTitle title="电子邮件通知" />
</template>
<el-row>
<el-col :span="12">
<el-form-item label="电子邮件" prop="newEmail">
<el-input v-model="formData.newEmail" placeholder="" />
</el-form-item>
</el-col>
<el-col :span="12" >
<div style="margin-left: 30px">
<el-button size="Default" @click="onAddEmail">增加</el-button>
<el-button size="Default" @click="onDelEmail">删除</el-button>
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="" prop="">
<div style="border: 1px solid #dcdfe6;width:100%;height: 200px;">
<el-scrollbar height="200px">
<ul style="padding-left: 4px;margin-top: 4px;">
<li style="list-style-type: none" v-for="(item, index) in formData.newEmails" :key="index" @click="selectItem(index)" :class="{'selected': selectedIndex === index}">
{{ item }}
</li>
</ul>
</el-scrollbar>
</div>
</el-form-item>
</el-col>
</el-row>
</el-card>
<!-- 右上角账户信息 -->
<el-col :span="12" class="detail-info-item" style="padding-left: 0px; margin-top: 20px">
<el-card shadow="never" class="h-full">
<template #header>
<CardTitle title="发票信息" />
</template>
<el-row>
<el-col :span="12">
<el-form-item label="发票抬头" prop="invoiceCode">
<el-input v-model="formData.invoiceCode" placeholder="请输入发票抬头" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="发票名称" prop="invoiceName">
<el-input v-model="formData.invoiceName" placeholder="请输入发票名称" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="发票地址" prop="invoiceAddress">
<el-input v-model="formData.invoiceAddress" placeholder="请输入发票地址" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="发票备注" prop="invoiceRemarks">
<el-input v-model="formData.invoiceRemarks"
:autosize="{ minRows: 4, maxRows: 6 }"
type="textarea"
placeholder="请输入发票备注" />
</el-form-item>
</el-col>
</el-row>
</el-card>
</el-col>
<!-- 右上角账户信息 -->
<!-- <el-col :span="12" class="detail-info-item" style="padding-right: 0px; margin-top: 20px">
<el-card shadow="never" class="h-full">
<template #header>
<CardTitle title="送货信息" />
</template>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="150px"
v-loading="formLoading"
>
<el-row>
<el-col :span="12">
<el-form-item label="订单号码" prop="name">
<el-input v-model="formData.name" disabled="true" placeholder="*** New ***" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="订单状态" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item label="备注" prop="remarks">
<el-input v-model="formData.remarks" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</el-col>-->
</div>
</el-form>
</el-row>
<!-- <el-affix position="bottom" :offset="20">
<el-row style="margin-top: 20px;border-top: #1e83e9" justify="end" >
<el-col :span="2">
<el-button type="primary" plain size="mini" @click="openForm(undefined)">下一步</el-button>
</el-col>
</el-row>
</el-affix>
-->
</ContentWrap>
<!-- 固定在底部的行面板 -->
<el-affix position="bottom" :offset="20">
<div style="background-color: #909399; padding: 20px; text-align: right;">
<!-- 使用 el-row el-col 来控制布局虽然在这个简单场景下可能不是必需的 -->
<el-row>
<el-col :span="24">
<!-- 结算按钮 -->
<!-- <el-button type="primary">结算</el-button>-->
<!-- 下一步按钮 -->
<el-button type="success" style="margin-left: 10px;">下一步</el-button>
</el-col>
</el-row>
</div>
</el-affix>
</template>
<script setup lang="ts">
import {defaultProps} from "@/utils/tree";
import * as BrandApi from "@/api/oms/brand";
//
const calculateDateAfterDays = (days) => {
const now = new Date();
now.setDate(now.getDate() + days);
// 'YYYY-MM-DD'
return now.toISOString().split('T')[0];
}
const formRef = ref() // Ref
const selectedIndex = ref(-1)
const emails = ref([])
const formData = ref({
name: undefined,
email: undefined,
contacts: undefined,
phone: undefined,
invoiceAddress: undefined,
areaId: undefined,
status: undefined,
type: undefined,
remarks: undefined,
orderNo: undefined,
invoiceName: undefined,
newEmail: '',
newEmails: [],
bizdate: new Date().toISOString(),
plansenddate: calculateDateAfterDays(7)
})
const formRules = reactive({
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
contacts: [{ required: true, message: '联系人不能为空', trigger: 'blur' }],
brandId: [{ required: true, message: '品牌不能为空', trigger: 'blur' }],
phone: [{ required: true, message: '联系人手机号不能为空', trigger: 'blur' },
{
pattern: /^(?:(?:\+|00)86)?1(?:3[\d]|4[5-79]|5[0-35-9]|6[5-7]|7[0-8]|8[\d]|9[189])\d{8}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
],
email: [
{
type: 'email',
message: '请输入正确的邮箱地址',
trigger: ['blur', 'change']
}
]
})
const onAddEmail = () => {
debugger;
if (formData.value.newEmail.trim() !== '') {
formData.value.newEmails.push(formData.value.newEmail);
formData.value.newEmail = ''; //
}
}
const selectItem = async (index:number) => {
selectedIndex.value = index;
}
const onDelEmail = () => {
if(selectedIndex.value>=0){
debugger;
formData.value.newEmails.splice(selectedIndex.value, 1); //
//
if(selectedIndex.value === selectedIndex.value) {
selectedIndex.value = -1;
}
}
selectedIndex.value = -1;
}
/** 查询列表 */
const getCustomer = async () => {
loading.value = true
try {
const data = await CustomerApi.getCustomerPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 初始化 **/
const brandList = ref<any[]>([]) //
onMounted(async () => {
//
brandList.value = await BrandApi.getSimpleBrandList()
})
</script>
<style scoped lang="scss">
/* 为选中的行添加背景色 */
.selected {
background-color: #dddee0; /* 浅绿色,可以根据需要调整 */
}
</style>

View File

@ -0,0 +1,184 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="客户id" prop="customerId">
<el-input v-model="formData.customerId" placeholder="请输入客户id" />
</el-form-item>
<el-form-item label="业务日期" prop="bizdate">
<el-date-picker
v-model="formData.bizdate"
type="date"
value-format="x"
placeholder="选择业务日期"
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="确认日期" prop="confirmdate">
<el-date-picker
v-model="formData.confirmdate"
type="date"
value-format="x"
placeholder="选择确认日期"
/>
</el-form-item>
<el-form-item label="计划日期" prop="plansenddate">
<el-date-picker
v-model="formData.plansenddate"
type="date"
value-format="x"
placeholder="选择计划日期"
/>
</el-form-item>
<el-form-item label="手机" prop="phone">
<el-input v-model="formData.phone" placeholder="请输入手机" />
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="formData.remarks" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="邮件列表数据格式xxx@xx.com;xxx@xx.com;" prop="emails">
<el-input v-model="formData.emails" placeholder="请输入邮件列表数据格式xxx@xx.com;xxx@xx.com;" />
</el-form-item>
<el-form-item label="发票抬头" prop="invoiceCode">
<el-input v-model="formData.invoiceCode" placeholder="请输入发票抬头" />
</el-form-item>
<el-form-item label="发票名称" prop="invoiceName">
<el-input v-model="formData.invoiceName" placeholder="请输入发票名称" />
</el-form-item>
<el-form-item label="地址" prop="address">
<el-input v-model="formData.address" placeholder="请输入地址" />
</el-form-item>
<el-form-item label="货币" prop="currency">
<el-input v-model="formData.currency" placeholder="请输入货币" />
</el-form-item>
<el-form-item label="发票备注" prop="invoiceRemarks">
<el-input v-model="formData.invoiceRemarks" placeholder="请输入发票备注" />
</el-form-item>
</el-form>
<!-- 子表的表单 -->
<el-tabs v-model="subTabsName">
<el-tab-pane label="销售订单明细" name="saleOrderEntry">
<SaleOrderEntryForm ref="saleOrderEntryFormRef" :parent-id="formData.id" />
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { SaleOrderApi, SaleOrderVO } from '@/api/oms/saleorder'
import SaleOrderEntryForm from './components/SaleOrderEntryForm.vue'
/** 销售订单 表单 */
defineOptions({ name: 'SaleOrderForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
customerId: undefined,
bizdate: undefined,
remark: undefined,
confirmdate: undefined,
plansenddate: undefined,
phone: undefined,
remarks: undefined,
emails: undefined,
invoiceCode: undefined,
invoiceName: undefined,
address: undefined,
currency: undefined,
invoiceRemarks: undefined,
})
const formRules = reactive({
})
const formRef = ref() // Ref
/** 子表的表单 */
const subTabsName = ref('saleOrderEntry')
const saleOrderEntryFormRef = ref()
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await SaleOrderApi.getSaleOrder(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
try {
await saleOrderEntryFormRef.value.validate()
} catch (e) {
subTabsName.value = 'saleOrderEntry'
return
}
//
formLoading.value = true
try {
const data = formData.value as unknown as SaleOrderVO
//
data.saleOrderEntrys = saleOrderEntryFormRef.value.getData()
if (formType.value === 'create') {
await SaleOrderApi.createSaleOrder(data)
message.success(t('common.createSuccess'))
} else {
await SaleOrderApi.updateSaleOrder(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
customerId: undefined,
bizdate: undefined,
remark: undefined,
confirmdate: undefined,
plansenddate: undefined,
phone: undefined,
remarks: undefined,
emails: undefined,
invoiceCode: undefined,
invoiceName: undefined,
address: undefined,
currency: undefined,
invoiceRemarks: undefined,
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,121 @@
<template>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
<el-table-column label="物料id" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.materialId`" :rules="formRules.materialId" class="mb-0px!">
<el-input v-model="row.materialId" placeholder="请输入物料id" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="物料名称" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.materialName`" :rules="formRules.materialName" class="mb-0px!">
<el-input v-model="row.materialName" placeholder="请输入物料名称" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="物料规格" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.materialSpec`" :rules="formRules.materialSpec" class="mb-0px!">
<el-input v-model="row.materialSpec" placeholder="请输入物料规格" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="单价" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.price`" :rules="formRules.price" class="mb-0px!">
<el-input v-model="row.price" placeholder="请输入单价" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="数量" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.qty`" :rules="formRules.qty" class="mb-0px!">
<el-input v-model="row.qty" placeholder="请输入数量" />
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="60">
<template #default="{ $index }">
<el-button @click="handleDelete($index)" link></el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3">
<el-button @click="handleAdd" round>+ 添加销售订单明细</el-button>
</el-row>
</template>
<script setup lang="ts">
import { SaleOrderApi } from '@/api/oms/saleorder'
const props = defineProps<{
parentId: undefined // id
}>()
const formLoading = ref(false) //
const formData = ref([])
const formRules = reactive({
})
const formRef = ref() // Ref
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.parentId,
async (val) => {
// 1.
formData.value = []
// 2. val
if (!val) {
return;
}
try {
formLoading.value = true
formData.value = await SaleOrderApi.getSaleOrderEntryListByParentId(val)
} finally {
formLoading.value = false
}
},
{ immediate: true }
)
/** 新增按钮操作 */
const handleAdd = () => {
const row = {
id: undefined,
parentId: undefined,
materialId: undefined,
materialName: undefined,
materialSpec: undefined,
price: undefined,
qty: undefined,
}
row.parentId = props.parentId
formData.value.push(row)
}
/** 删除按钮操作 */
const handleDelete = (index) => {
formData.value.splice(index, 1)
}
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
}
/** 表单值 */
const getData = () => {
return formData.value
}
defineExpose({ validate, getData })
</script>

View File

@ -0,0 +1,123 @@
<template>
<!-- 列表 -->
<ContentWrap>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['oms:sale-order:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="ID" align="center" prop="id" />
<el-table-column label="物料id" align="center" prop="materialId" />
<el-table-column label="物料名称" align="center" prop="materialName" />
<el-table-column label="物料规格" align="center" prop="materialSpec" />
<el-table-column label="单价" align="center" prop="price" />
<el-table-column label="数量" align="center" prop="qty" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['oms:sale-order:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['oms:sale-order:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<SaleOrderEntryForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { SaleOrderApi } from '@/api/oms/saleorder'
import SaleOrderEntryForm from './SaleOrderEntryForm.vue'
const { t } = useI18n() //
const message = useMessage() //
const props = defineProps<{
parentId?: number // id
}>()
const loading = ref(false) //
const list = ref([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
parentId: undefined as unknown
})
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.parentId,
(val: number) => {
if (!val) {
return
}
queryParams.parentId = val
handleQuery()
},
{ immediate: true, deep: true }
)
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await SaleOrderApi.getSaleOrderEntryPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
if (!props.parentId) {
message.error('请选择一个OMS销售订单主表')
return
}
formRef.value.open(type, id, props.parentId)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await SaleOrderApi.deleteSaleOrderEntry(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
</script>

View File

@ -0,0 +1,357 @@
<template>
<ContentWrap>
<el-button
v-if="formData.billStatus != 'C'"
type="primary"
@click="handleSave"
>保存</el-button>
<el-button
v-if="formData.billStatus == 'B'"
type="primary"
@click="handleAudit"
>审核</el-button>
<el-button
v-if="formData.billStatus == 'A' || formData.billStatus == 'B' "
type="primary"
@click="remark">备注</el-button>
<!-- 订单信息 -->
<el-descriptions title="订单信息">
<el-descriptions-item label="订单号: ">{{ formData.billno }}</el-descriptions-item>
<el-descriptions-item label="业务日期: ">{{ formatDate(formData.bizdate,'YYYY-MM-DD') }}</el-descriptions-item>
<el-descriptions-item label="确认日期: ">{{ formatDate(formData.confirmdate,'YYYY-MM-DD') }}</el-descriptions-item>
<el-descriptions-item label="计划日期: ">{{ formatDate(formData.plansenddate,'YYYY-MM-DD') }}</el-descriptions-item>
<el-descriptions-item label="手机: ">{{ formData.phone }}</el-descriptions-item>
<el-descriptions-item label="传真: ">{{ formData.fax }}</el-descriptions-item>
<el-descriptions-item label="邮件: ">{{ formData.emails }}</el-descriptions-item>
<el-descriptions-item label="客户编号: ">{{ formData?.customer?.number }}</el-descriptions-item>
<el-descriptions-item label="客户名称: ">{{ formData?.customer?.name }}</el-descriptions-item>
<el-descriptions-item label="客户公司: ">{{ formData?.customer?.company }}</el-descriptions-item>
<el-descriptions-item label="订单备注: ">{{ formData.remark }}</el-descriptions-item>
</el-descriptions>
<!-- 订单状态 -->
<el-descriptions :column="3" title="订单状态">
<el-descriptions-item label="单据状态: ">
<dict-tag :type="DICT_TYPE.OMS_BILL_STATUS" :value="formData.billStatus" />
</el-descriptions-item>
<el-descriptions-item label="订单状态: ">
<dict-tag :type="DICT_TYPE.OMS_ORDER_STATUS" :value="formData.orderStatus" />
</el-descriptions-item>
<el-descriptions-item v-if="formData.billStatus == 'AA' " label="驳回原因: ">{{ formData?.rejectReason }}</el-descriptions-item>
<el-descriptions-item v-hasPermi="['oms:sale-order:update']" label-class-name="no-colon">
<!-- 待发货 -->
<template >
<!-- 快递发货 -->
<el-button
type="primary"
@click="delivery"
>
发货
</el-button>
<el-button
type="primary"
@click="updateAddress"
>
修改地址
</el-button>
<!-- 到店自提 -->
<el-button
type="primary"
@click="handleAudit"
>
核销
</el-button>
</template>
</el-descriptions-item>
<!-- <el-descriptions-item>
<template #label><span style="color: red">提醒: </span></template>
买家付款成功后货款将直接进入您的商户号微信支付宝<br />
请及时关注你发出的包裹状态确保可以配送至买家手中 <br />
如果买家表示没收到货或货物有问题请及时联系买家处理友好协商
</el-descriptions-item>-->
</el-descriptions>
<!-- 商品信息 -->
<el-descriptions title="产品信息">
<el-descriptions-item labelClassName="no-colon">
<el-row :gutter="20">
<el-col :span="544">
<el-form
ref="itemFormEntrysRef"
:model="formData.entrys"
:rules="itemFormEntrysRules"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData.entrys"
border
:on-cell-click="cellClick"
>
<el-table-column label="产品编码" prop="materialName" width="150"/>
<el-table-column label="产品名称" prop="materialName" width="150"/>
<el-table-column label="产品规格" prop="materialSpec" width="150" />
<el-table-column label="数量" prop="qty" width="150" />
<el-table-column label="单价" width="250">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.price`" :rules="itemFormEntrysRules.price" class="mb-0px!"
v-if="row.billStatus!='C'"
>
<el-input-number
style="width: 100%"
v-model.number="row.price"
:step="0.001"
:controls="false"
@change="changeRow(row,'price')"
placeholder="请输入单价"
/>
</el-form-item>
<span v-else @click="editRow($index)">{{ row.price }}</span>
</template>
</el-table-column>
<el-table-column label="折扣(%)" prop="discount" width="250" >
<template #default="{ row, $index }">
<el-input-number
style="width: 100%"
v-if="row.billStatus!='C'"
v-model.number="row.discount"
:step="0.001"
:controls="false"
min="0"
max="100"
@change="changeRow(row,'discount')"
placeholder="请输入折扣(%)"
/>
<span v-else @click="editRow($index)">{{ row.discount }}</span>
</template>
</el-table-column>
<el-table-column label="金额" prop="amount" width="250" />
<!-- <el-table-column label="合计" prop="payPrice" width="150">
<template #default="{ row }">{{ fenToYuan(row.payPrice) }}</template>
</el-table-column>-->
</el-table>
</el-form>
</el-col>
<el-col :span="10" />
</el-row>
</el-descriptions-item>
</el-descriptions>
<!-- 订单信息 -->
<el-descriptions title="发票信息">
<el-descriptions-item label="发票抬头: ">{{ formData.invoiceCode }}</el-descriptions-item>
<el-descriptions-item label="发票名称: ">{{ formData.invoiceName }}</el-descriptions-item>
<el-descriptions-item label="货币: ">{{ formData.currency }}</el-descriptions-item>
<el-descriptions-item label="发票备注: ">{{ formData.invoiceRemarks }}</el-descriptions-item>
</el-descriptions>
<!-- 订单信息 -->
<el-descriptions title="制单信息">
<el-descriptions-item label="创建时间: ">{{ formatDate(formData.createTime) }}</el-descriptions-item>
<el-descriptions-item label="修改人: ">{{ formData.updaterName }}</el-descriptions-item>
<el-descriptions-item label="修改时间: ">{{ formatDate(formData.updateTime) }}</el-descriptions-item>
<el-descriptions-item label="审核人: ">{{ formData.auditorName }}</el-descriptions-item>
<el-descriptions-item label="审核时间: ">{{ formatDate(formData.auditorTime) }}</el-descriptions-item>
</el-descriptions>
</ContentWrap>
<!-- 各种操作的弹窗 -->
<OrderUpdateRemarkForm ref="updateRemarkForm" @success="getDetail" />
<OrderUpdateAddressForm ref="updateAddressFormRef" @success="getDetail" />
</template>
<script lang="ts" setup>
import { fenToYuan } from '@/utils'
import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime'
import OrderUpdateRemarkForm from '@/views/oms/saleorder/form/OrderUpdateRemarkForm.vue'
import OrderUpdateAddressForm from '@/views/oms/saleorder/form/OrderUpdateAddressForm.vue'
import { useTagsViewStore } from '@/store/modules/tagsView'
import { propTypes } from '@/utils/propTypes'
import {SaleOrderApi, SaleOrderVO} from "@/api/oms/saleorder";
defineOptions({ name: 'TradeOrderDetail' })
const message = useMessage() //
const itemFormEntrysRef = ref() // Ref
const itemFormEntrysRules = reactive({
price: [{ required: true, message: '单价不能为空', trigger: 'blur' }],
})
//
const formData = ref<SaleOrderVO>({
logs: []
})
/** 各种操作 */
const updateRemarkForm = ref() // Ref
const remark = () => {
updateRemarkForm.value?.open(formData.value)
}
const updateAddressFormRef = ref() // Ref
const updateAddress = () => {
updateAddressFormRef.value?.open(formData.value)
}
const changeRow = async (row,key:string) => {
if('price' == key || 'discount' == key ){
let price = row.price
let qty = row.qty
let discount = row.discount
row.amount = (price*qty) * (1-(discount/100))
//100*(1-(2/100))
}
}
const handleSave = async () => {
try {
//
await itemFormEntrysRef.value.validate()
//
await SaleOrderApi.updateOrderEntrys(formData.value)
message.success('保存成功')
//
await getDetail()
} catch {}
}
/** 审核 */
const handleAudit = async () => {
try {
//
await itemFormEntrysRef.value.validate()
//
await message.confirm('确认审核订单吗?')
//
await SaleOrderApi.updateSaleOrderBillStatus([formData.value.id],'audit')
message.success('审核成功')
//
await getDetail()
} catch {}
}
/** 获得详情 */
const { params } = useRoute() //
const props = defineProps({
id: propTypes.number.def(undefined), // ID
showPickUp: propTypes.bool.def(true) //
})
const id = (params.id || props.id) as unknown as number
const getDetail = async () => {
if (id) {
const res = (await SaleOrderApi.getSaleOrder(id)) as SaleOrderVO
//
if (!res) {
message.error('销售订单不存在')
close()
}
formData.value = res
}
}
/** 关闭 tag */
const { delView } = useTagsViewStore() //
const { push, currentRoute } = useRouter() //
const close = () => {
delView(unref(currentRoute))
push({ name: 'SaleOrder' })
}
/** 初始化 **/
onMounted(async () => {
await getDetail()
})
</script>
<style lang="scss" scoped>
:deep(.el-descriptions) {
&:not(:nth-child(1)) {
margin-top: 20px;
}
.el-descriptions__title {
display: flex;
align-items: center;
&::before {
display: inline-block;
width: 3px;
height: 20px;
margin-right: 10px;
background-color: #409eff;
content: '';
}
}
.el-descriptions-item__container {
margin: 0 10px;
.no-colon {
margin: 0;
&::after {
content: '';
}
}
}
}
// 线
:deep(.el-timeline) {
margin: 10px 0 0 160px;
.el-timeline-item__wrapper {
position: relative;
top: -20px;
.el-timeline-item__timestamp {
position: absolute !important;
top: 10px;
left: -150px;
}
}
.el-timeline-right-content {
display: flex;
align-items: center;
min-height: 30px;
padding: 10px;
background-color: #f7f8fa;
&::before {
position: absolute;
top: 10px;
left: 13px; /* 将伪元素水平居中 */
border-color: transparent #f7f8fa transparent transparent; /* 尖角颜色,左侧朝向 */
border-style: solid;
border-width: 8px; /* 调整尖角大小 */
content: ''; /* 必须设置 content 属性 */
}
}
.dot-node-style {
position: absolute;
left: -5px;
display: flex;
width: 20px;
height: 20px;
font-size: 10px;
color: #fff;
border-radius: 50%;
justify-content: center;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,98 @@
<template>
<Dialog v-model="dialogVisible" title="修改订单收货地址" width="35%">
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="120px">
<el-form-item label="收件人">
<el-input v-model="formData.receiverName" placeholder="请输入收件人名称" />
</el-form-item>
<el-form-item label="手机号">
<el-input v-model="formData.receiverMobile" placeholder="请输入收件人手机号" />
</el-form-item>
<el-form-item label="所在地">
<el-tree-select
v-model="formData.receiverAreaId"
:data="areaList"
:props="defaultProps"
:render-after-expand="true"
/>
</el-form-item>
<el-form-item label="详细地址">
<el-input
v-model="formData.receiverDetailAddress"
:rows="3"
placeholder="请输入收件人详细地址"
type="textarea"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { getAreaTree } from '@/api/system/area'
import { copyValueToTarget } from '@/utils'
import { defaultProps } from '@/utils/tree'
import {SaleOrderApi,SaleOrderVO} from "@/api/oms/saleorder";
defineOptions({ name: 'OrderUpdateAddressForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const formLoading = ref(false) // 12
const formData = ref({
id: undefined, //
receiverName: '', //
receiverMobile: '', //
receiverAreaId: null, //
receiverDetailAddress: '' //
})
const areaList = ref([]) //
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (row: SaleOrderVO) => {
resetForm()
//
copyValueToTarget(formData.value, row)
dialogVisible.value = true
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
formLoading.value = true
try {
const data = unref(formData)
await SaleOrderApi.updateOrderAddress(data)
message.success(t('common.updateSuccess'))
dialogVisible.value = false
//
emit('success', true)
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined, //
receiverName: '', //
receiverMobile: '', //
receiverAreaId: null, //
receiverDetailAddress: '' //
}
formRef.value?.resetFields()
}
onMounted(async () => {
//
areaList.value = await getAreaTree()
})
</script>

View File

@ -0,0 +1,70 @@
<template>
<Dialog v-model="dialogVisible" title="订单备注" width="45%">
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
<el-form-item label="备注">
<el-input
v-model="formData.remark"
:rows="3"
placeholder="请输入订单备注"
type="textarea"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import {SaleOrderApi,SaleOrderVO} from "@/api/oms/saleorder";
defineOptions({ name: 'OrderUpdateRemarkForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const formLoading = ref(false) // 12
const formData = ref({
id: undefined, //
remark: '' //
})
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (row: SaleOrderVO) => {
resetForm()
//
formData.value.id = row.id
formData.value.remark = row.remark
dialogVisible.value = true
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
formLoading.value = true
try {
const data = unref(formData)
await SaleOrderApi.updateOrderRemark(data)
message.success(t('common.updateSuccess'))
dialogVisible.value = false
//
emit('success', true)
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined, //
remark: '' //
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,589 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb1px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="单据编号" prop="billno">
<el-input
v-model="queryParams.billno"
placeholder="请输入单据编号"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="客户" prop="customerId">
<el-input
v-model="queryParams.customerId"
placeholder="请输入客户"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="业务日期" prop="bizdate">
<el-date-picker
v-model="queryParams.bizdate"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="确认日期" prop="confirmdate">
<el-date-picker
v-model="queryParams.confirmdate"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="计划日期" prop="plansenddate">
<el-date-picker
v-model="queryParams.plansenddate"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="单据状态" prop="billStatus">
<el-select
v-model="queryParams.billStatus"
placeholder="请选择单据状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.OMS_BILL_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="订单状态" prop="orderStatus">
<el-select
v-model="queryParams.orderStatus"
placeholder="请选择订单状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.OMS_ORDER_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="手机" prop="phone">
<el-input
v-model="queryParams.phone"
placeholder="请输入手机"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input
v-model="queryParams.remarks"
placeholder="请输入备注"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @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-form-item>
</el-form>
<el-row :gutter="10" class="-mb-1px">
<el-col :span="1.5">
<!-- <el-button
type="danger"
plain
@click="handleReject()"
:disabled="selectionList.length === 0"
>驳回
</el-button>
<el-button
type="primary"
plain
@click="handleUpdateBillStatus('invalid')"
:disabled="selectionList.length === 0"
>
作废
</el-button>
<el-button
type="warning"
plain
@click="handleUpdateBillStatus('submit')"
:disabled="selectionList.length === 0"
>提交
</el-button>
<el-button
type="primary"
plain
@click="handleUpdateBillStatus('audit')"
:disabled="selectionList.length === 0"
>
审核
</el-button>
<el-button
type="primary"
plain
@click="generateProduceOrder()"
:disabled="selectionList.length === 0 || queryParams.tabType === 0"
>
生成制单
</el-button>-->
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-col>
</el-row>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-tabs v-model="queryParams.tabType" @tab-click="handleTabClick">
<el-tab-pane
v-for="item in tabsData"
:key="item.type"
:label="item.name + '(' + item.count + ')'"
:name="item.type"
/>
</el-tabs>
<el-table v-loading="loading" :data="list" :stripe="true"
:show-overflow-tooltip="true"
@selection-change="handleSelectionChange"
:row-class-name="tableRowClassName"
>
<el-table-column width="30" label="选择" type="selection" />
<el-table-column label="单据编号" align="center" prop="billno" width="120px"/>
<el-table-column label="客户" align="center" prop="customerId" width="120px"/>
<el-table-column label="销售员" align="center" prop="customerId" width="180px"/>
<el-table-column label="跟单员" align="center" prop="customerId" width="180px"/>
<el-table-column label="单据状态" align="center" prop="billStatus" width="180px">
<template #default="scope">
<dict-tag :type="DICT_TYPE.OMS_BILL_STATUS" :value="scope.row.billStatus" />
</template>
</el-table-column>
<el-table-column label="订单状态" align="center" prop="orderStatus" width="180px">
<template #default="scope">
<dict-tag :type="DICT_TYPE.OMS_ORDER_STATUS" :value="scope.row.orderStatus" />
</template>
</el-table-column>
<el-table-column
label="业务日期"
align="center"
prop="bizdate"
:formatter="dateFormatter2"
width="180px"
/>
<el-table-column
label="确认日期"
align="center"
prop="confirmdate"
:formatter="dateFormatter2"
width="180px"
/>
<el-table-column
label="计划日期"
align="center"
prop="plansenddate"
:formatter="dateFormatter2"
width="180px"
/>
<el-table-column label="手机" align="center" prop="phone" />
<el-table-column label="备注" align="center" prop="remarks" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column
label="更新时间"
align="center"
prop="updateTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" width="150" fixed="right">
<template #default="scope">
<el-button
v-hasPermi="['oms:sale-order:query']"
link
type="primary"
@click="openDetail(scope.row.id)"
>
<Icon icon="ep:notification" />
详情
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['oms:sale-order:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<SaleOrderForm ref="formRef" @success="getList" />
<!-- 驳回对话框 -->
<el-dialog :title="rejectTitle" v-model="rejectOpen" width="750px" style="height: 210px;" append-to-body>
<el-form ref="form" :model="rejectform" label-width="80px" >
<el-form-item label="驳回原因" prop="rejectReason" >
<el-input v-model="rejectform.rejectReason" :rows="3" type="textarea" placeholder="请输入驳回原因" />
</el-form-item>
</el-form>
<div class="dialog-footer" style="text-align: right">
<el-button type="primary" @click="submitRejectForm"> </el-button>
<el-button @click="rejectCancel"> </el-button>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import {dateFormatter, dateFormatter2} from '@/utils/formatTime'
import download from '@/utils/download'
import { SaleOrderApi, SaleOrderVO } from '@/api/oms/saleorder'
import SaleOrderForm from './SaleOrderForm.vue'
import {checkPermi} from "@/utils/permission";
/** 销售订单 列表 */
defineOptions({ name: 'SaleOrder' })
const { currentRoute, push } = useRouter() //
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<SaleOrderVO[]>([]) //
const total = ref(0) //
// tabs
const tabsData = ref([
{
name: '已下单',
type: 0,
count: 0
},
{
name: '生产中',
type: 1,
count: 0
},
{
name: '已完成',
type: 2,
count: 0
}
])
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
billno: undefined,
customerId: undefined,
tabType: 0,
bizdate: [],
confirmdate: [],
plansenddate: [],
phone: undefined,
remarks: undefined,
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
//
const rejectOpen = ref(false)
const rejectTitle = ref('')
const rejectform = reactive({
rejectReason: undefined
})
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await SaleOrderApi.getSaleOrderPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 获得每个 Tab 的数量 */
const getTabsCount = async () => {
const res = await SaleOrderApi.getTabsCount()
for (let objName in res) {
tabsData.value[Number(objName)].count = res[objName]
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
debugger;
//
await message.delConfirm()
//
await SaleOrderApi.deleteSaleOrder(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 驳回操作 */
const handleReject = async () => {
const ids = selectionList.value.map((item) => item.id)
const billStatus = selectionList.value.map((item) => item.billStatus)
debugger
for(let vals of billStatus) {
if(vals!='A' && vals!='B'){
message.error("请选择单据状态保存或已提交的数据行");
return;
}
}
rejectform.rejectReason = undefined
rejectTitle.value = "是否驳回选中的数据项?"
rejectOpen.value = true
}
/** 审批操作 */
const handleUpdateBillStatus = async (operateKey: string) => {
try {
const ids = selectionList.value.map((item) => item.id)
const billStatus = selectionList.value.map((item) => item.billStatus)
for(let vals of billStatus) {
if("audit" == operateKey && vals!='B'){
message.error("请选择单据状态已提交的数据行");
return;
}
if("invalid" == operateKey && (vals=='C')){
debugger
message.error("单据已审核不允许作废");
return;
}
}
debugger
//
let operateName = ''
if(operateKey === 'submit'){
operateName = '提交'
}else if(operateKey === "audit"){
operateName = '审核'
}else if(operateKey === "invalid"){
operateName = '作废'
}else {
return
}
await message.confirm(`确定${operateName}该订单吗?`)
//
await SaleOrderApi.updateSaleOrderBillStatus(ids, operateKey)
message.success(`${operateName}成功`)
//
await getList()
selectionList.value = selectionList.value.filter((item) => !ids.includes(item.id))
} catch {}
}
/** 审批/反审批操作 */
const generateProduceOrder = async () => {
try {
const ids = selectionList.value.map((item) => item.id)
const billStatus = selectionList.value.map((item) => item.billStatus)
for(let vals of billStatus) {
if(vals!='C'){
message.error("请选择单据状态已审核的数据行");
return;
}
}
//
await message.confirm(`是否生成生产制单?`)
//
await SaleOrderApi.generateProduceOrder(ids)
message.success(`生成成功!`)
//
await getList()
selectionList.value = selectionList.value.filter((item) => !ids.includes(item.id))
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await SaleOrderApi.exportSaleOrder(queryParams)
download.excel(data, '销售订单.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 切换 Tab */
const handleTabClick = (tab: TabsPaneContext) => {
queryParams.tabType = tab.paneName as number
getList()
}
/** 选中操作 */
const selectionList = ref<SaleOrderVO[]>([])
const handleSelectionChange = (rows: SaleOrderVO[]) => {
selectionList.value = rows
}
/** 驳回提交按钮 */
const submitRejectForm = async () => {
const ids = selectionList.value.map((item) => item.id)
const billStatus = selectionList.value.map((item) => item.billStatus)
if(rejectform.rejectReason != null) {
const data = {
ids:ids,
reason:rejectform.rejectReason
}
await SaleOrderApi.rejectOrder(data).then(response => {
message.success("驳回成功");
rejectOpen.value = false;
getList();
});
}else {
message.error(`请填写驳回原因!`)
}
}
/** 驳回取消按钮 */
const rejectCancel = () =>{
rejectOpen.value = false
rejectform.rejectReason = undefined
}
/** 查看订单详情 */
const openDetail = (id: number) => {
push({ name: 'SaleOrderDetail', params: { id } })
}
const tableRowClassName = ({
row,
rowIndex,
}: {
row: SaleOrderVO
rowIndex: number
}) => {
if (row.billStatus === 'AA') {
return 'warning-row'
}
return ''
}
// /
watch(
() => currentRoute.value,
() => {
getList()
}
)
/** 激活时 */
onActivated(() => {
getList()
})
/** 初始化 **/
onMounted(() => {
getList()
//
getTabsCount()
})
</script>
<style>
.el-table .warning-row {
--el-table-tr-bg-color: #D4D4D4;
}
.el-table .success-row {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
</style>

View File

@ -0,0 +1,160 @@
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
import { dateFormatter } from '@/utils/formatTime'
// 表单校验
export const rules = reactive({
})
// CrudSchema https://doc.iocoder.cn/vue3/crud-schema/
const crudSchemas = reactive<CrudSchema[]>([
{
label: '单据编号',
field: 'billno',
isSearch: true,
isForm: false,
},
{
label: '客户id',
field: 'customerId',
isSearch: true,
form: {
component: 'InputNumber',
value: 0
},
},
{
label: '业务日期',
field: 'bizdate',
formatter: dateFormatter,
isSearch: true,
search: {
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
type: 'daterange',
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
}
},
form: {
component: 'DatePicker',
componentProps: {
type: 'datetime',
valueFormat: 'x'
}
},
},
{
label: '备注',
field: 'remark',
form: {
component: 'Input',
componentProps: {
type: 'textarea',
rows: 4
},
colProps: {
span: 24
}
},
},
{
label: '更新时间',
field: 'udate',
formatter: dateFormatter,
isForm: false,
},
{
label: '创建时间',
field: 'cdate',
formatter: dateFormatter,
isForm: false,
},
{
label: '确认日期',
field: 'confirmdate',
formatter: dateFormatter,
isSearch: true,
search: {
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
type: 'daterange',
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
}
},
form: {
component: 'DatePicker',
componentProps: {
type: 'datetime',
valueFormat: 'x'
}
},
},
{
label: '计划日期',
field: 'plansenddate',
formatter: dateFormatter,
isSearch: true,
search: {
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
type: 'daterange',
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
}
},
form: {
component: 'DatePicker',
componentProps: {
type: 'datetime',
valueFormat: 'x'
}
},
},
{
label: '手机',
field: 'phone',
isSearch: true,
},
{
label: '备注',
field: 'remarks',
isSearch: true,
},
{
label: '邮件列表数据格式xxx@xx.com;xxx@xx.com;',
field: 'emails',
isTable: false,
},
{
label: '发票抬头',
field: 'invoiceCode',
isTable: false,
},
{
label: '发票名称',
field: 'invoiceName',
isTable: false,
},
{
label: '地址',
field: 'address',
isTable: false,
},
{
label: '货币',
field: 'currency',
isTable: false,
},
{
label: '发票备注',
field: 'invoiceRemarks',
isTable: false,
},
{
label: '操作',
field: 'action',
isForm: false
}
])
export const { allSchemas } = useCrudSchemas(crudSchemas)