|
|
@@ -0,0 +1,1040 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <el-drawer
|
|
|
+ v-model="drawer"
|
|
|
+ :title="props.drawerTitle"
|
|
|
+ size="918"
|
|
|
+ @close="handleClose"
|
|
|
+ >
|
|
|
+ <div class="item-title"><span></span> 当报价信息满足以下条件时</div>
|
|
|
+ <el-form
|
|
|
+ ref="formRef"
|
|
|
+ :model="formData"
|
|
|
+ label-width="auto"
|
|
|
+ class="demo-dynamic"
|
|
|
+ :rules="formRules"
|
|
|
+ >
|
|
|
+ <!-- 多组条件容器(组之间是"或者"关系) -->
|
|
|
+ <div class="condition-container">
|
|
|
+ <div
|
|
|
+ v-for="(group, groupIndex) in formData.conditionGroups"
|
|
|
+ :key="group.groupKey"
|
|
|
+ class="condition-group"
|
|
|
+ >
|
|
|
+ <!-- 组内条件行(行之间是"并且"关系) -->
|
|
|
+ <el-form-item
|
|
|
+ v-for="(domain, rowIndex) in group.actions"
|
|
|
+ :key="domain.key"
|
|
|
+ :prop="`conditionGroups.${groupIndex}.actions.${rowIndex}`"
|
|
|
+ :error="domain.errorMsg"
|
|
|
+ >
|
|
|
+ <div class="condition-row">
|
|
|
+ <!-- 1. 条件类型选择框 -->
|
|
|
+ <el-select
|
|
|
+ v-model="domain.attrCode"
|
|
|
+ placeholder="选择条件"
|
|
|
+ style="width: 341px"
|
|
|
+ @change="handleConditionChange(domain)"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in dispatchList"
|
|
|
+ :key="item.attrCode"
|
|
|
+ :label="item.attrName"
|
|
|
+ :value="item.attrCode"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+
|
|
|
+ <!-- 2. 运算符选择框 -->
|
|
|
+ <el-select
|
|
|
+ v-model="domain.operator"
|
|
|
+ placeholder="选择运算符"
|
|
|
+ style="width: 100px"
|
|
|
+ :disabled="!domain.attrCode"
|
|
|
+ clearable
|
|
|
+ @change="handleOperatorChange(domain)"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in getOperations(domain.attrCode)"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+
|
|
|
+ <!-- 3. 值输入区域(动态切换) -->
|
|
|
+ <!-- 单选按钮组 -->
|
|
|
+ <el-radio-group
|
|
|
+ v-model="domain.min"
|
|
|
+ v-if="getConditionType(domain.attrCode) === 'radio'"
|
|
|
+ >
|
|
|
+ <el-radio
|
|
|
+ :value="item.id"
|
|
|
+ size="large"
|
|
|
+ v-for="item in getOptions(domain.attrCode)"
|
|
|
+ :key="item.id"
|
|
|
+ >{{ item.name }}</el-radio
|
|
|
+ >
|
|
|
+ </el-radio-group>
|
|
|
+
|
|
|
+ <!-- 多选下拉 -->
|
|
|
+ <el-select
|
|
|
+ v-model="domain.minArray"
|
|
|
+ placeholder="选择值"
|
|
|
+ multiple
|
|
|
+ style="width: 341px"
|
|
|
+ v-if="getConditionType(domain.attrCode) === 'checkbox'"
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in getOptions(domain.attrCode)"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+
|
|
|
+ <!-- 单选下拉 -->
|
|
|
+ <el-select
|
|
|
+ v-model="domain.max"
|
|
|
+ placeholder="选择值"
|
|
|
+ style="width: 341px"
|
|
|
+ v-if="getConditionType(domain.attrCode) === 'select'"
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in getOptions(domain.attrCode)"
|
|
|
+ :key="item.code"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.code"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+
|
|
|
+ <!-- 数值输入 -->
|
|
|
+ <el-input
|
|
|
+ v-model="domain.min"
|
|
|
+ placeholder="输入数值"
|
|
|
+ type="number"
|
|
|
+ style="width: 341px"
|
|
|
+ v-if="
|
|
|
+ getConditionType(domain.attrCode) === 'inputNumber' &&
|
|
|
+ domain.operator !== '1'
|
|
|
+ "
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+ <!-- 数值输入范围 -->
|
|
|
+ <div
|
|
|
+ v-if="
|
|
|
+ getConditionType(domain.attrCode) === 'inputNumber' &&
|
|
|
+ domain.operator == '1'
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-input-number
|
|
|
+ v-model="domain.min"
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ style="width: 160px"
|
|
|
+ ></el-input-number>
|
|
|
+ 至
|
|
|
+ <el-input-number
|
|
|
+ v-model="domain.max"
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ style="width: 170px"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 标签输入 -->
|
|
|
+ <el-input-tag
|
|
|
+ v-model="domain.minArray"
|
|
|
+ style="width: 341px"
|
|
|
+ v-if="getConditionType(domain.attrCode) === 'inputTag'"
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ placeholder="输入内容后按回车键隔开"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 日期选择 -->
|
|
|
+ <el-date-picker
|
|
|
+ v-model="domain.max"
|
|
|
+ type="date"
|
|
|
+ placeholder="选择日期"
|
|
|
+ style="width: 341px"
|
|
|
+ v-if="
|
|
|
+ getConditionType(domain.attrCode) === 'datetime' &&
|
|
|
+ domain.operator !== '1'
|
|
|
+ "
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 日期范围选择 -->
|
|
|
+ <div
|
|
|
+ v-if="
|
|
|
+ getConditionType(domain.attrCode) === 'datetime' &&
|
|
|
+ domain.operator == '1'
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-date-picker
|
|
|
+ v-model="domain.min"
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ type="datetime"
|
|
|
+ format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ time-format="HH:mm:ss"
|
|
|
+ placeholder="选择日期时间"
|
|
|
+ style="width: 170px"
|
|
|
+ >
|
|
|
+ </el-date-picker>
|
|
|
+ 至
|
|
|
+ <el-date-picker
|
|
|
+ v-model="domain.max"
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ type="datetime"
|
|
|
+ format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ time-format="HH:mm:ss"
|
|
|
+ placeholder="选择日期时间"
|
|
|
+ style="width: 170px"
|
|
|
+ >
|
|
|
+ </el-date-picker>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 车牌级联选择 -->
|
|
|
+ <el-cascader
|
|
|
+ :options="getOptions(domain.attrCode)"
|
|
|
+ :props="cascaderProps"
|
|
|
+ v-model="domain.max"
|
|
|
+ placeholder="选择车牌"
|
|
|
+ clearable
|
|
|
+ v-if="getConditionType(domain.attrCode) === 'plate'"
|
|
|
+ style="width: 341px"
|
|
|
+ :disabled="!domain.operator"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 4. 删除行按钮 -->
|
|
|
+ <el-button
|
|
|
+ type="text"
|
|
|
+ :icon="Delete"
|
|
|
+ size="small"
|
|
|
+ style="color: #333333"
|
|
|
+ @click="handleRemoveRow(groupIndex, rowIndex)"
|
|
|
+ v-if="group.actions.length > 1"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 组内添加并且条件按钮 -->
|
|
|
+ <el-button
|
|
|
+ type="text"
|
|
|
+ @click="handleAddRow(groupIndex)"
|
|
|
+ class="add-condition-btn"
|
|
|
+ >
|
|
|
+ + 并且条件
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 添加或者条件组按钮 -->
|
|
|
+ <el-form-item class="add-group-btn">
|
|
|
+ <el-button type="primary" @click="handleAddGroup">
|
|
|
+ + 或者条件
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <!-- 执行动作区域 -->
|
|
|
+ <!-- 执行动作区域 -->
|
|
|
+ <div class="item-title"><span></span> 将执行以下动作</div>
|
|
|
+ <el-form
|
|
|
+ :model="formData"
|
|
|
+ :rules="formRules"
|
|
|
+ ref="formRef"
|
|
|
+ class="action-form"
|
|
|
+ >
|
|
|
+ <!-- 指定报价协议 -->
|
|
|
+ <el-form-item
|
|
|
+ prop="formdata.actionJson[0].agreementId"
|
|
|
+ class="form-item"
|
|
|
+ >
|
|
|
+ <span>指定报价协议:</span>
|
|
|
+ <el-select
|
|
|
+ v-model="formData.actionJson[0].agreementId"
|
|
|
+ placeholder="选择报价协议"
|
|
|
+ style="width: 240px"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in agreementList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.agreementName"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 对接人变更方式选择 -->
|
|
|
+ <el-form-item>
|
|
|
+ <el-radio-group
|
|
|
+ v-model="formData.actionJson[1].linkType"
|
|
|
+ @change="handleTypeChange"
|
|
|
+ >
|
|
|
+ <!-- 手动输入对接人 -->
|
|
|
+ <el-radio
|
|
|
+ value="manual_input"
|
|
|
+ size="large"
|
|
|
+ style="margin: 20px 0; display: block"
|
|
|
+ >
|
|
|
+ 手动输入对接人信息:
|
|
|
+ <el-form-item
|
|
|
+ prop="actionJson.name"
|
|
|
+ style="display: inline-block; margin: 0 20px"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="formData.actionJson[1].name"
|
|
|
+ style="width: 327px"
|
|
|
+ placeholder="变更后对接人姓名"
|
|
|
+ :disabled="formData.actionJson[1].linkType === 'select_account'"
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ prop="actionJson.phone"
|
|
|
+ style="display: inline-block"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="formData.actionJson[1].phone"
|
|
|
+ style="width: 327px"
|
|
|
+ type="number"
|
|
|
+ placeholder="变更后对接人手机号"
|
|
|
+ maxlength="11"
|
|
|
+ @input="handlePhoneInput"
|
|
|
+ :disabled="formData.actionJson[1].linkType === 'select_account'"
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-radio>
|
|
|
+
|
|
|
+ <!-- 选择保司账号 -->
|
|
|
+ <el-radio
|
|
|
+ value="select_account"
|
|
|
+ size="large"
|
|
|
+ style="margin: 20px 0; display: block"
|
|
|
+ >
|
|
|
+ 选择已有保司账号:
|
|
|
+ <el-form-item
|
|
|
+ prop="actionJson.accountId"
|
|
|
+ style="display: inline-block; margin-left: 20px"
|
|
|
+ >
|
|
|
+ <el-select
|
|
|
+ v-model="formData.actionJson[1].accountId"
|
|
|
+ placeholder="选择变更后的保司账号"
|
|
|
+ style="width: 662px"
|
|
|
+ :disabled="formData.actionJson[1].linkType === 'manual_input'"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <!-- -->
|
|
|
+ <el-option
|
|
|
+ v-for="item in accountList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.username"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <!-- 抽屉底部按钮 -->
|
|
|
+ <template #footer>
|
|
|
+ <div class="foot">
|
|
|
+ <el-button @click="handleClose"> 取消 </el-button>
|
|
|
+ <el-button type="primary" @click="submit"> 保存 </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-drawer>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { reactive, ref, type Ref, onMounted } from "vue";
|
|
|
+import type { FormInstance, FormRules } from "element-plus";
|
|
|
+import { Delete } from "@element-plus/icons-vue";
|
|
|
+import { ElMessage } from "element-plus";
|
|
|
+import api from "@/api";
|
|
|
+import { getDictItems } from "@/api/dict";
|
|
|
+
|
|
|
+// 抽屉状态
|
|
|
+const drawer = ref(true);
|
|
|
+
|
|
|
+const emit = defineEmits(["close"]);
|
|
|
+
|
|
|
+// 组件props
|
|
|
+const props = defineProps({
|
|
|
+ companyId: {
|
|
|
+ type: [String, Number],
|
|
|
+ required: true,
|
|
|
+ validator: (value: string | number) => value !== "" && value !== 0,
|
|
|
+ },
|
|
|
+ editData: {},
|
|
|
+ drawerTitle: {
|
|
|
+ type: String,
|
|
|
+ default: "规则配置",
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+// 表单实例引用
|
|
|
+const formRef: Ref<FormInstance | undefined> = ref(undefined);
|
|
|
+
|
|
|
+// 级联选择器配置
|
|
|
+const cascaderProps = { multiple: true, checkStrictly: true };
|
|
|
+
|
|
|
+// 数据类型定义
|
|
|
+interface ConditionRow {
|
|
|
+ key: number;
|
|
|
+ min: any;
|
|
|
+ operator: string;
|
|
|
+ attrCode: string;
|
|
|
+ max: any;
|
|
|
+ errorMsg: string;
|
|
|
+ minArray?: string[];
|
|
|
+}
|
|
|
+
|
|
|
+interface ConditionGroup {
|
|
|
+ groupKey: number;
|
|
|
+ actions: ConditionRow[];
|
|
|
+}
|
|
|
+
|
|
|
+// 表单核心数据
|
|
|
+// 表单核心数据 - 优化actionJson结构
|
|
|
+const formData = ref<{
|
|
|
+ conditionGroups: ConditionGroup[];
|
|
|
+ actionJson: [
|
|
|
+ {
|
|
|
+ actionCode: "specify_agreement"; // 固定:指定报价协议
|
|
|
+ agreementId: string; // 报价协议ID
|
|
|
+ },
|
|
|
+ {
|
|
|
+ actionCode: "change_bs_account"; // 固定:变更对接人
|
|
|
+ linkType: "manual_input" | "select_account"; // 对接人方式:手动输入/选择账号
|
|
|
+ name: string; // 手动输入-姓名
|
|
|
+ phone: string; // 手动输入-手机号
|
|
|
+ accountId: string; // 选择账号-账号ID
|
|
|
+ }
|
|
|
+ ];
|
|
|
+}>({
|
|
|
+ actionJson: [
|
|
|
+ {
|
|
|
+ actionCode: "specify_agreement",
|
|
|
+ agreementId: "", // 初始为空
|
|
|
+ },
|
|
|
+ {
|
|
|
+ actionCode: "change_bs_account",
|
|
|
+ linkType: "manual_input", // 默认选中“手动输入”
|
|
|
+ name: "",
|
|
|
+ phone: "",
|
|
|
+ accountId: "",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ conditionGroups: [
|
|
|
+ {
|
|
|
+ groupKey: Date.now(),
|
|
|
+ actions: [
|
|
|
+ {
|
|
|
+ attrCode: "",
|
|
|
+ key: Date.now(),
|
|
|
+ min: "",
|
|
|
+ operator: "",
|
|
|
+ max: "",
|
|
|
+ errorMsg: "",
|
|
|
+ minArray: [],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+});
|
|
|
+
|
|
|
+// 下拉选项数据源
|
|
|
+const dispatchList = ref([]);
|
|
|
+const agreementList = ref([]);
|
|
|
+const accountList = ref([]);
|
|
|
+const dictionaries = ref([]);
|
|
|
+
|
|
|
+const dispatchId = ref(""); //调度id
|
|
|
+
|
|
|
+// 表单校验规则
|
|
|
+// 先定义独立校验函数(放在formRules之前)
|
|
|
+const validateConditionGroups = (rule: any, value: ConditionGroup[], callback: any) => {
|
|
|
+ // 1. 校验:至少1组条件
|
|
|
+ if (value.length === 0) {
|
|
|
+ return callback(new Error("至少配置1组“或者”条件"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 遍历每组校验
|
|
|
+ for (const group of value) {
|
|
|
+ // 2.1 校验:每组至少1行“并且”条件
|
|
|
+ if (group.actions.length === 0) {
|
|
|
+ return callback(new Error("每组条件至少包含1条“并且”规则"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2.2 遍历每行校验完整性
|
|
|
+ for (const row of group.actions) {
|
|
|
+ row.errorMsg = ""; // 清空历史错误,避免残留
|
|
|
+ const attrType = getConditionType(row.attrCode);
|
|
|
+ const isRangeOp = row.operator === "1"; // 是否为“介于”等范围运算符
|
|
|
+
|
|
|
+ // 校验1:条件类型未选择
|
|
|
+ if (!row.attrCode) {
|
|
|
+ row.errorMsg = "请选择条件类型(如“报价金额”“车辆类型”)";
|
|
|
+ return callback(new Error(row.errorMsg));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验2:运算符未选择
|
|
|
+ if (!row.operator) {
|
|
|
+ row.errorMsg = "请选择运算符(如“等于”“大于”)";
|
|
|
+ return callback(new Error(row.errorMsg));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验3:根据条件类型校验“值”
|
|
|
+ // 3.1 多选/标签类型(依赖minArray)
|
|
|
+ if (["checkbox", "inputTag"].includes(attrType)) {
|
|
|
+ if (!row.minArray || row.minArray.length === 0) {
|
|
|
+ row.errorMsg = `请选择至少一个条件值(当前为${attrType === "checkbox" ? "多选" : "标签"}类型)`;
|
|
|
+ return callback(new Error(row.errorMsg));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 3.2 范围类型(需同时填min和max)
|
|
|
+ else if (isRangeOp) {
|
|
|
+ if (row.min === null || row.min === "" || row.min === undefined) {
|
|
|
+ row.errorMsg = "请填写范围起始值(如“1000”)";
|
|
|
+ return callback(new Error(row.errorMsg));
|
|
|
+ }
|
|
|
+ if (row.max === null || row.max === "" || row.max === undefined) {
|
|
|
+ row.errorMsg = "请填写范围结束值(如“2000”)";
|
|
|
+ return callback(new Error(row.errorMsg));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 3.3 普通类型(单选/输入框,依赖min或max)
|
|
|
+ else {
|
|
|
+ const targetVal = ["radio", "select", "plate", "datetime"].includes(attrType)
|
|
|
+ ? row.max
|
|
|
+ : row.min;
|
|
|
+ if (targetVal === null || targetVal === "" || targetVal === undefined) {
|
|
|
+ row.errorMsg = `请${attrType === "datetime" ? "选择" : "输入"}条件值(当前为${attrType === "select" ? "下拉选择" : "文本输入"}类型)`;
|
|
|
+ return callback(new Error(row.errorMsg));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 所有校验通过
|
|
|
+ callback();
|
|
|
+};
|
|
|
+
|
|
|
+const validateAgreementId = (rule: any, value: string, callback: any) => {
|
|
|
+ if (!value) {
|
|
|
+ return callback(new Error("请选择需要指定的报价协议"));
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+};
|
|
|
+
|
|
|
+const validateLinkManName = (rule: any, value: string, callback: any) => {
|
|
|
+ const linkType = formData.value.actionJson[1].linkType;
|
|
|
+ if (linkType === "manual_input" && !value?.trim()) {
|
|
|
+ return callback(new Error("请输入对接人姓名(手动输入模式)"));
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+};
|
|
|
+
|
|
|
+const validateLinkManPhone = (rule: any, value: string, callback: any) => {
|
|
|
+ const linkType = formData.value.actionJson[1].linkType;
|
|
|
+ if (linkType !== "manual_input") {
|
|
|
+ return callback();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!value) {
|
|
|
+ return callback(new Error("请输入对接人手机号(手动输入模式)"));
|
|
|
+ }
|
|
|
+ if (!/^1[3-9]\d{9}$/.test(value)) {
|
|
|
+ return callback(new Error("手机号格式错误,请输入11位有效号码(如13800138000)"));
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+};
|
|
|
+
|
|
|
+const validateAccountId = (rule: any, value: string, callback: any) => {
|
|
|
+ const linkType = formData.value.actionJson[1].linkType;
|
|
|
+ if (linkType === "select_account" && !value) {
|
|
|
+ return callback(new Error("请选择变更后的保司账号(选择账号模式)"));
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+};
|
|
|
+
|
|
|
+// 优化后的formRules
|
|
|
+const formRules = reactive<FormRules>({
|
|
|
+ // 1. 条件组校验(核心)
|
|
|
+ conditionGroups: [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ trigger: ["submit", "blur"], // 移除频繁的change触发,仅在提交和失焦时校验
|
|
|
+ // validator: validateConditionGroups,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+
|
|
|
+ // 2. 执行动作:指定报价协议(必选)
|
|
|
+ "actionJson[0].agreementId": [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ trigger: ["submit", "blur"], // 下拉选择无需change触发,失焦+提交足够
|
|
|
+ validator: validateAgreementId,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+
|
|
|
+ // 3. 执行动作:对接人姓名(仅手动输入时必选)
|
|
|
+ "actionJson[1].name": [
|
|
|
+ {
|
|
|
+ trigger: ["submit", "blur"],
|
|
|
+ validator: validateLinkManName,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+
|
|
|
+ // 4. 执行动作:对接人手机号(仅手动输入时必选+格式校验)
|
|
|
+ "actionJson[1].phone": [
|
|
|
+ {
|
|
|
+ trigger: ["submit", "blur"], // 移除input触发,避免输入过程中频繁报错
|
|
|
+ validator: validateLinkManPhone,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+
|
|
|
+ // 5. 执行动作:保司账号(仅选择账号时必选)
|
|
|
+ "actionJson[1].accountId": [
|
|
|
+ {
|
|
|
+ trigger: ["submit", "blur"],
|
|
|
+ validator: validateAccountId,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+});
|
|
|
+
|
|
|
+
|
|
|
+// 工具函数:根据条件类型获取对应的运算符列表
|
|
|
+const getOperations = (conditionValue: string) => {
|
|
|
+ const target = dispatchList.value.find(
|
|
|
+ (item) => item.attrCode === conditionValue
|
|
|
+ );
|
|
|
+ return target?.rulesDict || [];
|
|
|
+};
|
|
|
+
|
|
|
+// 工具函数:根据条件类型获取对应的选项列表
|
|
|
+const getOptions = (conditionValue: string) => {
|
|
|
+ const target = dispatchList.value.find(
|
|
|
+ (item) => item.attrCode === conditionValue
|
|
|
+ );
|
|
|
+ return target?.dictLabal || [];
|
|
|
+};
|
|
|
+
|
|
|
+// 工具函数:根据条件类型获取输入组件类型
|
|
|
+const getConditionType = (conditionValue: string) => {
|
|
|
+ const target = dispatchList.value.find(
|
|
|
+ (item) => item.attrCode === conditionValue
|
|
|
+ );
|
|
|
+ return target?.attrType || "input";
|
|
|
+};
|
|
|
+
|
|
|
+// 条件切换时的处理
|
|
|
+const handleConditionChange = (domain: ConditionRow) => {
|
|
|
+ domain.max = null;
|
|
|
+ domain.operator = "";
|
|
|
+};
|
|
|
+
|
|
|
+// 运算符切换时重置值
|
|
|
+const handleOperatorChange = (domain: ConditionRow) => {
|
|
|
+ const attrType = getConditionType(domain.attrCode);
|
|
|
+ // 重置对应类型的值
|
|
|
+ if (["checkbox", "inputTag"].includes(attrType)) {
|
|
|
+ domain.minArray = [];
|
|
|
+ } else {
|
|
|
+ domain.min = null;
|
|
|
+ domain.max = null;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 组内添加并且条件行
|
|
|
+const handleAddRow = (groupIndex: number) => {
|
|
|
+ formData.conditionGroups[groupIndex].actions.push({
|
|
|
+ key: Date.now(),
|
|
|
+ min: "",
|
|
|
+ attrCode: "",
|
|
|
+ operator: "",
|
|
|
+ max: null,
|
|
|
+ errorMsg: "",
|
|
|
+ minArray: [],
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 删除组内条件行
|
|
|
+const handleRemoveRow = (groupIndex: number, rowIndex: number) => {
|
|
|
+ formData.conditionGroups[groupIndex].actions.splice(rowIndex, 1);
|
|
|
+};
|
|
|
+
|
|
|
+// 添加新的或者条件组
|
|
|
+const handleAddGroup = () => {
|
|
|
+ formData.conditionGroups.push({
|
|
|
+ groupKey: Date.now(),
|
|
|
+ actions: [
|
|
|
+ {
|
|
|
+ key: Date.now(),
|
|
|
+ min: "",
|
|
|
+ attrCode: "",
|
|
|
+ operator: "",
|
|
|
+ max: null,
|
|
|
+ errorMsg: "",
|
|
|
+ minArray: [],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 手机号输入处理
|
|
|
+const handlePhoneInput = () => {
|
|
|
+ const phone = formData.value.actionJson[1].phone;
|
|
|
+ if (phone) {
|
|
|
+ // 截取前11位,去除非数字字符
|
|
|
+ const filtered = phone.replace(/\D/g, "").slice(0, 11);
|
|
|
+ formData.value.actionJson[1].phone = filtered;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取条件类型列表
|
|
|
+const getList = async () => {
|
|
|
+ try {
|
|
|
+ const res = await api.dispatchApi.dispatchList({});
|
|
|
+ dispatchList.value = res.data;
|
|
|
+ } catch (error) {
|
|
|
+ console.error("获取条件类型列表失败:", error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取报价协议列表
|
|
|
+const getByCompanyId = async () => {
|
|
|
+ try {
|
|
|
+ const res = await api.dispatchApi.getByCompanyIdt(props.companyId);
|
|
|
+ agreementList.value = res.data;
|
|
|
+ } catch (error) {
|
|
|
+ console.error("获取报价协议列表失败:", error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取保司账号列表
|
|
|
+const getAccountList = async () => {
|
|
|
+ try {
|
|
|
+ const res = await api.dispatchApi.getAgreementAttributionByCompanyId(props.companyId);
|
|
|
+ accountList.value = res.data;
|
|
|
+ } catch (error) {
|
|
|
+ console.error("获取保司账号列表失败:", error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+//清空选项数据
|
|
|
+const handleTypeChange = () => {
|
|
|
+ const linkType = formData.value.actionJson[1].linkType;
|
|
|
+ const actionJson = formData.value.actionJson[1];
|
|
|
+
|
|
|
+ if (linkType === "manual_input") {
|
|
|
+ // 切换到手动输入:清空账号ID
|
|
|
+ actionJson.accountId = "";
|
|
|
+ } else if (linkType === "select_account") {
|
|
|
+ // 切换到选择账号:清空姓名和手机号
|
|
|
+ actionJson.name = "";
|
|
|
+ actionJson.phone = "";
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 提交表单
|
|
|
+const submit = async () => {
|
|
|
+ // if (!formRef.value) return;
|
|
|
+
|
|
|
+
|
|
|
+ // await formRef.value.validate();
|
|
|
+ const actionJson = formData.value.actionJson;
|
|
|
+
|
|
|
+ // 构建提交的actions数据
|
|
|
+ // const actions =formData.value.actionJson
|
|
|
+ const actions = [
|
|
|
+ // 报价协议动作
|
|
|
+ {
|
|
|
+ actionCode: actionJson[0].actionCode,
|
|
|
+ actionJson:{
|
|
|
+
|
|
|
+ agreementId: actionJson[0].agreementId,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 对接人动作(仅保留当前方式的有效字段)
|
|
|
+ {
|
|
|
+ actionCode: actionJson[1].linkType=='manual_input'?'change_poc':'change_bs_account',
|
|
|
+ actionJson:{
|
|
|
+ ...(actionJson[1].linkType === "manual_input"
|
|
|
+ ? { name: actionJson[1].name, phone: actionJson[1].phone }
|
|
|
+ : { accountId: actionJson[1].accountId }),
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 最终提交数据
|
|
|
+ const submitData = {
|
|
|
+ companyId: props.companyId + "",
|
|
|
+ attrLinks: formData.value.conditionGroups.flatMap((group => group.actions)),
|
|
|
+ actions,
|
|
|
+ // ...(dispatchId.value && { dispatchId: dispatchId.value }), // 编辑时携带ID
|
|
|
+ };
|
|
|
+
|
|
|
+ // 调用接口(新增/编辑)
|
|
|
+ const apiFunc = dispatchId.value
|
|
|
+ ? api.dispatchApi.updateAgreementDispatch
|
|
|
+ : api.dispatchApi.addAgreementDispatch;
|
|
|
+ const res = await apiFunc(submitData);
|
|
|
+
|
|
|
+ if (res.code === 200) {
|
|
|
+ ElMessage.success('保存成功');
|
|
|
+ handleClose();
|
|
|
+ } else {
|
|
|
+ ElMessage.error('保存失败');
|
|
|
+ }
|
|
|
+ // try {
|
|
|
+ // } catch (error) {
|
|
|
+ // ElMessage.error("表单校验失败,请检查填写内容");
|
|
|
+ // }
|
|
|
+};
|
|
|
+
|
|
|
+// 关闭抽屉
|
|
|
+const handleClose = () => {
|
|
|
+ emit("close", false);
|
|
|
+};
|
|
|
+
|
|
|
+// 组件挂载时初始化数据
|
|
|
+// onMounted(async () => {
|
|
|
+// await getList();
|
|
|
+// await getByCompanyId();
|
|
|
+// await getAccountList();
|
|
|
+// dictionaries.value = await getDictItems({ dictCode: "dispatch_action" });
|
|
|
+// console.log(dictionaries.value);
|
|
|
+// formData.value = props.editData;
|
|
|
+// });
|
|
|
+onMounted(async () => {
|
|
|
+ await Promise.all([getList(), getByCompanyId(), getAccountList()]);
|
|
|
+ dictionaries.value = await getDictItems({ dictCode: "dispatch_action" });
|
|
|
+
|
|
|
+ dispatchId.value = props.editData.dispatchId;
|
|
|
+ if (props.editData && Object.keys(props.editData).length > 0) {
|
|
|
+ const rawData = props.editData;
|
|
|
+
|
|
|
+ const conditionGroups: ConditionGroup[] = [];
|
|
|
+ const rawGroups = rawData.groupedAttrLinks
|
|
|
+ ? rawData.groupedAttrLinks
|
|
|
+ : rawData.attrLinks
|
|
|
+ ? [rawData.attrLinks]
|
|
|
+ : [];
|
|
|
+
|
|
|
+ rawGroups.forEach((rawGroup: any[], groupIndex: number) => {
|
|
|
+ const group: ConditionGroup = {
|
|
|
+ groupKey: rawData.dispatchId || Date.now(),
|
|
|
+ actions: [], // 组内"并且"条件行
|
|
|
+ };
|
|
|
+
|
|
|
+ rawGroup.forEach((rawAttr: any) => {
|
|
|
+ // 每个原始条件项对应一个 action 行
|
|
|
+ group.actions.push({
|
|
|
+ key: rawAttr.id || Date.now(),
|
|
|
+ attrCode: rawAttr.attrCode || "",
|
|
|
+ operator: rawAttr.operator || "",
|
|
|
+ min: rawAttr.min ? parseInt(rawAttr.min) : "",
|
|
|
+ max: rawAttr.max ? parseInt(rawAttr.max) : "",
|
|
|
+ // 强制确保minArray为数组(避免undefined导致校验失败)
|
|
|
+ minArray: rawAttr.minArray ? [...rawAttr.minArray] : [],
|
|
|
+ errorMsg: "",
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 确保每个组至少有一行条件(防止空组)
|
|
|
+ if (group.actions.length === 0) {
|
|
|
+ group.actions.push({
|
|
|
+ key: Date.now(),
|
|
|
+ attrCode: "",
|
|
|
+ operator: "",
|
|
|
+ min: "",
|
|
|
+ max: "",
|
|
|
+ errorMsg: "",
|
|
|
+ minArray: [],
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ conditionGroups.push(group);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 3. 组装最终的formData
|
|
|
+ formData.value = {
|
|
|
+ agreement: "",
|
|
|
+ actionJson: [
|
|
|
+ // 第一个对象:报价协议(对应初始 actionJson[0])
|
|
|
+ {
|
|
|
+ actionCode: "specify_agreement",
|
|
|
+ agreementId: "", // 从编辑数据中取协议ID
|
|
|
+ },
|
|
|
+ // 第二个对象:对接人信息(对应初始 actionJson[1])
|
|
|
+ {
|
|
|
+ actionCode: "",
|
|
|
+ accountId: "",
|
|
|
+ name: "",
|
|
|
+ phone: "",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ conditionGroups:
|
|
|
+ conditionGroups.length > 0
|
|
|
+ ? conditionGroups
|
|
|
+ : [
|
|
|
+ {
|
|
|
+ groupKey: Date.now(),
|
|
|
+ actions: [
|
|
|
+ {
|
|
|
+ key: Date.now(),
|
|
|
+ attrCode: "",
|
|
|
+ operator: "",
|
|
|
+ min: "",
|
|
|
+ max: "",
|
|
|
+ errorMsg: "",
|
|
|
+ minArray: [],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ // 如果没有编辑数据,使用默认初始值
|
|
|
+ formData.value = {
|
|
|
+ agreement: "",
|
|
|
+ actionJson: [
|
|
|
+ // 第一个对象:报价协议(对应初始 actionJson[0])
|
|
|
+ {
|
|
|
+ actionCode: "specify_agreement",
|
|
|
+ agreementId: "", // 从编辑数据中取协议ID
|
|
|
+ },
|
|
|
+ // 第二个对象:对接人信息(对应初始 actionJson[1])
|
|
|
+ {
|
|
|
+ actionCode: "", // 显式添加 actionCode
|
|
|
+ accountId: "",
|
|
|
+ name: "",
|
|
|
+ phone: "",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ conditionGroups: [
|
|
|
+ {
|
|
|
+ groupKey: Date.now(),
|
|
|
+ actions: [
|
|
|
+ {
|
|
|
+ key: Date.now(),
|
|
|
+ attrCode: "",
|
|
|
+ operator: "",
|
|
|
+ min: "",
|
|
|
+ max: "",
|
|
|
+ errorMsg: "",
|
|
|
+ minArray: [],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ }
|
|
|
+ console.log(formData.value);
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.condition-container {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.condition-group {
|
|
|
+ width: 100%;
|
|
|
+ padding: 24px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ border: 1px solid #eee;
|
|
|
+ border-radius: 6px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background-color: #fff;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.group-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ padding-bottom: 8px;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.condition-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+
|
|
|
+.add-condition-btn {
|
|
|
+ margin-top: 8px;
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+
|
|
|
+.add-group-btn {
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+
|
|
|
+.item-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ span {
|
|
|
+ display: inline-block;
|
|
|
+ width: 4px;
|
|
|
+ height: 18px;
|
|
|
+ background: #0083ff;
|
|
|
+ border-radius: 10px 10px 10px 10px;
|
|
|
+ margin-right: 8px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.form-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ span {
|
|
|
+ font-size: 14px;
|
|
|
+ margin-right: 10px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.foot {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ padding-top: 10px;
|
|
|
+ border-top: 1px solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.main-container) {
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+:deep(.el-row) {
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+:deep(.el-drawer__header) {
|
|
|
+ height: 48px;
|
|
|
+ font-size: 20px;
|
|
|
+ color: #333333;
|
|
|
+ border-bottom: 1px solid #eeeeee;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ padding-bottom: 20px;
|
|
|
+}
|
|
|
+:deep(.el-drawer__body) {
|
|
|
+ padding: 0 24px;
|
|
|
+}
|
|
|
+</style>
|