最近的工作是在做后台管理系统中的采购订单,大体流程是客户先选择供应商、然后从供应商提供的商品中进行选择,选择后,可对价格、数量、金额等字段进行编辑,最终形成一个包含多个商品对象的数组,每个商品对象具有很多与该商品相关的属性,大体如下:
const productList = [
{
ItemId: 2000, // 商品ID
Name: 'xxx', // 品名
IsFresh: true, // 是否为生鲜商品
PurcPrice: 20, // 档案进价
Price: 21, // 价格
CostPrice: 20, // 加权平均价
Qty: 30, // 数量
Amount: 630, // 小计金额
LargeQty: 30 // 箱数
// 单位、自编码、 ......
}
// ......
]
客户编辑完后,最后点击保存按钮,调用接口生成一个采购订单。保存时,需要对客户选择的商品进行一些校验,现在有三个校验规则:
- 商品的小计金额(Amount)不能超过99999999,
- 判断采购设置中是否启用了:“采购价格高于加权平均价时,进行提示”,如果启用了,
当存在某一商品的采购价格(Price)高于加权平均价(CostPrice)时,弹框提示 - 采购设置中不启用:“生鲜商品允许价格高于商品档案进价”,那么当一个商品为生鲜商品,
且价格高于档案进价时,弹框提示
发现上面的三个校验是相互独立的,有些校验一旦不满足就不能保存(1、3),有些条件不满足也依然可以保存,让用户自己选择(2)。为了让这几个校验的过程能够相互独立,刚好可以使用职责链模式对代码进行管理。
参阅《javascript设计模式与开发实践》一书中对职责链模式的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
下面是具体的实现方式:
/**
* 职责链模式
*/
export class Chain {
constructor(fn) {
this.fn = fn
this.successor = null
}
setNextSuccessor(successor) {
return (this.successor = successor)
}
async passRequest() {
const res = await this.fn.apply(this, arguments)
if (res === 'nextSuccessor' && this.successor) {
return await this.successor.passRequest.apply(this.successor, arguments)
}
return res
}
}
/**
* 使用职责链模式进行保存前的校验
* @param {Object} data 需要校验的数据
*/
async function validate(data) {
// 校验:小计金额最大值限制
function validateAmountMax() {
const index = data.findIndex((item) => item.Amount >= 99999999)
if (index > -1) {
return new Promise((resolve) => {
const modal = Modal.confirm({
title: '提示',
content: '小计金额不能超过99999999',
icon: createVNode(ExclamationCircleOutlined),
okText: '确定',
cancelText: createVNode(),
async onOk() {
nextTick(() => modal.destroy())
resolve(false)
}
})
})
}
return 'nextSuccessor'
}
// 校验:采购价格是否高于加权平均价
function validateInPriceHigherThanAutoPrice() {
// 判断采购设置中是否启用了:“采购价格高于加权平均价时,进行提示”,如果启用了,
// 当存在某一商品的采购价格高于加权平均价时,弹框提示
if (purchaseSettings.value.InPriceHigherThanAutoPrice == 'Y') {
const needTip = data.some((item) => item.Price > item.CostPrice)
if (needTip) {
return new Promise((resolve) => {
const modal = Modal.confirm({
title: '确认',
content: '存在商品的当前价格高于加权平均价,是否继续保存?',
icon: createVNode(ExclamationCircleOutlined),
okText: '确定',
cancelText: '取消',
async onOk() {
nextTick(() => modal.destroy())
resolve('nextSuccessor')
},
async onCancel() {
nextTick(() => modal.destroy())
resolve(false)
}
})
})
}
}
return 'nextSuccessor'
}
// 校验:生鲜商品允许价格高于商品档案进价
function validateFreshAllowInPriceHigherThanItemPrice() {
// 采购设置中不启用:“生鲜商品允许价格高于商品档案进价”,那么当一个商品为生鲜商品,
// 且价格高于档案进价时,弹框提示
if (purchaseSettings.value.FreshAllowInPriceHigherThanItemPrice == 'N') {
const needTip = data.some((item) => item.IsFresh && item.Price > item.PurcPrice)
if (needTip) {
return new Promise((resolve) => {
const modal = Modal.confirm({
title: '提示',
content: '采购设置设置了不允许生鲜商品价格高于商品档案进价,请重新输入价格!',
icon: createVNode(ExclamationCircleOutlined),
okText: '确定',
cancelText: createVNode(),
async onOk() {
nextTick(() => modal.destroy())
resolve(false)
}
})
})
}
}
return true
}
// 将三个校验函数包装成职责链节点
const valAmount = new Chain(validateAmountMax)
const valCostPrice = new Chain(validateInPriceHigherThanAutoPrice)
const valFresh = new Chain(validateFreshAllowInPriceHigherThanItemPrice)
// 指定节点在职责链中的顺序
valAmount.setNextSuccessor(valCostPrice).setNextSuccessor(valFresh)
// 把请求传递给第一个节点
return await valAmount.passRequest()
}
// 保存
async function save(data) {
if (!(await validate(data))) return false
// ....... 调接口进行保存
}
相关文章
暂无评论...