rule.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032
  1. <template>
  2. <div>
  3. <el-drawer v-model="drawer" :title="props.drawerTitle" size="918" @close="handleClose">
  4. <div class="item-title"><span></span> 当报价信息满足以下条件时</div>
  5. <el-form ref="formRef" :model="formData" label-width="auto" class="demo-dynamic" :rules="formRules">
  6. <!-- 多组条件容器(组之间是"或者"关系) -->
  7. <div class="condition-container">
  8. <div v-for="(group, groupIndex) in formData.conditionGroups" :key="group.groupKey" class="condition-group">
  9. <!-- 组内条件行(行之间是"并且"关系) -->
  10. <el-form-item v-for="(domain, rowIndex) in group.actions" :key="domain.key"
  11. :prop="`conditionGroups.${groupIndex}.actions.${rowIndex}`" :error="domain.errorMsg">
  12. <div class="condition-row">
  13. <!-- 1. 条件类型选择框 -->
  14. <el-select v-model="domain.attrCode" placeholder="选择条件" style="width: 341px"
  15. @change="(val) => handleConditionChange(domain, val)" clearable>
  16. <el-option v-for="item in dispatchList" :key="item.attrCode" :label="item.attrName"
  17. :value="item.attrCode" />
  18. </el-select>
  19. <!-- 2. 运算符选择框 -->
  20. <el-select v-model="domain.operator" placeholder="选择运算符" style="width: 100px"
  21. :disabled="!domain.attrCode" clearable @change="handleOperatorChange(domain)">
  22. <el-option v-for="item in getOperations(domain.attrCode)" :key="item.value" :label="item.label"
  23. :value="item.value" />
  24. </el-select>
  25. <!-- 3. 值输入区域(动态切换) -->
  26. <!-- 单选按钮组 -->
  27. <el-radio-group v-model="domain.min" v-if="getConditionType(domain.attrCode) === 'radio'">
  28. <el-radio :value="item.id + ''" size="large" v-for="item in getOptions(domain.attrCode)"
  29. :key="item.id + ''">{{ item.name }}</el-radio>
  30. </el-radio-group>
  31. <!-- 多选下拉 -->
  32. <el-select v-model="domain.minArray" placeholder="选择值" multiple style="width: 341px"
  33. v-if="getConditionType(domain.attrCode) === 'checkbox'" :disabled="!domain.operator" clearable>
  34. <template #header>
  35. <el-checkbox v-model="domain.checkAll" :indeterminate="domain.indeterminate"
  36. @change="(val) => handleCheckAll(domain, val)">
  37. 全选
  38. </el-checkbox>
  39. </template>
  40. <el-option v-for="(item, index) in getOptions(domain.attrCode)" :key="index" :label="item.name"
  41. :value="item.idStr" />
  42. </el-select>
  43. <!-- 单选下拉 -->
  44. <el-select v-model="domain.minArray" placeholder="选择值" style="width: 341px" multiple
  45. @change="change(domain)" v-if="getConditionType(domain.attrCode) === 'select'"
  46. :disabled="!domain.operator" clearable>
  47. <template #header>
  48. <el-checkbox v-model="domain.checkAll" :indeterminate="domain.indeterminate"
  49. @change="(val) => handleCheckAll(domain, val)">
  50. 全选
  51. </el-checkbox>
  52. </template>
  53. <el-option v-for="item in getOptions(domain.attrCode)" :key="item.idStr" :label="item.name"
  54. :value="item.idStr" />
  55. </el-select>
  56. <!-- 数值输入 -->
  57. <el-input v-model="domain.min" placeholder="输入数值" type="number" style="width: 341px" v-if="
  58. getConditionType(domain.attrCode) === 'inputNumber' &&
  59. domain.operator !== '1'
  60. " :disabled="!domain.operator" clearable />
  61. <!-- 数值输入范围 -->
  62. <div v-if="
  63. getConditionType(domain.attrCode) === 'inputNumber' &&
  64. domain.operator == '1'
  65. ">
  66. <el-input-number v-model="domain.min" :disabled="!domain.operator"
  67. style="width: 160px"></el-input-number>
  68. <el-input-number v-model="domain.max" :disabled="!domain.operator"
  69. style="width: 170px"></el-input-number>
  70. </div>
  71. <!-- 标签输入 -->
  72. <el-input-tag v-model="domain.minArray" style="width: 341px"
  73. v-if="getConditionType(domain.attrCode) === 'inputTag'" :disabled="!domain.operator"
  74. placeholder="输入内容后按回车键隔开" />
  75. <!-- 日期选择 -->
  76. <el-date-picker v-model="domain.max" type="date" placeholder="选择日期" style="width: 341px" v-if="
  77. getConditionType(domain.attrCode) === 'datetime' &&
  78. domain.operator !== '1'
  79. " value-format="YYYY-MM-DD" :disabled="!domain.operator" clearable />
  80. <!-- 日期范围选择 -->
  81. <div v-if="
  82. getConditionType(domain.attrCode) === 'datetime' &&
  83. domain.operator == '1'
  84. ">
  85. <el-date-picker v-model="domain.min" :disabled="!domain.operator" type="datetime"
  86. format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" time-format="HH:mm:ss"
  87. placeholder="选择日期时间" style="width: 170px">
  88. </el-date-picker>
  89. <el-date-picker v-model="domain.max" :disabled="!domain.operator" type="datetime"
  90. format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" time-format="HH:mm:ss"
  91. placeholder="选择日期时间" style="width: 170px">
  92. </el-date-picker>
  93. </div>
  94. <!-- 车牌级联选择 -->
  95. <el-cascader :options="getOptions(domain.attrCode)" :props="cascaderProps" v-model="domain.max"
  96. placeholder="选择车牌" clearable v-if="getConditionType(domain.attrCode) === 'plate'" style="width: 341px"
  97. :disabled="!domain.operator" />
  98. <!-- 4. 删除行按钮 -->
  99. <el-button type="text" :icon="Delete" size="small" style="color: #333333"
  100. @click="handleRemoveRow(groupIndex, rowIndex)" v-if="group.actions.length > 1" />
  101. </div>
  102. </el-form-item>
  103. <div class="flex justify-between items-center">
  104. <!-- 组内添加并且条件按钮 -->
  105. <el-button type="text" @click="handleAddRow(groupIndex)" class="add-condition-btn">
  106. + 并且条件
  107. </el-button>
  108. <el-button v-if="groupIndex !== 0" type="text"
  109. @click="formData.conditionGroups = formData.conditionGroups.filter(v => v.groupKey !== group.groupKey)"
  110. class="add-condition-btn">
  111. 删除
  112. </el-button>
  113. </div>
  114. </div>
  115. </div>
  116. <!-- 添加或者条件组按钮 -->
  117. <el-form-item class="add-group-btn">
  118. <el-button type="primary" @click="handleAddGroup">
  119. + 或者条件
  120. </el-button>
  121. </el-form-item>
  122. </el-form>
  123. <!-- 执行动作区域 -->
  124. <!-- 执行动作区域 -->
  125. <div class="item-title"><span></span> 将执行以下动作</div>
  126. <el-form :model="formData" :rules="formRules" ref="formRef" class="action-form">
  127. <!-- 指定报价协议 -->
  128. <el-form-item prop="formdata.actionJson[0].agreementId" class="form-item">
  129. <span>指定报价协议:</span>
  130. <el-select v-model="formData.actionJson[0].agreementId" placeholder="选择报价协议" style="width: 240px" clearable>
  131. <el-option v-for="item in agreementList" :key="item.id" :label="item.agreementName" :value="item.id" />
  132. </el-select>
  133. </el-form-item>
  134. <!-- 对接人变更方式选择 -->
  135. <el-form-item>
  136. <el-checkbox-group v-model="linkTypeList" @change="handleTypeChange">
  137. <!-- 手动输入对接人 -->
  138. <el-checkbox value="manual_input" size="large" style="margin: 20px 0; display: block">
  139. 手动输入对接人信息:
  140. <el-form-item prop="actionJson.name" style="display: inline-block; margin: 0 20px">
  141. <el-input v-model="formData.actionJson[1].name" style="width: 327px" placeholder="变更后对接人姓名"
  142. :disabled="!linkTypeList.includes('manual_input')" clearable />
  143. </el-form-item>
  144. <el-form-item prop="actionJson.phone" style="display: inline-block">
  145. <el-input v-model="formData.actionJson[1].phone" style="width: 327px" type="number"
  146. placeholder="变更后对接人手机号" maxlength="11" @input="handlePhoneInput"
  147. :disabled="!linkTypeList.includes('manual_input')" clearable />
  148. </el-form-item>
  149. </el-checkbox>
  150. <!-- 选择保司账号 -->
  151. <el-checkbox value="select_account" size="large" style="margin: 20px 0; display: block">
  152. 选择已有保司账号:
  153. <el-form-item prop="actionJson.accountId" style="display: inline-block; margin-left: 20px">
  154. <el-select v-model="formData.actionJson[2].accountId" placeholder="选择变更后的保司账号" style="width: 662px"
  155. :disabled="!linkTypeList.includes('select_account')" clearable>
  156. <!-- -->
  157. <el-option v-for="item in accountList" :key="item.id" :label="item.username" :value="item.id" />
  158. </el-select>
  159. </el-form-item>
  160. </el-checkbox>
  161. </el-checkbox-group>
  162. </el-form-item>
  163. </el-form>
  164. <!-- 抽屉底部按钮 -->
  165. <template #footer>
  166. <div class="foot">
  167. <el-button @click="handleClose"> 取消 </el-button>
  168. <el-button type="primary" @click="submit"> 保存 </el-button>
  169. </div>
  170. </template>
  171. </el-drawer>
  172. </div>
  173. </template>
  174. <script lang="ts" setup>
  175. import { reactive, ref, type Ref, onMounted } from "vue";
  176. import type { FormInstance, FormRules } from "element-plus";
  177. import { Delete } from "@element-plus/icons-vue";
  178. import { ElMessage } from "element-plus";
  179. import api from "@/api";
  180. import { getDictItems } from "@/api/dict";
  181. // 抽屉状态
  182. const drawer = ref(true);
  183. const emit = defineEmits(["close"]);
  184. // 组件props
  185. const props = defineProps({
  186. companyId: {
  187. type: [String, Number],
  188. required: true,
  189. validator: (value: string | number) => value !== "" && value !== 0,
  190. },
  191. editData: {},
  192. editType: "",
  193. drawerTitle: {
  194. type: String,
  195. default: "规则配置",
  196. },
  197. });
  198. // 表单实例引用
  199. const formRef: Ref<FormInstance | undefined> = ref(undefined);
  200. // 级联选择器配置
  201. const cascaderProps = { multiple: true, checkStrictly: true };
  202. // 数据类型定义
  203. interface ConditionRow {
  204. key: number;
  205. min: any;
  206. attrType: string;
  207. operator: string;
  208. attrCode: string;
  209. max: any;
  210. errorMsg: string;
  211. checkAll: boolean;
  212. indeterminate: boolean;
  213. minArray?: string[];
  214. }
  215. interface ConditionGroup {
  216. groupKey: number;
  217. actions: ConditionRow[];
  218. }
  219. const linkTypeList = ref([])
  220. // 表单核心数据
  221. // 表单核心数据 - 优化actionJson结构
  222. const formData = ref<{
  223. conditionGroups: ConditionGroup[];
  224. actionJson: [
  225. {
  226. actionCode: "specify_agreement"; // 固定:指定报价协议
  227. agreementId: string; // 报价协议ID
  228. },
  229. {
  230. actionCode: "change_bs_account"; // 固定:变更对接人
  231. linkType: "manual_input" | "select_account"; // 对接人方式:手动输入/选择账号
  232. name: string; // 手动输入-姓名
  233. phone: string; // 手动输入-手机号
  234. accountId: string; // 选择账号-账号ID
  235. },
  236. {
  237. actionCode: "change_bs_account"; // 固定:变更对接人
  238. linkType: "manual_input" | "select_account"; // 对接人方式:手动输入/选择账号
  239. name: string; // 手动输入-姓名
  240. phone: string; // 手动输入-手机号
  241. accountId: string; // 选择账号-账号ID
  242. }
  243. ];
  244. }>({
  245. actionJson: [
  246. {
  247. actionCode: "specify_agreement",
  248. agreementId: "", // 初始为空
  249. },
  250. {
  251. actionCode: "change_bs_account",
  252. linkType: "manual_input", // 默认选中“手动输入”
  253. name: "",
  254. phone: "",
  255. accountId: "",
  256. },
  257. {
  258. actionCode: "change_bs_account",
  259. linkType: "select_account", // 默认选中“手动输入”
  260. name: "",
  261. phone: "",
  262. accountId: "",
  263. },
  264. ],
  265. conditionGroups: [
  266. {
  267. groupKey: Date.now(),
  268. actions: [
  269. {
  270. attrType: "",
  271. attrCode: "",
  272. key: Date.now(),
  273. min: "",
  274. operator: "",
  275. max: "",
  276. errorMsg: "",
  277. minArray: [],
  278. },
  279. ],
  280. },
  281. ],
  282. });
  283. const selectprops = {
  284. value: 'id',
  285. label: 'name',
  286. }
  287. // 下拉选项数据源
  288. const dispatchList = ref([]);
  289. const agreementList = ref([]);
  290. const accountList = ref([]);
  291. const dictionaries = ref([]);
  292. const dispatchId = ref(""); //调度id
  293. const editType = ref("");
  294. const handleCheckAll = (item: any, val: boolean) => {
  295. item.indeterminate = false
  296. if (val) {
  297. item.minArray = getOptions(item.attrCode).map((item: any) => item.idStr)
  298. } else {
  299. item.minArray = []
  300. }
  301. }
  302. const change = (item: any) => {
  303. console.log('change', item)
  304. if (item?.attrType == 'select') {
  305. if (item.minArray.length === 0) {
  306. item.checkAll = false
  307. item.indeterminate = false
  308. } else if (item.minArray.length === getOptions(item.attrCode).length) {
  309. item.checkAll = true
  310. item.indeterminate = false
  311. } else {
  312. item.indeterminate = true
  313. }
  314. }
  315. }
  316. const validateAgreementId = (rule: any, value: string, callback: any) => {
  317. if (!value) {
  318. return callback(new Error("请选择需要指定的报价协议"));
  319. }
  320. callback();
  321. };
  322. const validateLinkManName = (rule: any, value: string, callback: any) => {
  323. const linkType = formData.value.actionJson[1].linkType;
  324. if (linkType === "manual_input" && !value?.trim()) {
  325. return callback(new Error("请输入对接人姓名(手动输入模式)"));
  326. }
  327. callback();
  328. };
  329. const validateLinkManPhone = (rule: any, value: string, callback: any) => {
  330. const linkType = formData.value.actionJson[1].linkType;
  331. if (linkType !== "manual_input") {
  332. return callback();
  333. }
  334. if (!value) {
  335. return callback(new Error("请输入对接人手机号(手动输入模式)"));
  336. }
  337. if (!/^1[3-9]\d{9}$/.test(value)) {
  338. return callback(
  339. new Error("手机号格式错误,请输入11位有效号码(如13800138000)")
  340. );
  341. }
  342. callback();
  343. };
  344. const validateAccountId = (rule: any, value: string, callback: any) => {
  345. const linkType = formData.value.actionJson[1].linkType;
  346. if (linkType === "select_account" && !value) {
  347. return callback(new Error("请选择变更后的保司账号(选择账号模式)"));
  348. }
  349. callback();
  350. };
  351. // 优化后的formRules
  352. const formRules = reactive<FormRules>({
  353. // 1. 条件组校验(核心)
  354. conditionGroups: [
  355. {
  356. required: true,
  357. trigger: ["submit", "blur"], // 移除频繁的change触发,仅在提交和失焦时校验
  358. // validator: validateConditionGroups,
  359. },
  360. ],
  361. // 2. 执行动作:指定报价协议(必选)
  362. "actionJson[0].agreementId": [
  363. {
  364. required: true,
  365. trigger: ["submit", "blur"], // 下拉选择无需change触发,失焦+提交足够
  366. validator: validateAgreementId,
  367. },
  368. ],
  369. // 3. 执行动作:对接人姓名(仅手动输入时必选)
  370. "actionJson[1].name": [
  371. {
  372. trigger: ["submit", "blur"],
  373. validator: validateLinkManName,
  374. },
  375. ],
  376. // 4. 执行动作:对接人手机号(仅手动输入时必选+格式校验)
  377. "actionJson[1].phone": [
  378. {
  379. trigger: ["submit", "blur"], // 移除input触发,避免输入过程中频繁报错
  380. validator: validateLinkManPhone,
  381. },
  382. ],
  383. // 5. 执行动作:保司账号(仅选择账号时必选)
  384. "actionJson[1].accountId": [
  385. {
  386. trigger: ["submit", "blur"],
  387. validator: validateAccountId,
  388. },
  389. ],
  390. });
  391. // 工具函数:根据条件类型获取对应的运算符列表
  392. const getOperations = (conditionValue: string) => {
  393. const target = dispatchList.value.find(
  394. (item) => item.attrCode === conditionValue
  395. );
  396. return target?.rulesDict || [];
  397. };
  398. // 工具函数:根据条件类型获取对应的选项列表
  399. const getOptions = (conditionValue: string) => {
  400. const target = dispatchList.value.find(
  401. (item) => item.attrCode === conditionValue
  402. );
  403. let arr=target?.dictLabal || []
  404. if(conditionValue==='InsruanceProduct') arr=arr.map(v=>({...v,idStr:v.code}))
  405. return target?.dictLabal || [];
  406. };
  407. // 工具函数:根据条件类型获取输入组件类型
  408. const getConditionType = (conditionValue: string) => {
  409. const target = dispatchList.value.find(
  410. (item) => item.attrCode === conditionValue
  411. );
  412. return target?.attrType || "input";
  413. };
  414. // 条件切换时的处理
  415. const handleConditionChange = (domain: ConditionRow, val: string) => {
  416. const target = dispatchList.value.find((item: any) => item.attrCode == val)
  417. domain.max = null;
  418. domain.operator = "";
  419. domain.checkAll = false;
  420. domain.indeterminate = false
  421. domain.attrType = target?.attrType || "input";
  422. };
  423. // 运算符切换时重置值
  424. const handleOperatorChange = (domain: ConditionRow) => {
  425. const attrType = getConditionType(domain.attrCode);
  426. domain.checkAll = false;
  427. domain.indeterminate = false
  428. // 重置对应类型的值
  429. if (["checkbox", "inputTag", "select"].includes(attrType)) {
  430. domain.minArray = [];
  431. } else {
  432. domain.min = null;
  433. domain.max = null;
  434. }
  435. };
  436. // 组内添加并且条件行
  437. const handleAddRow = (groupIndex: number) => {
  438. formData.value.conditionGroups[groupIndex].actions.push({
  439. key: Date.now(),
  440. min: "",
  441. attrType: "",
  442. attrCode: "",
  443. operator: "",
  444. max: null,
  445. errorMsg: "",
  446. minArray: [],
  447. });
  448. };
  449. // 删除组内条件行
  450. const handleRemoveRow = (groupIndex: number, rowIndex: number) => {
  451. formData.value.conditionGroups[groupIndex].actions.splice(rowIndex, 1);
  452. };
  453. // 添加新的或者条件组
  454. const handleAddGroup = () => {
  455. formData.value.conditionGroups.push({
  456. groupKey: Date.now(),
  457. actions: [
  458. {
  459. key: Date.now(),
  460. min: "",
  461. attrType: "",
  462. attrCode: "",
  463. operator: "",
  464. max: null,
  465. errorMsg: "",
  466. minArray: [],
  467. },
  468. ],
  469. });
  470. };
  471. // 手机号输入处理
  472. const handlePhoneInput = () => {
  473. const phone = formData.value.actionJson[1].phone;
  474. if (phone) {
  475. // 截取前11位,去除非数字字符
  476. const filtered = phone.replace(/\D/g, "").slice(0, 11);
  477. formData.value.actionJson[1].phone = filtered;
  478. }
  479. };
  480. // 获取条件类型列表
  481. const getList = async () => {
  482. try {
  483. const res = await api.dispatchApi.listByParam({
  484. scopeApplication: 'PROTOCOL_SCHEDULING',
  485. companyId: props.companyId
  486. });
  487. dispatchList.value = res.data;
  488. } catch (error) {
  489. console.error("获取条件类型列表失败:", error);
  490. }
  491. };
  492. // 获取报价协议列表
  493. const getByCompanyId = async () => {
  494. try {
  495. const res = await api.dispatchApi.getByCompanyIdt(props.companyId);
  496. agreementList.value = res.data;
  497. } catch (error) {
  498. console.error("获取报价协议列表失败:", error);
  499. }
  500. };
  501. // 获取保司账号列表
  502. const getAccountList = async () => {
  503. try {
  504. const res = await api.dispatchApi.getAgreementAttributionByCompanyId(
  505. props.companyId
  506. );
  507. accountList.value = res.data;
  508. } catch (error) {
  509. console.error("获取保司账号列表失败:", error);
  510. }
  511. };
  512. //清空选项数据
  513. const handleTypeChange = (value) => {
  514. if (!value.includes("manual_input")) {
  515. formData.value.actionJson[1].name = "";
  516. formData.value.actionJson[1].phone = "";
  517. }
  518. if (!value.includes("select_account")) {
  519. formData.value.actionJson[2].accountId = "";
  520. }
  521. };
  522. // 提交表单
  523. const submit = async () => {
  524. const { editType } = props;
  525. // if (!formRef.value) return;
  526. // await formRef.value.validate();
  527. const actionJson = formData.value.actionJson;
  528. // const actions =formData.value.actionJson
  529. let actions = [
  530. {
  531. actionCode: actionJson[0].actionCode,
  532. actionJson: {
  533. agreementId: actionJson[0].agreementId,
  534. },
  535. },
  536. ];
  537. if (linkTypeList.value.includes('manual_input')) {
  538. const obj = {
  539. actionCode:
  540. actionJson[1].linkType == "manual_input"
  541. ? "change_poc"
  542. : "change_bs_account",
  543. actionJson: {
  544. ...(actionJson[1].linkType === "manual_input"
  545. ? { name: actionJson[1].name, phone: actionJson[1].phone }
  546. : { accountId: actionJson[1].accountId }),
  547. },
  548. }
  549. actions.push(obj)
  550. }
  551. if (linkTypeList.value.includes('select_account')) {
  552. const obj = {
  553. actionCode:
  554. actionJson[2].linkType == "manual_input"
  555. ? "change_poc"
  556. : "change_bs_account",
  557. actionJson: {
  558. ...(actionJson[2].linkType === "manual_input"
  559. ? { name: actionJson[2].name, phone: actionJson[2].phone }
  560. : { accountId: actionJson[2].accountId }),
  561. },
  562. }
  563. actions.push(obj)
  564. }
  565. if (!actionJson[0].agreementId) {
  566. ElMessage({
  567. message: '请选择指定报价协议!',
  568. type: 'error'
  569. })
  570. return
  571. }
  572. // 处理多选框额外传值
  573. const allActions = formData.value.conditionGroups.flatMap((group) => group.actions);
  574. allActions.map((item) => {
  575. if (["checkbox", "inputTag", "select"].includes(item.attrType)) {
  576. item.min = item.minArray.join(',');
  577. }
  578. return item
  579. })
  580. const submitData = {
  581. companyId: props.companyId + "",
  582. attrLinks: allActions,
  583. actions,
  584. ...(dispatchId.value && { dispatchId: dispatchId.value }), // 编辑时携带ID
  585. };
  586. const apiFunc = editType === "edit"
  587. ? api.dispatchApi.updateAgreementDispatch
  588. : api.dispatchApi.addAgreementDispatch;
  589. // const apiFunc = api.dispatchApi.addAgreementDispatch;
  590. const res = await apiFunc(submitData);
  591. if (res.code === 200) {
  592. ElMessage.success("保存成功");
  593. handleClose();
  594. } else {
  595. ElMessage.error("保存失败");
  596. }
  597. // try {
  598. // } catch (error) {
  599. // ElMessage.error("表单校验失败,请检查填写内容");
  600. // }
  601. };
  602. // 关闭抽屉
  603. const handleClose = () => {
  604. emit("close", false);
  605. };
  606. // 组件挂载时初始化数据
  607. // onMounted(async () => {
  608. // await getList();
  609. // await getByCompanyId();
  610. // await getAccountList();
  611. // dictionaries.value = await getDictItems({ dictCode: "dispatch_action" });
  612. // console.log(dictionaries.value);
  613. // formData.value = props.editData;
  614. // });
  615. onMounted(async () => {
  616. // 1. 加载基础数据(条件类型、协议列表等)
  617. await Promise.all([
  618. getList(), // 条件类型列表
  619. getByCompanyId(), // 报价协议列表
  620. getAccountList(), // 保司账号列表
  621. ]);
  622. regionLicenseNumber('0'),//车牌地区
  623. regionLicenseNumber('14')//车牌号
  624. dictionaries.value = await getDictItems({ dictCode: "dispatch_action" });
  625. // 2. 提取props参数
  626. const { editType, editData } = props;
  627. console.log('editData', editData)
  628. dispatchId.value = editData?.dispatchId || "";
  629. // 3. 初始化formData基础结构(固定保留actionJson默认值,仅动态处理conditionGroups)
  630. const baseFormData = {
  631. // actionJson保持初始默认结构(新增/编辑通用基础配置)
  632. actionJson: [
  633. {
  634. actionCode: "specify_agreement",
  635. agreementId: "", // 新增时为空,编辑时后续单独处理
  636. },
  637. {
  638. actionCode: "change_bs_account",
  639. linkType: "manual_input" as "manual_input" | "select_account",
  640. name: "",
  641. phone: "",
  642. accountId: "",
  643. },
  644. {
  645. actionCode: "change_bs_account",
  646. linkType: "select_account" as "manual_input" | "select_account",
  647. name: "",
  648. phone: "",
  649. accountId: "",
  650. },
  651. ],
  652. // 条件组:根据editType动态赋值
  653. conditionGroups: [] as ConditionGroup[],
  654. };
  655. let isDefaultScene = false;
  656. // 4. 新增场景:仅初始化conditionGroups为默认一组条件
  657. if (!editType || editType === "add") {
  658. isDefaultScene = true;
  659. console.log("[初始化] 进入场景:无editType或add"); // 新增日志
  660. // 提取原始条件组(兼容editData可能的格式)
  661. const rawGroups = editData?.groupedAttrLinks
  662. ? editData.groupedAttrLinks
  663. : editData?.attrLinks
  664. ? [editData.attrLinks]
  665. : [];
  666. console.log("[初始化] 提取到的rawGroups:", rawGroups, "长度:", rawGroups.length); // 新增日志
  667. // 转换rawGroups为conditionGroups
  668. baseFormData.conditionGroups = rawGroups.map((rawGroup: any[], groupIdx: number) => {
  669. const group: ConditionGroup = {
  670. groupKey: editData?.dispatchId || `group_${groupIdx}_${Date.now()}`,
  671. actions: [],
  672. };
  673. // 处理组内条件行
  674. if (rawGroup.length > 0) {
  675. group.actions = rawGroup.map((rawRow: any, rowIdx: number) => ({
  676. key: rawRow.id || `row_${groupIdx}_${rowIdx}_${Date.now()}`,
  677. attrType: rawRow.attrType || "",
  678. attrCode: rawRow.attrCode || "",
  679. operator: rawRow.operator || "",
  680. min: rawRow.min !== undefined ? rawRow.min : null,
  681. max: rawRow.max !== undefined ? rawRow.max : null,
  682. minArray: rawRow.minArray ? [...rawRow.minArray] : [],
  683. errorMsg: "",
  684. }));
  685. } else {
  686. // 组内无数据时,兜底1行
  687. group.actions.push({
  688. key: Date.now(),
  689. attrType: "",
  690. attrCode: "",
  691. operator: "",
  692. min: null,
  693. max: null,
  694. errorMsg: "",
  695. minArray: [],
  696. });
  697. }
  698. return group;
  699. });
  700. // 第一次兜底:如果转换后还是空,直接加1组
  701. if (baseFormData.conditionGroups.length === 0) {
  702. console.log("[初始化] rawGroups为空,第一次兜底添加1组条件"); // 新增日志
  703. baseFormData.conditionGroups.push({
  704. groupKey: Date.now(),
  705. actions: [
  706. {
  707. key: Date.now(),
  708. attrType: "",
  709. attrCode: "",
  710. operator: "",
  711. min: null,
  712. max: null,
  713. errorMsg: "",
  714. minArray: [],
  715. },
  716. ],
  717. });
  718. }
  719. }
  720. // 5. 编辑场景:从editData解析conditionGroups(保持之前的编辑逻辑)
  721. else if (
  722. editType === "edit" &&
  723. editData &&
  724. Object.keys(editData).length > 0
  725. ) {
  726. const rawGroups = editData.groupedAttrLinks
  727. ? editData.groupedAttrLinks
  728. : editData.attrLinks
  729. ? [editData.attrLinks]
  730. : [];
  731. baseFormData.conditionGroups = rawGroups.map(
  732. (rawGroup: any[], groupIdx: number) => {
  733. const group: ConditionGroup = {
  734. groupKey: editData.dispatchId || `group_${groupIdx}_${Date.now()}`,
  735. actions: [],
  736. };
  737. rawGroup.forEach((rawRow: any, rowIdx: number) => {
  738. console.log('rawRow', rawRow)
  739. group.actions.push({
  740. key: rawRow.id || `row_${groupIdx}_${rowIdx}_${Date.now()}`,
  741. attrType: rawRow.attrType || "",
  742. attrCode: rawRow.attrCode || "",
  743. operator: rawRow.operator || "",
  744. min: rawRow.min !== undefined ? rawRow.min : null,
  745. max: rawRow.max !== undefined ? rawRow.max : null,
  746. minArray: rawRow.minArray ? [...rawRow.minArray] : [],
  747. errorMsg: "",
  748. });
  749. });
  750. // 兜底:确保每组至少有一行条件
  751. if (group.actions.length === 0) {
  752. group.actions.push({
  753. key: Date.now(),
  754. attrType: "",
  755. attrCode: "",
  756. operator: "",
  757. min: null,
  758. max: null,
  759. errorMsg: "",
  760. minArray: [],
  761. });
  762. }
  763. console.log('group-------------', group)
  764. return group;
  765. }
  766. );
  767. // 编辑场景补充:回显actionJson(报价协议和对接人信息)
  768. if (editData.actions && editData.actions.length > 0) {
  769. // 回显报价协议
  770. const agreementAction = editData.actions.find(
  771. (action: any) => action.actionCode === "specify_agreement"
  772. );
  773. if (agreementAction?.actionJson?.agreementId) {
  774. baseFormData.actionJson[0].agreementId =
  775. agreementAction.actionJson.agreementId;
  776. }
  777. // 回显对接人信息
  778. const linkAction = editData.actions.find(
  779. (action: any) =>
  780. action.actionCode === "change_poc"
  781. );
  782. const linkAction1 = editData.actions.find(
  783. (action: any) =>
  784. action.actionCode === "change_bs_account"
  785. );
  786. if (linkAction?.actionJson) {
  787. const linkJson = linkAction.actionJson;
  788. if (linkJson.accountId) {
  789. baseFormData.actionJson[1].linkType = "select_account";
  790. baseFormData.actionJson[1].accountId = linkJson.accountId;
  791. } else {
  792. linkTypeList.value.push('manual_input')
  793. baseFormData.actionJson[1].linkType = "manual_input";
  794. baseFormData.actionJson[1].name = linkJson.name || "";
  795. baseFormData.actionJson[1].phone = linkJson.phone || "";
  796. }
  797. }
  798. if (linkAction1?.actionJson) {
  799. const linkJson = linkAction1.actionJson;
  800. if (linkJson.accountId) {
  801. linkTypeList.value.push('select_account')
  802. baseFormData.actionJson[2].linkType = "select_account";
  803. baseFormData.actionJson[2].accountId = linkJson.accountId;
  804. }
  805. }
  806. }
  807. if (baseFormData.conditionGroups.length === 0) {
  808. baseFormData.conditionGroups = [
  809. {
  810. groupKey: Date.now(),
  811. actions: [
  812. {
  813. key: Date.now(),
  814. attrType: "",
  815. attrCode: "",
  816. operator: "",
  817. min: null,
  818. max: null,
  819. errorMsg: "",
  820. minArray: [],
  821. },
  822. ],
  823. },
  824. ];
  825. }
  826. }
  827. // 6. 赋值给formData(确保响应式生效)
  828. formData.value = baseFormData;
  829. console.log(
  830. `[${editType}] 场景初始化完成,conditionGroups长度:`,
  831. formData.value.conditionGroups.length
  832. );
  833. });
  834. const regionLicenseNumber = (value: string) => {
  835. api.agreementApi.getAreaLicense(value).then((res: any) => {
  836. if (res.code == '200') {
  837. let data: any = [];
  838. res.data.map((val: any) => {
  839. data.push({
  840. idStr: val.license,
  841. name: val.license,
  842. })
  843. return val;
  844. })
  845. if (value == '14') {
  846. const licenseNoItem = dispatchList.value.find((item: any) => item.attrCode == 'LicenseNo');
  847. if (licenseNoItem) {
  848. licenseNoItem.dictLabal = data;
  849. }
  850. }
  851. if (value == '0') {
  852. const licenseAreaItem = dispatchList.value.find((item: any) => item.attrCode == 'LicenseArea');
  853. if (licenseAreaItem) {
  854. licenseAreaItem.dictLabal = data;
  855. }
  856. }
  857. } else {
  858. }
  859. });
  860. }
  861. </script>
  862. <style scoped lang="scss">
  863. .condition-container {
  864. margin-bottom: 16px;
  865. }
  866. .condition-group {
  867. width: 100%;
  868. padding: 24px;
  869. margin-bottom: 20px;
  870. border: 1px solid #eee;
  871. border-radius: 6px;
  872. box-sizing: border-box;
  873. background-color: #fff;
  874. position: relative;
  875. }
  876. .group-header {
  877. display: flex;
  878. justify-content: space-between;
  879. align-items: center;
  880. margin-bottom: 16px;
  881. padding-bottom: 8px;
  882. border-bottom: 1px solid #f0f0f0;
  883. }
  884. .condition-row {
  885. display: flex;
  886. align-items: center;
  887. gap: 8px;
  888. flex-wrap: wrap;
  889. }
  890. .add-condition-btn {
  891. margin-top: 8px;
  892. text-align: left;
  893. }
  894. .add-group-btn {
  895. text-align: left;
  896. }
  897. .item-title {
  898. display: flex;
  899. align-items: center;
  900. margin-bottom: 20px;
  901. span {
  902. display: inline-block;
  903. width: 4px;
  904. height: 18px;
  905. background: #0083ff;
  906. border-radius: 10px 10px 10px 10px;
  907. margin-right: 8px;
  908. }
  909. }
  910. .form-item {
  911. display: flex;
  912. align-items: center;
  913. span {
  914. font-size: 14px;
  915. margin-right: 10px;
  916. }
  917. }
  918. .foot {
  919. display: flex;
  920. justify-content: center;
  921. align-items: center;
  922. gap: 12px;
  923. padding-top: 10px;
  924. border-top: 1px solid #eee;
  925. }
  926. :deep(.main-container) {
  927. height: 100%;
  928. }
  929. :deep(.el-row) {
  930. height: 100%;
  931. }
  932. :deep(.el-drawer__header) {
  933. height: 48px;
  934. font-size: 20px;
  935. color: #333333;
  936. border-bottom: 1px solid #eeeeee;
  937. margin-bottom: 24px;
  938. padding-bottom: 20px;
  939. }
  940. :deep(.el-drawer__body) {
  941. padding: 0 24px;
  942. }
  943. </style>