- index.vue
<template><div><el-button type="primary" @click="showDialog">添加邮递配置</el-button><el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="800px" :before-close="handleCloseDialog"><el-form ref="form" :model="form" :rules="formRules" label-width="120px"><el-form-item label="邮递名称" prop="name"><el-input v-model="form.name" style="width: 200px;"></el-input></el-form-item><el-form-item label="邮递角色" prop="role"><el-select v-model="form.role" placeholder="请选择" class="custom-select"><el-option label="邮政员" value="postal"></el-option><el-option label="管理员" value="admin"></el-option></el-select></el-form-item><el-form-item label="邮政编号ID"><el-input v-model="form.postListId" placeholder="请输入邮政编号ID" style="width: 200px;"></el-input></el-form-item><el-form-item label="邮递周期" prop="cycleItem"><el-select v-model="form.cycleItem" placeholder="邮递周期" class="custom-select"@change="handleCycleChange"><el-option label="周期" value="cycle"></el-option></el-select><el-select v-model="form.period" placeholder="周期选项"><el-option v-for="periodsItem in periods" :key="periodsItem.value" :label="periodsItem.label":value="periodsItem.value"></el-option></el-select></el-form-item><el-form-item><template #label><span ref="customLabelExpression" /></template><PostCondition v-model="conditions" :item-disabled="itemDisabled" :show-item-btn="showItemBtn" /></el-form-item><el-form-item label="实时邮递次数"><div class="input-row"><el-col style="max-width:200px;"><div class="flexible-input-group"><el-input v-model="form.frequencyDay" class="flexible-input"@input="handleFrequencyInput"></el-input><span class="input-unit">天</span></div></el-col><el-col style="max-width:200px;"><div class="flexible-input-group"><el-input v-model="form.frequencyCount" class="flexible-input"@input="handleTimesInput"></el-input><span class="input-unit">次</span></div></el-col></div></el-form-item><el-form-item label="邮递方式" prop="selectedMethods"><el-select v-model="form.selectedMethods" multiple placeholder="请选择" class="custom-select"><el-option label="邮件" value="0"></el-option><el-option label="短信" value="1"></el-option></el-select></el-form-item><el-form-item label="状态" prop="status"><el-select v-model="form.status" placeholder="请选择" class="custom-select"><el-option label="上线" value="1"></el-option><el-option label="下线" value="0"></el-option></el-select></el-form-item><el-form-item label="推送内容" prop="content"><el-input type="textarea" v-model="form.content" :rows="5" maxlength="400" show-word-limitstyle="width: 600px;"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="handleSubmit">确定</el-button></span></el-dialog></div>
</template><script>
import '@/styles/textarea.css'
import PostCondition from './PostCondition.vue'export default {components: {PostCondition,},data() {return {dialogVisible: false,dialogTitle: '添加邮递配置',form: {name: '中国邮政', // 邮递名称role: 'postal', // 邮递角色period: '', // 邮递周期单位postListId: '', // 邮政编号IDcycleItem: '', // 邮递周期选项frequencyDay: '', // 频率:天frequencyCount: '', // 频率:次selectedMethods: [], // 邮递方式status: '1',content: '',richTextContent: '', // 富文本内容expression: ''// 条件表达式},selectConditions: [], // 邮递条件选项列表,根据周期动态设置bakSelectConditions: [], // 邮递条件选项列表备份,用于重置periods: [], // 邮递周期单位列表disableConditionSelect: false, // 控制邮递条件选择框是否禁用showPercentageSuffix: false, // 是否显示百分比输入框后缀formRules: { // 表单验证规则name: [{ required: true, message: '请输入邮递名称', trigger: 'blur' }],role: [{ required: true, message: '请选择邮递角色', trigger: 'change' }],cycleItem: [{ required: true, message: '请选择邮递周期', trigger: 'change' }],period: [{ required: true, message: '请选择邮递周期单位', trigger: 'change' }],method: [{ required: true, message: '请选择邮递方式', trigger: 'change' }],status: [{ required: true, message: '请选择状态', trigger: 'change' }],content: [{ required: true, message: '请输入推送内容', trigger: 'blur' }],selectedMethods: [{ required: true, message: '请选择通知方式', trigger: 'blur' },{validator: (rule, value, callback) => {if (value.length === 0) {callback(new Error('请选择邮递方式'));} else {callback();}},trigger: 'change'}],richTextContent: [{ required: true, message: '请输入内容', trigger: 'blur' }],showPercentageSuffix: false},periodsToValue: {'day': 1,'week': 2,'month': 3},conditions: [{ field: '', operator: '', value: '' }], // 邮递条件itemDisabled: false,showItemBtn: true};},methods: {showDialog() {this.dialogVisible = true;this.$nextTick(() => {this.updateCustomLabel();});},handleCloseDialog(done) {// 清空表单数据this.$refs.form.resetFields();// 关闭对话框done();},handleConditionsData() {const result = this.conditions.map(condition => {let cleanedValue = condition.value.replace(/,/g, '')if (!isNaN(cleanedValue) && cleanedValue.includes('.')) {cleanedValue = (parseFloat(cleanedValue) / 100).toFixed(4)}return `${condition.field}|${condition.operator}|${cleanedValue}`}).join('&')return result},handleSubmit() {// 手动触发一次表单验证this.$refs.form.validate((valid) => {if (valid) {// 表单验证通过,执行提交操作const allValid = this.conditions.every(item => item.field && item.operator && item.value)if (!allValid) {this.$message.error('邮递条件选项还有未填完,请仔细检查一下!')return}// 根据选中的通知方式处理内容并构造数据格式this.form.expression = this.handleConditionsData()this.printVal()alert('提交成功!');} else {alert('表单验证失败!');}});},updateCustomLabel() {const label = '* 邮递条件';const firstChar = label.charAt(0);const restOfLabel = label.slice(1);// 创建一个 span 元素const spanElement = document.createElement('span');// 设置第一个字符的颜色为 #F56C6CspanElement.innerHTML = `<span style="color: #F56C6C">${firstChar}</span>${restOfLabel}`;// 替换 el-form-item 的 label 内容this.$refs.customLabelExpression.innerHTML = '';this.$refs.customLabelExpression.appendChild(spanElement);},handleCycleChange(value) {this.disableConditionSelect = false;// 根据周期设置条件选项列表if (value === 'cycle') {this.periods = [{ label: '天', value: 'day' },{ label: '周', value: 'week' },{ label: '月', value: 'month' },];}},checkFloatNumber(value) {let number = value.replace(/[^\d.]/g, '') // 清除非数字和小数点字符.replace(/\.{2,}/g, '.') // 只保留一个小数点.replace(/^(-)*(\d+)\.(\d{0,2}).*$/, '$1$2.$3'); // 限制小数点后两位return number;},checkIntNumber(value) {let number = value.replace(/[^\d.]/g, '') // 清除非数字和小数点字符.replace(/\.{1,}/g, '') // 保留0个小数点return number;},formatNumber(value) {console.log("去除输入中的非数字字符")// 去除输入中的非数字字符if (!value) return '';let number = value.toString().replace(/\D/g, '');// 格式化为千位分隔符的形式let formatted = new Intl.NumberFormat('en-US').format(number);return formatted;},handleSelectedMethods(listData) {// 将数组转换成逗号分隔的字符串const result = listData.join(',');return result;},handleTouchCount(valueKey) {let value = this.form[valueKey].trim(); // 获取输入框的值if (value === '') {return; // 检查是否为空}let intValue = parseInt(value, 10); // 尝试转换为整数if (isNaN(intValue) || !Number.isInteger(intValue) || intValue < 1) {// 如果不是合法的整数,则给出警告并设置为最小匹配值this.$message.warning({message: '已自动将其自动设置为最小匹配值',duration: 1000});this.form[valueKey] = 1; // 设置为最小匹配值} else {// 更新数据this.form[valueKey] = intValue.toString();}},handleFrequencyInput() {this.handleTouchCount('frequencyDay');},handleTimesInput() {this.handleTouchCount('frequencyCount');},printVal() {console.log("邮递名称:", this.form.name); // 邮递名称console.log("邮递角色:", this.form.role); // 邮递角色console.log("邮政编号ID:", this.form.postListId); // 邮政编号IDconsole.log("邮递周期:", this.periodsToValue[this.form.period]); // 邮递周期console.log("邮递条件表达式:", this.form.expression);// 邮递条件表达式console.log("邮递频率:", this.form.frequencyDay); // 邮递频率console.log("邮递次数:", this.form.frequencyCount); // 邮递次数console.log("邮递方式:", this.handleSelectedMethods(this.form.selectedMethods));// 邮递方式console.log("状态:", this.form.status);// 状态console.log("推送内容:", this.form.content);// 推送内容},},
};
</script><style scoped>
.dialog-footer {text-align: center;
}.input-row {display: flex;align-items: center;
}.flexible-input-group {/* display: flex; *//* align-items: center; */margin-right: 20px;/* 调整输入框组之间的间距 */
}.flexible-input-group .flexible-input {width: 150px !important;margin-right: 10px;
}/* .input-row /deep/ .flexible-input {width: 100px;
} */.input-unit {margin-left: 5px;/* 调整单位文本和输入框之间的间距 */
}.custom-select {width: 200px;margin-right: 10px;
}
</style>
PostCondition.vue
<template><div><!-- 使用渲染函数自定义 label 的内容 --><div v-for="(condition, index) in conditions" :key="generateKey(index)" class="condition-item"><el-row :class="{ 'error-row': hasError(index) }" class="condition-row" style="margin-top: 2.5px"><div class="condition-content"><el-col><!-- 邮递条件字段选择 --><el-form-item><el-select v-model="condition.field" placeholder="邮递条件" class="custom-select":disabled="itemDisabled"><el-option v-for="(selCondition) in selectConditions" :key="selCondition.label":label="selCondition.label" :value="selCondition.value" /></el-select></el-form-item></el-col><el-col><!-- 运算符选择--><el-form-item style="width:100px"><el-select v-model="condition.operator" placeholder="运算符" class="custom-select":disabled="itemDisabled" style="width:100px"><el-option label=">" value=">" /><el-option v-if="!(condition.field === 'email_delivery_success_rate' || condition.field === 'email_delivery_failed_rate')"label="=" value="=" /><el-option label="<" value="<" /></el-select></el-form-item></el-col><el-col class="input-col"><!-- 值输入 --><el-form-item><el-input v-model="condition.value" class="flexible-input" placeholder="有效值":disabled="itemDisabled" style="min-width:200px" @blur="updateAndUnFormat(index)"@input="checkConditionValueInput(index)" @focus="unFormatNumber(index)"><i v-if="condition.field === 'email_delivery_success_rate' || condition.field === 'email_delivery_failed_rate'"slot="suffix" style="font-style: normal; margin-right: 10px;">%</i></el-input></el-form-item></el-col><el-col class="button-col"><!-- 添加和移除按钮 --><div class="button-container"><el-button v-if="showItemBtn" icon="el-icon-plus" class="custom-button round-icon"type="primary" :disabled="itemDisabled" @click="addCondition(index)" /><el-button v-if="showItemBtn" icon="el-icon-close" class="custom-button round-icon"type="danger" :disabled="itemDisabled" @click="removeCondition(index)" /></div></el-col></div></el-row></div></div>
</template><script>
import { v4 as uuidv4 } from 'uuid'
export default {props: {value: {type: Array,required: true},itemDisabled: {type: Boolean,default: true},showItemBtn: {type: Boolean,default: false}},data() {return {selectConditions: [{ label: '邮件人数', value: 'recipient_user', showPercentageSuffix: false },{ label: '新增邮件人数', value: 'new_recipient_user', showPercentageSuffix: false },{ label: '流水金额,分', value: 'pay_amount', showPercentageSuffix: false },{ label: '邮件次数', value: 'recipient_count', showPercentageSuffix: false },{ label: '传输时长,秒', value: 'transfer_duration', showPercentageSuffix: false },{ label: '滞留时长,秒', value: 'dwell_duration', showPercentageSuffix: false },{ label: '运转次数', value: 'operating_cycles', showPercentageSuffix: false },{ label: '邮件传送成功抵达率', value: 'email_delivery_success_rate', showPercentageSuffix: true },{ label: '邮件传送失败成交率', value: 'email_delivery_failed_rate', showPercentageSuffix: true }],addBool: true}},computed: {conditions: {get() {return this.value},set(newValue) {this.$emit('input', newValue)}}},methods: {hasError(index) {const condition = this.conditions[index]return !condition.field || !condition.operator || !condition.value},updateAndUnFormat(index) {this.processConditionValue(index)const oneCondition = this.conditions[index]if (oneCondition.field !== 'email_delivery_success_rate' && oneCondition.field !== 'email_delivery_failed_rate') {oneCondition.value = this.formatNumber(oneCondition.value)}},checkConditionValueInput(index) {const oneCondition = this.conditions[index]const value = oneCondition.valueif (oneCondition.field === 'email_delivery_success_rate' || oneCondition.field === 'email_delivery_failed_rate') {oneCondition.value = this.checkFloatNumber(value) // 调用 checkIntNumber 处理输入值} else {oneCondition.value = this.checkIntNumber(value) // 调用 checkIntNumber 处理输入值}},checkFloatNumber(value) {const number = value.replace(/[^\d.]/g, '') // 清除非数字和小数点字符.replace(/\.{2,}/g, '.') // 只保留一个小数点.replace(/^(-)*(\d+)\.(\d{0,2}).*$/, '$1$2.$3') // 限制小数点后两位return number},checkIntNumber(value) {const number = value.replace(/[^\d.]/g, '') // 清除非数字和小数点字符.replace(/\.{1,}/g, '') // 保留0个小数点return number},formatNumber(value) {// 去除输入中的非数字字符if (!value) return ''const number = value.toString().replace(/\D/g, '')// 格式化为千位分隔符的形式const formatted = new Intl.NumberFormat('en-US').format(number)return formatted},// 去除千位分隔符并返回数字unFormatNumber(index) {this.$nextTick(() => {const oneCondition = this.conditions[index]if (oneCondition.field !== 'email_delivery_success_rate' && oneCondition.field !== 'email_delivery_failed_rate') {// 去除输入中的非数字字符,包括逗号oneCondition.value = oneCondition.value.toString().replace(/[^\d]/g, '')}})},addCondition() {// 创建一个新的条件对象const newCondition = { field: '', operator: '', value: '' }// 将新条件插入到数组末尾this.conditions.push(newCondition)},removeCondition(index) {// 如果只有一个条件,则仅清空输入框的值,不删除条件对象if (this.conditions.length > 1) {this.conditions.splice(index, 1)} else {this.conditions[index].value = ''}},processConditionValue(index) {const oneCondition = this.conditions[index]const value = oneCondition.valueconsole.log('processConditionValue value:', value)if (!value) {return // 如果数字为空,不继续进行后续操作}if (oneCondition.field === 'email_delivery_success_rate' || oneCondition.field === 'email_delivery_failed_rate') {this.handleFloatNumberInput(value, index)} else {this.handleIntNumberInput(value, index)}},handleIntNumberInput(value, index) {// 去除前导0value = value.toString().replace(/^0+(?!$)/, '')// 纠正输入的整数if (value === '' || isNaN(value)) {this.conditions[index].value = ''} else {this.conditions[index].value = value}},handleFloatNumberInput(value, index) {const number = parseFloat(value).toFixed(2) // 将数字转换为浮点数再转换回字符串,去掉前导零if (number < 0 || number > 100) {this.$message.error({message: '输入的范围应为0-100%',duration: 1000})this.conditions[index].value = undefinedreturn}// 判断价格小数部分是否需要补全const needsCompletion = !/\.\d{2}$/.test(value)this.conditions[index].value = number// 如果需要补全,则提示用户if (needsCompletion) {this.$message.info({message: '数字已自动补全为两位小数。',duration: 1000})}},generateKey(index) {return `${this.conditions[index].field}-${index}`},getUUid(index) {return `${uuidv4()}-${this.selectConditions[index]}`},},
}
</script><style scoped>
.condition-item {/* margin-bottom: 10px; */margin-bottom: 18px;
}.condition-row {display: flex;align-items: center;
}.condition-content {display: flex;width: 100%;
}.custom-select {width: 200px;margin-right: 10px;
}.flexible-input {flex: 1;padding-left: 5px;margin-right: 10px;
}.round-icon {width: 32px;height: 32px;border-radius: 50%;font-size: 16px;line-height: 32px;display: flex;justify-content: center;align-items: center;padding: 0;
}.round-icon.el-button--primary {background-color: #38aa7e;color: #ffffff;
}.round-icon.el-button--danger {background-color: #F56C6C;color: #ffffff;
}.error-row {color: #e45b5b;margin-top: 5px;
}.error-text {margin-top: 5px;color: #e45b5b;;height: 16px;line-height: 16px;
}.button-container {display: flex;
}.input-col {/* 添加输入框和按钮之间的间距 */margin-right: 10px;
}
</style>