增加批量绑定

This commit is contained in:
wuweihua
2026-10-30 11:07:51 +08:00
parent 6b10a41b9d
commit 8ed97fed4a
11 changed files with 355 additions and 80 deletions
@@ -7,22 +7,47 @@
class="ps-dialog"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="96px">
<el-form-item label="客户" prop="customerCode">
<el-form-item :label="isEdit ? '客户' : '客户'" :prop="isEdit ? 'customerCode' : 'customerCodes'">
<customer-select
v-if="isEdit"
v-model="form.customerCode"
value-key="customerCode"
placeholder="请选择客户"
@change="onCustomerSelectChange"
/>
<customer-select
v-else
v-model="form.customerCodes"
value-key="customerCode"
multiple
placeholder="请选择客户(可多选)"
@change="onBatchCustomerChange"
/>
</el-form-item>
<el-form-item label="客户信息">
<el-form-item v-if="isEdit" label="客户信息">
<div class="ps-readonly" :class="{ 'ps-readonly--empty': !form.customerCode }">
<span class="ps-readonly__code">{{ form.customerCode || '未选择' }}</span>
<span class="ps-readonly__name">{{ form.customerName || '选择客户后自动带出' }}</span>
</div>
</el-form-item>
<el-form-item v-else label="已选">
<div v-if="!selectedCustomers.length" class="ps-readonly ps-readonly--empty">
<span class="ps-readonly__name">请从上方下拉多选客户</span>
</div>
<div v-else class="batch-tags">
<el-tag
v-for="c in selectedCustomers"
:key="c.customerCode"
size="small"
closable
class="batch-tag"
@close="removeSelected(c.customerCode)"
>{{ c.customerName || c.customerCode }}</el-tag>
</div>
</el-form-item>
<el-form-item label="优先级" prop="bindPriority">
<el-input-number
v-model="form.bindPriority"
@@ -33,32 +58,11 @@
<div class="ps-hint">数字越小优先级越高,1 为最高;同一客户命中多个套餐时按此排序</div>
</el-form-item>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="生效日期" prop="prBegindate">
<el-date-picker
v-model="form.prBegindate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择日期"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="失效日期" prop="prEnddate">
<el-date-picker
v-model="form.prEnddate"
type="date"
value-format="YYYY-MM-DD"
placeholder="长期有效"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<div class="ps-hint ps-hint--block">{{ bindRangeHint }}</div>
<el-form-item v-if="!isEdit">
<el-checkbox v-model="form.overwriteExisting">
覆盖已绑定其他套餐的客户(不勾选则跳过)
</el-checkbox>
</el-form-item>
</el-form>
<template #footer>
@@ -70,25 +74,20 @@
<script setup name="SetCustomerDialog">
/**
* 套餐绑定客户 · 新增 / 编辑弹窗
* 由表格组件通过 ref.open(row) 唤起,保存成功后 emit('success') 通知页面刷新。
* 外观统一走 styles/dialog.scss 的 .ps-dialog(见该文件顶部说明)。
* 套餐绑定客户 · 新增(支持批量) / 编辑弹窗
*/
import { createEmptyCustomerRow } from '../../utils/priceSet'
import { addSetCustomer, updateSetCustomer } from '@/api/price/set'
import { batchAddSetCustomer, updateSetCustomer } from '@/api/price/set'
const props = defineProps({
/** 所属套餐ID */
priceSetId: {
type: [Number, String],
default: undefined
},
/** 新行默认生效日期(套餐生效日期) */
defaultBegindate: {
type: String,
default: undefined
},
/** 新行默认失效日期(套餐失效日期) */
defaultEnddate: {
type: String,
default: undefined
@@ -102,64 +101,112 @@ const { proxy } = getCurrentInstance()
const visible = ref(false)
const title = ref('')
const submitting = ref(false)
const isEdit = ref(false)
const formRef = ref()
const form = ref({})
const selectedCustomers = ref([])
const rules = {
customerCode: [{ required: true, message: '请选择客户', trigger: 'change' }],
prBegindate: [{ required: true, message: '生效日期不能为空', trigger: 'change' }]
}
/** 有效期一句话说明,替掉原来两行零散提示 */
const bindRangeHint = computed(() => {
const begin = form.value.prBegindate
const end = form.value.prEnddate
if (!begin) return '生效日期为必填,失效日期留空表示长期有效。'
return end
? `该客户在 ${begin} 至 ${end} 期间的询价将命中本套餐;失效日期留空表示长期有效。`
: `该客户自 ${begin} 起长期命中本套餐;如需限制期限,请填写失效日期。`
const rules = computed(() => {
if (isEdit.value) {
return {
customerCode: [{ required: true, message: '请选择客户', trigger: 'change' }]
}
}
return {
customerCodes: [{
type: 'array',
required: true,
min: 1,
message: '请至少选择一个客户',
trigger: 'change'
}]
}
})
/** 打开弹窗:row 为空表示新增 */
function open(row) {
form.value = row
? { ...row }
: {
...createEmptyCustomerRow({
effectiveDate: props.defaultBegindate,
expireDate: props.defaultEnddate
}),
priceSetId: props.priceSetId
}
title.value = row ? '编辑客户绑定' : '添加客户'
isEdit.value = !!row
selectedCustomers.value = []
if (row) {
form.value = { ...row }
title.value = '编辑客户绑定'
} else {
form.value = {
...createEmptyCustomerRow({
effectiveDate: props.defaultBegindate,
expireDate: props.defaultEnddate
}),
priceSetId: props.priceSetId,
customerCodes: [],
overwriteExisting: false
}
title.value = '添加客户'
}
visible.value = true
nextTick(() => formRef.value && formRef.value.clearValidate())
}
/** 下拉选中客户后回填代码 / 名称 / ID */
function onCustomerSelectChange(code, item) {
form.value.customerCode = code || undefined
form.value.customerName = item ? item.customerName : ''
form.value.customerId = item ? item.customerId : undefined
}
function onBatchCustomerChange(codes, items) {
form.value.customerCodes = Array.isArray(codes) ? codes : []
selectedCustomers.value = Array.isArray(items) ? items.slice() : []
}
function removeSelected(code) {
form.value.customerCodes = (form.value.customerCodes || []).filter(c => String(c) !== String(code))
selectedCustomers.value = selectedCustomers.value.filter(c => String(c.customerCode) !== String(code))
}
async function submit() {
const valid = await formRef.value.validate().catch(() => false)
if (!valid) return
const payload = { ...form.value, priceSetId: props.priceSetId }
submitting.value = true
const req = payload.customerPricesetId ? updateSetCustomer(payload) : addSetCustomer(payload)
req.then(() => {
proxy.$modal.msgSuccess('保存成功')
try {
if (isEdit.value) {
const payload = { ...form.value, priceSetId: props.priceSetId }
await updateSetCustomer(payload)
proxy.$modal.msgSuccess('保存成功')
} else {
const customers = selectedCustomers.value.map(c => ({
customerId: c.customerId,
customerCode: c.customerCode,
customerName: c.customerName
}))
const res = await batchAddSetCustomer({
priceSetId: props.priceSetId,
bindPriority: form.value.bindPriority,
prBegindate: form.value.prBegindate,
prEnddate: form.value.prEnddate,
overwriteExisting: !!form.value.overwriteExisting,
customers
})
proxy.$modal.msgSuccess(res.msg || '批量绑定成功')
}
visible.value = false
emit('success')
}).finally(() => {
} finally {
submitting.value = false
})
}
}
defineExpose({ open })
</script>
<!-- 非 scoped:弹窗被 append-to-body 传送到 body,样式需全局生效,命名空间见 styles/dialog.scss -->
<style lang="scss" src="../../styles/dialog.scss"></style>
<style scoped>
.batch-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
width: 100%;
max-height: 140px;
overflow-y: auto;
}
.batch-tag {
max-width: 100%;
}
</style>
@@ -5,6 +5,17 @@
:count="customers.length"
sub="一个客户只能绑定一个套餐"
>
<template #action>
<el-button
v-if="editable"
type="primary"
size="small"
icon="Plus"
@click="dialogRef.open()"
v-hasPermi="['price:set:edit']"
>添加客户</el-button>
</template>
<el-table :data="customers" border class="customer-table" empty-text="暂未绑定客户">
<el-table-column label="客户代码" prop="customerCode" min-width="140" show-overflow-tooltip>
<template #default="{ row }">
@@ -18,18 +29,57 @@
<span v-else class="priority">{{ row.bindPriority }}</span>
</template>
</el-table-column>
<el-table-column v-if="editable" label="操作" width="120" align="center" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="dialogRef.open(row)" v-hasPermi="['price:set:edit']">编辑</el-button>
<el-button link type="danger" @click="remove(row)" v-hasPermi="['price:set:edit']">解绑</el-button>
</template>
</el-table-column>
</el-table>
<set-customer-dialog
ref="dialogRef"
:price-set-id="set.priceSetId"
:default-begindate="set.effectiveDate"
:default-enddate="set.expireDate"
@success="emit('refresh')"
/>
</set-section-card>
</template>
<script setup name="SetCustomerTable">
/**
* 套餐详情 · 绑定客户表格
* 自带新增 / 编辑弹窗与解绑,数据变更后 emit('refresh') 由页面重新拉取详情。
*/
import SetSectionCard from './SetSectionCard.vue'
import SetCustomerDialog from './SetCustomerDialog.vue'
import { isChildEditable } from '../../utils/priceSet'
import { delSetCustomer } from '@/api/price/set'
const props = defineProps({
set: { type: Object, required: true }
/** 套餐详情对象 */
set: {
type: Object,
required: true
}
})
const emit = defineEmits(['refresh'])
const { proxy } = getCurrentInstance()
const dialogRef = ref()
const customers = computed(() => props.set.customers || [])
const editable = computed(() => isChildEditable(props.set.status))
function remove(row) {
proxy.$modal.confirm('确认解绑该客户?').then(() => delSetCustomer(row.customerPricesetId)).then(() => {
proxy.$modal.msgSuccess('已解绑')
emit('refresh')
}).catch(() => {})
}
</script>
<style scoped>
+1 -1
View File
@@ -18,7 +18,7 @@
<set-basic-info-card :set="set" />
<set-customer-table :set="set" />
<set-customer-table :set="set" @refresh="load" />
<set-version-table
v-model="currentPsvId"