目录
一. 需求描述+需求分析
1.1 需求描述
如下图所示,是一个销售管理后台的修改页面,我类似地将各个字段的含义做了简单的标注,Producer 大家可以理解为销售人员(中介),假设一件商品是由"甲、乙、丙......"等非单独一个人推销卖出的产品,那么这些人中就有一个一定付出的努力最大,所以得到的报酬也应该最多。
因此现在要新增一个主附Producer单选框,标记出来谁是主Producer,方便后台计算员工薪酬奖金。
要求一:如果只有一个人员,则默认为主Producer!
要求二:付出成果占比低的人同样可以成为主Producer,"Share Commission"与"Share Production" 均可以为0,但是在提交的时候需要弹出中断流程的提示框进行提示,经确认后才可提交审核!
要求三:同一个团队下,各个Producer人员的"Share Commission"与"Share Production"值相加应等于100!
要求四:默认情况下,只有同一个团队下人员之间可以更换主Producer,非同一个团队不能随意更换主Producer!
要求五:选择对应的主Producer时,对应的销售人员团队编码和员工编码不能为空!
要求六:拆分的Producer不能相同,例如不能第一行是员工A,第二行拆分时还是添加的员工A,员工不可重复!
1.2 需求分析
(1)因为不同的团队可能出现相同的员工姓名编码例如"A团队001的张三","B团队001的张三",员工编号可能重复不一定唯一,所以我们不能将员工编号直接作为单选框的值,但是如果使用"团队码值+员工码值",就可以确定一个唯一的销售人员了,不能只我们将字段名称就定义为"mainProducer";
(2)":label"标签采用的是中文文国际版,所以字段名称单独定义在其它文件作为Vue组件导入到此,因此我们在定义的时候也遵循这种设计理念,将添加的"主Producer"也在对应的文件中进行添加;
(3)因为是单选框,通过点击来进行选择主Producer,所以我们可以给单选框绑定一个 "@change" 函数,每当我的单选发生改变时,就去执行 change 函数,这样一来,我们就可以将需求要求的校验写在 change 函数中;
二. 代码实现
2.1 原代码分析
如下代码,是原本页面上已经存在的代码,可以看出一个表格,所以我们直接在原有的表格中添加即可。
表格中采用了 ElementUI 表格组件,
:data="splitForm.splitData"
:绑定表格数据源,来自splitForm
对象的splitData
数组 ,数据来源通常为后端返回;slot-scope="scope":
是 Vue.js 和 ElementUI 中用于表格列模板的一种特殊语法,它提供了对当前行数据的访问能力,scope 对象内容:
scope.row
:当前行的数据对象scope.$index
:当前行的索引(从0开始)scope.column
:当前列的信息scope.store
:表格的存储状态
<el-table :data="splitForm.splitData" :header-cell-style="{'text-align':'center'}" :cell-style="{'text-align':'center'}" style="width: 100%;">
<!-- 渠道类型 -->
<el-table-column :label="$t('feeSplit.feeSplit.qudaoleixing')" width="180px">
<template slot-scope="scope">
<el-form-item label-width="-120px">
<el-form-item :prop="'splitData.' + scope.$index + '.businessNature'" :rules="pageRules.businessNature">
<code-select v-model="scope.row.businessNature" code-type="ChannelTypeLv1" :disabled="!lookShowBtu || scope.row.dataFlag === '1' || scope.row.firstFlag" clearable @change="clearChild(scope.row, 'businessNature')" />
</el-form-item>
</el-form-item>
</template>
</el-table-column>
<!-- producer -->
<el-table-column :label="$t('feeSplit.feeSplit.Procucer')" width="140px">
<template slot-scope="scope">
<el-form-item label-width="-120px">
<el-form-item :prop="'splitData.' + scope.$index + '.producerCode'" :rules="pageRules.producerCode">
<code-search
v-model="scope.row.producerCode"
show-code
:show-name="false"
code-type="ProducerInPolicy"
:other-conditions="{ ChannelTypeLv1:scope.row.businessNature, ChannelTypeLv2:scope.row.businessNatureSub, comCode: scope.row.policyComCode, riskCode:scope.row.riskCode,dsProducer:scope.row.dsProducer }"
:disabled="!lookShowBtu || scope.row.dataFlag === '1' || scope.row.firstFlag"
@change="refFillProducer(scope.$index, ...arguments), clearChild(scope.row, 'producerCode')"
/>
</el-form-item>
</el-form-item>
</template>
</el-table-column>
<!-- producer name -->
<el-table-column :label="$t('feeSplit.feeSplit.ProcucerName')" width="180px">
<template slot-scope="scope">
<el-input v-model="scope.row.producerName" :title="scope.row.producerName" disabled />
</template>
</el-table-column>
<!-- 渠道细分 -->
<el-table-column :label="$t('feeSplit.feeSplit.qudaoxifen')" width="180px">
<template slot-scope="scope">
<el-form-item label-width="-120px">
<el-form-item :prop="'splitData.' + scope.$index + '.businessNatureSub'" :rules="pageRules.businessNatureSub">
<code-select v-model="scope.row.businessNatureSub" code-type="ChannelTypeLv2" :parent-code="scope.row.businessNature" :disabled="!lookShowBtu || scope.row.dataFlag === '1' || scope.row.firstFlag" clearable @change="clearChild(scope.row, 'businessNatureSub')" />
</el-form-item>
</el-form-item>
</template>
</el-table-column>
<!-- 经办人代码 -->
<el-table-column :label="$t('feeSplit.feeSplit.yewuyuandaima')" width="120px">
<template slot-scope="scope">
<el-form-item label-width="-120px">
<el-form-item :prop="'splitData.' + scope.$index + '.producerSubCode'" :rules="pageRules.producerSubCode">
<code-search
v-model="scope.row.producerSubCode"
show-code
:show-name="false"
code-type="HandlerInPolicyModel"
:include-codes="scope.row.handleCodeList"
:other-conditions="{ ChannelTypeLv1:scope.row.businessNature, ChannelTypeLv2:scope.row.businessNatureSub, Producer:scope.row.producerCode }"
:disabled="!lookShowBtu || scope.row.dataFlag === '1' || scope.row.firstFlag || scope.row.subFlag"
@change="refFillHandler(scope.$index, ...arguments), clearChild(scope.row, 'producerSubCode')"
/>
</el-form-item>
</el-form-item>
</template>
</el-table-column>
<!-- 经办人名称 -->
<el-table-column :label="$t('feeSplit.feeSplit.yewuyuanmingcheng')" width="180px">
<template slot-scope="scope">
<el-input v-model="scope.row.producerSubName" disabled />
</template>
</el-table-column>
<!-- 销售团队 -->
<el-table-column :label="$t('feeSplit.feeSplit.xiaoshoutuandui')" width="150px">
<template slot-scope="scope">
<code-search v-model="scope.row.teamCode" code-type="TeamCodeHis" disabled />
</template>
</el-table-column>
<!-- 业绩归属机构 -->
<el-table-column :label="$t('feeSplit.feeSplit.yejiguishujigou')" width="120px">
<template slot-scope="scope">
<code-trans v-model="scope.row.comCode" code-type="Company" />
<!-- <code-search v-model="scope.row.comCode" code-type="Company" disabled />-->
</template>
</el-table-column>
<!-- share commission -->
<el-table-column :label="$t('feeSplit.feeSplit.ShareCommission')" width="150px">
<template slot-scope="scope">
<el-input-number
v-model="scope.row.shareCommission"
:disabled="!lookShowBtu"
:precision="2"
:controls="false"
style="width: 100px"
@change="numberChange(scope.row.shareCommission,scope.$index,0)"
/>
</template>
</el-table-column>
<!-- share production -->
<el-table-column :label="$t('feeSplit.feeSplit.ShareProduction')" width="150px">
<template slot-scope="scope">
<el-input-number
v-model="scope.row.shareProduction"
:disabled="!lookShowBtu"
:precision="2"
:controls="false"
style="width: 100px"
@change="numberChange(scope.row.shareProduction,scope.$index,1)"
/>
</template>
</el-table-column>
</el-table>
2.2 新增代码编写
:lable 标签导入的文件如下所示,展示部分,我们新添加的单选框名称,也在这个文件中添加即可
export default {
mainProducerFlag: '主Producer', // 新增主Producer标识,作为表格列名
qudaoleixing: '渠道类型',
Procucer: 'Producer',
ProcucerName: 'Producer Name',
ShareCommission: 'Share Commission',
ShareProduction: 'Share Production'
......
}
新添加单选框主体代码,我们就直接加在表格<table>代码块最上面即可,然后我们在 Vue 的 data 数据中添加 v-mode 绑定的 "selectMainProducerCode" 属性
<!-- 主producer标识,单选框 -->
<el-table-column :label="$t('feeSplit.feeSplit.mainProducerFlag')" width="130px" align="center">
<template slot-scope="scope">
<el-radio v-model="selectMainProducerCode"
:label="getMainProducerKey(scope.row.producerCode, scope.row.producerSubCode)"
@change="updateMainAgent(scope.row.producerCode, scope.row.producerSubCode,scope.row.businessNature)">
</el-radio>
</template>
</el-table-column>
data() {
return {
selectMainProducerCode: null, // 费用拆分单选主Producer对应的十位码值,(producerCode + handlerCode)
prevSelectMainProducerCode: null, // 上一次单选选择的主Producer对应的十位码值,(producerCode + handlerCode)
selectMainBusinessNature: null, // 主Producer的渠道类型
prevSelectMainBusinessNature: null, // 上一次单选选择的主Producer对应的渠道类型
}
},
methods: {
// 在 methods 中添加单选标签值计算函数,动态的根据每一行的团队编码+员工编码得出一个唯一标识,
// 用于与 data 中的 "selectMainProducerCode"匹配,相同则被选中,不通则不被选中
getMainProducerKey(producerCode,producerSubCode) {
return producerCode + producerSubCode
},
// 编写绑定的 change 函数,函数传递三个参数,
// 参数一:选中行的团队编码,用于计算十位唯一值
// 参数二:选中行的员工编码,用于计算十位唯一值
// 参数三:选中行的渠道类型,用于判断新选的Producer是否与原Producer渠道相同,实现需求的要求四
// 结合上面的"getMainProducerKey"方法得出的十位唯一值,就可以判断当前行对应的员工是否被选中
updateMainAgent(producerCode,handlerCode,businessNature){
// 1. 判空.若为空则提示请选择 Producer,
// 此步判断实现需求的要求五"对应的销售人员团队编码和员工编码不能为空!"
if (!producerCode || producerCode === '' || !handlerCode || handlerCode === '') {
this.$message({
type: 'warning',
message: this.$t('请先选择Producer和经办人代码')
})
// 2. 弹出提示框后,回滚到上一个选择的 Producer, 然后 return 返回
this.$nextTick(() => {
this.selectMainProducerCode = this.prevSelectMainProducerCode
});
return;
}
// 3. 判断新选择的主Producer渠道类型需与原主Producer渠道类型是否一致
// 此步判断实现需求的要求四"非同一个团队不能随意更换主Producer!"
if (this.prevSelectMainBusinessNature !== null && this.prevSelectMainBusinessNature !== businessNature){
this.$message({
type: 'warning',
message: this.$t('新选择的主Producer渠道类型需与原主Producer渠道类型一致')
})
// 4. 弹出提示框后,回滚到上一个选择的 Producer 和 渠道类型值
this.$nextTick(() => {
this.selectMainBusinessNature = this.prevSelectMainBusinessNature
this.selectMainProducerCode = this.prevSelectMainProducerCode
});
return;
}
// 5. 若选择的 Producer 非空,将此次选择的 Producer 保存用于后续回滚操作使用, 上一次选中的Producer的Id值
this.prevSelectMainProducerCode = producerCode + handlerCode
this.selectMainProducerCode = producerCode + handlerCode
},
// 弹出费用拆分弹窗
feeSplit() {
const splitForm = {
splitData: [{
businessNature: this.selection[0].businessNature,
businessNatureSub: this.selection[0].businessNatureSub,
producerCode: this.selection[0].producerCode,
producerName: this.selection[0].producerName,
producerSubCode: this.selection[0].handlerCode,
producerSubName: this.selection[0].handlerName,
teamCode: this.selection[0].teamCode,
teamName: this.selection[0].teamName,
comCode: this.selection[0].branchCode, // producer的归属机构 即保单的业绩归属机构对应
shareCommission: 100,
shareProduction: 100,
permitNo: '',
permitName: '',
mobile: '',
interAddress: '',
// comCode: this.selection[0].comCode,
riskCode: this.selection[0].riskCode, // 产品
firstFlag: true, // 第一行数据
subFlag: false, // 经办人是否可修改
handelCodeList: []
}]
}
// 下面四行代码用于初始化值,实现需求的要求一“如果只有一个人员,则默认为主Producer!”。
// 未进行拆分,说明只有一个人,默认选择第一条记录作为主Producer
this.selectMainProducerCode = this.selection[0].producerCode + this.selection[0].handlerCode
// 未进行拆分,说明只有一个人,默认选择第一条记录作为主Producer ,记录值用于回滚操作
this.prevSelectMainProducerCode = this.selection[0].producerCode + this.selection[0].handlerCode
// 记录当前的渠道类型值,用于判断新选择的Producer渠道类型是否与当前渠道类型值相同
this.selectMainBusinessNature = this.selection[0].businessNature
// 记录当前的渠道类型值,用于回滚操作
this.prevSelectMainBusinessNature = this.selection[0].businessNature
},
// 保函数
submitButton(passCommpaymentCheck) {
this.$refs['splitForm'].validate(async valid => {
// 这里我只编写了新增的逻辑,其它原有的表单逻辑已省略......
// Share Commission总和必须为100%,供后续作为标识做判断
let comNum = 0
// Share Production总和必须为100%,供后续作为标识做判断
let proNum = 0
// 拆分数据producer相同标识
let proFlag = false
// 主Producer的shareCommission,shareProduction均为0标识
let mainProducerShareCommissionAndShareProductionIsZeroFlag = false
// 循环遍历修改后的集合数据,计算shareCommission,shareProduction各自的总和
this.splitForm.splitData.forEach(element => {
if (element.producerCode + element.producerSubCode === this.selectMainProducerCode){
if (element.shareCommission === 0 && element.shareProduction === 0){
mainProducerShareCommissionAndShareProductionIsZeroFlag = true
}
element.agentFlag = '1'
}else {
element.agentFlag = '0'
}
comNum = math.add(comNum, (element.shareCommission === undefined || element.shareCommission === '') ? 0 : element.shareCommission)
proNum = math.add(proNum, (element.shareProduction === undefined || element.shareProduction === '') ? 0 : element.shareProduction)
if ((element.shareCommission === undefined || element.shareCommission === '') && (element.shareProduction === undefined || element.shareProduction === '')) {
numFlag = true
return
}
// 比较所有拆分对象是否有重复
numsIn = 0
this.splitForm.splitData.forEach(elementIn => {
if (nums !== numsIn && elementIn.producerCode === element.producerCode && elementIn.producerSubCode === element.producerSubCode) {
proFlag = true
return
}
numsIn++
})
nums++
})
// 若主Producer的shareCommission,shareProduction均为0,则弹出提示框
// 这一步判断实现了需求的要求二,
if (mainProducerShareCommissionAndShareProductionIsZeroFlag){
try {
await this.$confirm(
this.$t('请注意!主Producer的share Commission和share Production都为0,请确认是否提交'),
this.$t('请确认'),
{
confirmButtonText: this.$t('common.ok'),
cancelButtonText: this.$t('common.close'),
type: 'warning'
}
);
} catch (error) {
// 用户点击了"取消"或关闭弹窗
return;
}
}
// 这一步判断实现了需求的要求六"员工不可重复拆分"
// 判断拆分数据的producer是否相同,相同弹出提示框
if (proFlag) {
this.$message({
type: 'warning',
message: this.$t('拆分数据的producer不能相同!')
})
return
}
// 这一步判断实现了需求的要求三"Share Commission值相加应等于100!"
// shareCommission总和必须为100%,不为100则弹出提示框
if ((comNum !== 100)) {
this.$message({
type: 'warning',
message: this.$t('Share Commission总和必须为100%!')
})
return
}
// 这一步判断实现了需求的要求三"Share Production"值相加应等于100!
// shareProduction总和必须为100%,不为100则弹出提示框
if ((proNum !== 100)) {
this.$message({
type: 'warning',
message: this.$t('Share Production总和必须为100%!')
})
return
}
})
},
}
三. 效果展示
3.1 需求要求一验证
选中一条数据,进入到拆分页面,可以发现,如果只有一条数据,那么会默认选中成为主Producer,需求要求一验证成功
3.2 需求要求二验证
如下图所示,我新增一条数据并设置为主Producer,将他的"Share Commission"和"Share Production"全设置为0,点击提交,如下弹窗,要求我们进一步确认方可提交,需求要求二验证成功;
3.3 需求要求三验证
如下,我新增两条数据,对其"Share Commission"和"Share Production"进行设置,但相加不为100,点击提交,触发了 submit 提交校验,弹出提示框,需求要求三验证成功
3.4 需求要求四验证
我尝试更换团队编码,点击选择另一个团队的销售人员作为主Producer,如下图所示,在点击的时候,因为触发了 change 函数校验,团队编码(渠道类型)必须相同,否则无法选择,需求要求四验证成功。
3.5 需求要求五验证
如下图所示,我新增一条数据,但是不选择 Producer,点击单选框,触发了 change 函数判空校验,弹出一条提示信息,提示要先选择Producer和经办人代码,需求要求五验证成功。
3.6 需求要求六验证
我新增一条相同的Producer,因为默认选择第一条作为主Producer,所以新增的第二条也被选中,但是没关系,我们尝试提交尝试提交,触发了 submit 提交函数校验,如下图所示。也弹出中断提示框,要求拆分的数据Producer不能相同,需求要求六验证成功。