index.vue 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134
  1. <template>
  2. <div>
  3. <van-form
  4. @submit="onSubmit"
  5. :disabled="formOption.disabled"
  6. :readonly="formOption.readonly"
  7. :label-width="formOption.labelWidth"
  8. :label-align="formOption.labelAlign || 'top'"
  9. :scroll-to-error="formOption.scroll"
  10. ref="testForm"
  11. >
  12. <van-cell-group inset>
  13. <div
  14. v-for="(i, index) in formConfig"
  15. :key="index"
  16. :style="i.style || ''"
  17. >
  18. <van-field v-if="i.type == 'title'" style="background: #ecebeb">
  19. <template #input>
  20. <div class="_title">
  21. {{ i.title }}
  22. </div>
  23. </template>
  24. </van-field>
  25. <van-field
  26. v-if="i.type == 'input'"
  27. v-model="formData[i.prop]"
  28. :label="i.label"
  29. :name="i.prop"
  30. :type="i.itemType ? i.itemType : 'text'"
  31. :placeholder="i.placeholder ? i.placeholder : '请输入'"
  32. :clearable="i.clearable ? i.clearable : false"
  33. :readonly="getFieldReadonly(i)"
  34. :rules="getRules(i.prop)"
  35. :required="getRequired(i.prop)"
  36. :right-icon="i.isNeedRightBtn ? i.rightIcon : ''"
  37. @click-right-icon="i.isNeedRightBtn ? i.rightIconClick() : () => {}"
  38. @blur="
  39. i.isNeedBlurMethon ? i.blurMethon(formData[i.prop]) : () => {}
  40. "
  41. @input="
  42. () => {
  43. return i.inputFn ? i.inputFn(formData[i.prop]) : () => {};
  44. }
  45. "
  46. >
  47. </van-field>
  48. <!-- switch -->
  49. <van-field
  50. v-if="i.type == 'switch'"
  51. :label="i.label"
  52. :name="i.prop"
  53. :required="getRequired(i.prop)"
  54. >
  55. <template #input>
  56. <van-switch v-model="formData[i.prop]" />
  57. </template>
  58. </van-field>
  59. <!-- 多选checkbox -->
  60. <van-field
  61. v-if="i.type == 'checkbox'"
  62. :label="i.label"
  63. :name="i.prop"
  64. :rules="getRules(i.prop)"
  65. :required="getRequired(i.prop)"
  66. >
  67. <template #input>
  68. <van-checkbox-group
  69. v-model="formData[i.prop]"
  70. direction="horizontal"
  71. >
  72. <van-checkbox
  73. shape="square"
  74. v-for="j in i.data"
  75. :key="j.value"
  76. :name="j.value"
  77. >{{ j.text }}</van-checkbox
  78. >
  79. </van-checkbox-group>
  80. </template>
  81. </van-field>
  82. <!-- 单选radio -->
  83. <van-field
  84. v-if="i.type == 'radio'"
  85. :label="i.label"
  86. :name="i.prop"
  87. :rules="getRules(i.prop)"
  88. :required="getRequired(i.prop)"
  89. >
  90. <template #input>
  91. <van-radio-group
  92. v-model="formData[i.prop]"
  93. direction="horizontal"
  94. >
  95. <van-radio
  96. v-for="j in i.data"
  97. :key="j.value"
  98. :name="j.value || j.id"
  99. >{{ j.label || j.title }}</van-radio
  100. >
  101. </van-radio-group>
  102. </template>
  103. </van-field>
  104. <!-- 单选 -->
  105. <van-field
  106. v-if="i.type == 'picker' && i.itemType == 'onePicker'"
  107. v-show="!i.showStatus"
  108. :label="i.label"
  109. :name="i.prop"
  110. v-model="formData[i.prop + 'Name']"
  111. is-link
  112. :readonly="true"
  113. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  114. @click="
  115. () =>
  116. formOption.readonly || i.readonly ? '' : (i.showPicker = true)
  117. "
  118. :rules="getRules(i.prop)"
  119. :required="getRequired(i.prop)"
  120. >
  121. </van-field>
  122. <van-popup
  123. v-model:show="i.showPicker"
  124. round
  125. position="bottom"
  126. v-if="i.type == 'picker' && i.itemType == 'onePicker'"
  127. >
  128. <van-picker
  129. :columns="i.data"
  130. :columns-field-names="
  131. i.fieldNames ? i.fieldNames : onePickerFieldNames
  132. "
  133. @cancel="i.showPicker = false"
  134. @confirm="
  135. (option) =>
  136. i.changeFn
  137. ? i.changeFn(option, i, index)
  138. : onConfirmPicker(option, i, index)
  139. "
  140. />
  141. </van-popup>
  142. <!-- 多选 -->
  143. <van-field
  144. v-if="i.type == 'multipleChoice' && i.itemType == 'multiple'"
  145. v-show="!i.showStatus"
  146. :label="i.label"
  147. :name="i.prop"
  148. v-model="formData[i.prop + 'Name']"
  149. is-link
  150. :readonly="true"
  151. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  152. @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
  153. :rules="getRules(i.prop)"
  154. :required="getRequired(i.prop)"
  155. >
  156. </van-field>
  157. <van-popup
  158. v-model:show="i.showPicker"
  159. round
  160. position="bottom"
  161. :style="{ height: '40%' }"
  162. v-if="i.type == 'multipleChoice' && i.itemType == 'multiple'"
  163. >
  164. <van-checkbox-group
  165. v-model="formData[i.prop]"
  166. @change="
  167. changeCheckboxGroup(formData, i.prop, i.data, i.fieldNames)
  168. "
  169. class="multipleChoice"
  170. >
  171. <van-checkbox
  172. v-for="item in i.data"
  173. :key="item.value"
  174. :name="item.value"
  175. >{{ item.text }}</van-checkbox
  176. >
  177. </van-checkbox-group>
  178. </van-popup>
  179. <!-- 时间选择器 -->
  180. <van-field
  181. v-if="i.type == 'picker' && i.itemType == 'datePicker'"
  182. :label="i.label"
  183. :name="i.prop"
  184. v-model="formData[i.prop]"
  185. is-link
  186. :readonly="true"
  187. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  188. @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
  189. :rules="getRules(i.prop)"
  190. :required="getRequired(i.prop)"
  191. >
  192. </van-field>
  193. <van-popup
  194. v-model:show="i.showPicker"
  195. round
  196. position="bottom"
  197. v-if="i.type == 'picker' && i.itemType == 'datePicker'"
  198. >
  199. <van-date-picker
  200. @confirm="(option) => onConfirmPicker(option, i, index)"
  201. @cancel="i.showPicker = false"
  202. :min-date="i.minDate"
  203. :max-date="i.maxDate"
  204. :columns-type="i.columnsType"
  205. />
  206. </van-popup>
  207. <!-- 时间选择器 带时分秒 -->
  208. <van-field
  209. v-if="i.type == 'picker' && i.itemType == 'datePickerTime'"
  210. :label="i.label"
  211. :name="i.prop"
  212. v-model="formData[i.prop]"
  213. is-link
  214. :readonly="true"
  215. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  216. @click="() => (!formOption.readonly ? defaultTimeFn(i, index) : '')"
  217. :rules="getRules(i.prop)"
  218. :required="getRequired(i.prop)"
  219. >
  220. </van-field>
  221. <van-popup
  222. v-model:show="i.showPicker"
  223. round
  224. position="bottom"
  225. v-if="i.type == 'picker' && i.itemType == 'datePickerTime'"
  226. >
  227. <van-picker-group
  228. :tabs="['日期', '时间']"
  229. @confirm="() => datePickerTimeConfirm(i, index)"
  230. @cancel="i.showPicker = false"
  231. >
  232. <van-date-picker
  233. v-model="datePickerDateArr"
  234. :columns-type="i.columnsType"
  235. />
  236. <van-time-picker
  237. v-model="datePickerTimeArr"
  238. :columns-type="['hour', 'minute', 'second']"
  239. />
  240. </van-picker-group>
  241. </van-popup>
  242. <!-- 级联 城市 -->
  243. <van-field
  244. v-if="i.type == 'cascader' && i.itemType == 'city'"
  245. :label="i.label"
  246. :name="i.prop"
  247. v-model="formData[i.prop + 'Name']"
  248. is-link
  249. :readonly="true"
  250. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  251. @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
  252. :rules="getRules(i.prop)"
  253. :required="getRequired(i.prop)"
  254. />
  255. <van-popup
  256. v-if="i.type == 'cascader' && i.itemType == 'city'"
  257. v-model:show="i.showPicker"
  258. round
  259. position="bottom"
  260. >
  261. <van-cascader
  262. v-model="formData[i.prop]"
  263. :title="i.title ? i.title : '请选择'"
  264. :options="cityOption"
  265. @close="i.showPicker = false"
  266. @change="(option) => cityOnChange(option, i, index)"
  267. @finish="
  268. (option) =>
  269. i.finishFn ? i.finishFn(option) : () => (i.showPicker = false)
  270. "
  271. />
  272. </van-popup>
  273. <!-- 级联 公共 -->
  274. <van-field
  275. v-if="i.type == 'cascader' && i.itemType == 'common'"
  276. :label="i.label"
  277. :name="i.prop"
  278. v-model="formData[i.prop + 'Name']"
  279. is-link
  280. :readonly="true"
  281. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  282. @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
  283. :rules="getRules(i.prop)"
  284. :required="getRequired(i.prop)"
  285. />
  286. <van-popup
  287. v-if="i.type == 'cascader' && i.itemType == 'common'"
  288. v-model:show="i.showPicker"
  289. round
  290. position="bottom"
  291. >
  292. <van-cascader
  293. v-model="formData[i.prop]"
  294. :title="i.title ? i.title : '请选择'"
  295. :options="i.data"
  296. :field-names="i.fieldNames ? i.fieldNames : fieldNames"
  297. @close="i.showPicker = false"
  298. @change="(option) => commonOnChange(option, i, index)"
  299. @finish="
  300. (option) =>
  301. i.finishFn ? i.finishFn(i, option) : handleCommonFinish(index)
  302. "
  303. />
  304. </van-popup>
  305. <!-- 文件上传 -->
  306. <van-field
  307. name="uploader"
  308. v-if="i.type == 'upload'"
  309. :label="i.label"
  310. :readonly="i.readonly ? true : false"
  311. >
  312. <template #input>
  313. <van-uploader
  314. v-model="formData[i.prop]"
  315. :after-read="afterRead"
  316. multiple
  317. :max-count="9"
  318. :max-size="5 * 1024 * 1024"
  319. @oversize="onOversize"
  320. />
  321. </template>
  322. </van-field>
  323. <!-- 插槽 -->
  324. <van-field
  325. v-if="i.type == 'slot'"
  326. :label="i.label"
  327. :rules="getRules(i.prop)"
  328. :required="getRequired(i.prop)"
  329. >
  330. <template #input>
  331. <div style="width: 100%">
  332. <slot :name="i.slotName"> {{ i.slotName }}插槽占位符 </slot>
  333. </div>
  334. </template>
  335. </van-field>
  336. </div>
  337. </van-cell-group>
  338. <!-- 循环业务数据 -->
  339. <van-cell-group
  340. inset
  341. v-for="(item, index) in formData[btnConfigCopy.prop]"
  342. :key="index"
  343. >
  344. <div class="row">
  345. <div>{{ btnConfigCopy.listTitle || "明细" }}{{ index + 1 }}</div>
  346. <van-button
  347. plain
  348. type="primary"
  349. @click="handleRemove(index, btnConfigCopy)"
  350. size="mini"
  351. style="border: none; background: #ecebeb"
  352. v-if="
  353. formOption.btnConfig !== undefined && formOption.btnConfig.isNeed
  354. "
  355. >删除</van-button
  356. >
  357. </div>
  358. <!-- 循环表单数据 -->
  359. <div v-for="(i, sonIndex) in btnConfigCopy.listConfig" :key="i.prop">
  360. <van-field
  361. v-if="i.type == 'input'"
  362. v-model="formData[btnConfigCopy.prop][index][i.prop]"
  363. :label="i.label"
  364. :name="i.prop"
  365. :type="i.itemType ? i.itemType : 'text'"
  366. :placeholder="i.placeholder ? i.placeholder : '请输入'"
  367. :clearable="i.clearable ? i.clearable : false"
  368. :readonly="getFieldReadonly(i)"
  369. :rules="getRules(i.prop)"
  370. :required="getRequired(i.prop)"
  371. @change="
  372. (val) => {
  373. return i.changeFn ? i.changeFn(index, val) : () => {};
  374. }
  375. "
  376. >
  377. </van-field>
  378. <!-- 单选 -->
  379. <van-field
  380. v-if="i.type == 'picker' && i.itemType == 'onePicker'"
  381. :label="i.label"
  382. :name="i.prop"
  383. v-model="formData[btnConfigCopy.prop][index][i.prop + 'Name']"
  384. is-link
  385. :readonly="true"
  386. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  387. @click="handleListItemClick(i, index, sonIndex)"
  388. :rules="getRules(i.prop)"
  389. :required="getRequired(i.prop)"
  390. >
  391. <template #input v-if="i.isShowScanCode">
  392. <div style="display: flex; height: 24px">
  393. <div style="width: calc(100vw - 100px)">
  394. {{ formData[btnConfigCopy.prop][index][i.prop + "Name"] }}
  395. </div>
  396. <div style="width: 100px; float: right; margin-top: -20px">
  397. <van-button
  398. plain
  399. type="primary"
  400. @click.native.stop="i.scanCode(index)"
  401. size="mini"
  402. style="border: none"
  403. >扫码</van-button
  404. >
  405. </div>
  406. </div>
  407. </template>
  408. </van-field>
  409. <!-- 时间选择器 -->
  410. <van-field
  411. v-if="i.type == 'picker' && i.itemType == 'datePicker'"
  412. :label="i.label"
  413. :name="i.prop"
  414. v-model="formData[btnConfigCopy.prop][index][i.prop]"
  415. is-link
  416. :readonly="true"
  417. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  418. @click="handleListItemClick(i, index, sonIndex)"
  419. :rules="getRules(i.prop)"
  420. :required="getRequired(i.prop)"
  421. >
  422. </van-field>
  423. </div>
  424. </van-cell-group>
  425. <!-- 单独写个循环,保证弹窗唯一 -->
  426. <div v-for="(item, index) in btnConfigCopy.listConfig" :key="index">
  427. <van-popup
  428. v-model:show="item.showPicker"
  429. round
  430. position="bottom"
  431. v-if="item.type == 'picker' && item.itemType == 'onePicker'"
  432. >
  433. <van-picker
  434. :columns="item.data"
  435. :columns-field-names="
  436. item.fieldNames ? item.fieldNames : onePickerFieldNames
  437. "
  438. @cancel="item.showPicker = false"
  439. @confirm="
  440. (option) =>
  441. item.changeFn
  442. ? item.changeFn(option, item, currentIndex, currentSonIndex, btnConfigCopy.prop)
  443. : onConfirmListPicker(option, item)
  444. "
  445. />
  446. <!-- @confirm="(option) => onConfirmListPicker(option, item)" -->
  447. </van-popup>
  448. <van-popup
  449. v-model:show="item.showPicker"
  450. round
  451. position="bottom"
  452. v-if="item.type == 'picker' && item.itemType == 'datePicker'"
  453. >
  454. <van-date-picker
  455. @confirm="(option) => onConfirmListPicker(option, item)"
  456. @cancel="item.showPicker = false"
  457. :min-date="item.minDate"
  458. :max-date="item.maxDate"
  459. :columns-type="item.columnsType"
  460. />
  461. </van-popup>
  462. </div>
  463. <!-- 按钮 -->
  464. <div
  465. class="btn-box"
  466. v-if="formOption.btnConfig !== undefined && formOption.btnConfig.isNeed"
  467. >
  468. <van-button
  469. :plain="btnConfigCopy.plain ? btnConfigCopy.plain : false"
  470. :type="btnConfigCopy.itemType ? btnConfigCopy.itemType : 'primary'"
  471. :size="btnConfigCopy.size ? btnConfigCopy.size : 'small'"
  472. style="width: 100%; border: none"
  473. @click="handlePush()"
  474. >
  475. <template #icon>
  476. <van-icon
  477. :name="btnConfigCopy.icon ? btnConfigCopy.icon : 'plus'"
  478. :size="12"
  479. />
  480. </template>
  481. {{
  482. btnConfigCopy.btnName ? btnConfigCopy.btnName : "添加"
  483. }}</van-button
  484. >
  485. </div>
  486. <div style="margin: 16px" v-show="!formOption.hiddenSubmitBtn">
  487. <van-button round block type="primary" native-type="submit">
  488. {{ formOption.submitBtnText || "提交" }}
  489. </van-button>
  490. </div>
  491. <div style="margin: 16px" v-show="formOption.otherBtn">
  492. <van-button round block type="warning" @click="handleOtherBtnClick">
  493. {{ formOption.otherBtnText || "其他" }}
  494. </van-button>
  495. </div>
  496. </van-form>
  497. </div>
  498. </template>
  499. <script setup>
  500. import { showLoadingToast, closeToast, showNotify } from "vant";
  501. import { formatDate } from "@/utils/auth";
  502. import {
  503. ref,
  504. getCurrentInstance,
  505. onMounted,
  506. reactive,
  507. computed,
  508. toRefs,
  509. watch,
  510. defineExpose,
  511. } from "vue";
  512. const submitBtn = ref(null)
  513. const props = defineProps({
  514. modelValue: {
  515. type: Object,
  516. default: false,
  517. },
  518. formConfig: {
  519. type: Array,
  520. default: false,
  521. },
  522. formOption: {
  523. type: Object,
  524. default: false,
  525. },
  526. rules: {
  527. type: Object,
  528. default: false,
  529. },
  530. });
  531. const proxy = getCurrentInstance().proxy;
  532. const { formConfig, formOption, rules } = toRefs(props);
  533. const formData = computed(() => {
  534. return proxy.modelValue;
  535. });
  536. const cityOption = ref([]);
  537. const fileList = ref([]);
  538. const fieldNames = { text: "label", value: "id", children: "children" };
  539. const onePickerFieldNames = {
  540. text: "text",
  541. value: "value",
  542. };
  543. const emit = defineEmits(["update:modelValue"]);
  544. const onSubmit = () => {
  545. emit("onSubmit");
  546. };
  547. const handleOtherBtnClick = () => {
  548. emit("otherBtnClick");
  549. };
  550. // 获取验证规则
  551. const getRules = (prop) => {
  552. if (rules.value.hasOwnProperty(prop) && rules.value[prop]) {
  553. return rules.value[prop];
  554. }
  555. };
  556. const getRequired = (prop) => {
  557. if (rules.value.hasOwnProperty(prop) && rules.value[prop]) {
  558. return true;
  559. }
  560. return false;
  561. };
  562. const getReadonly = (i) => {
  563. return i.readonly ? i.readonly : i.name == "picker" ? true : false;
  564. };
  565. const getFieldReadonly = (i) => {
  566. if (i.readonly && i.readonly === true) {
  567. return true;
  568. } else {
  569. if (formOption.value.readonly && formOption.value.readonly === true) {
  570. return true;
  571. } else {
  572. return false;
  573. }
  574. }
  575. };
  576. // 国家初始化
  577. const cityOptionInit = () => {
  578. proxy.post("/customizeArea/list", { parentId: "0" }).then((res) => {
  579. cityOption.value = res.data.map((item, index) => {
  580. return {
  581. ...item,
  582. index: index,
  583. text: item.name,
  584. value: item.id,
  585. children: [],
  586. };
  587. });
  588. });
  589. };
  590. const recursionFn = (arr, val, valueAtt, childrenAtt) => {
  591. for (let i = 0; i < arr.length; i++) {
  592. const e = arr[i];
  593. if (e[valueAtt] !== val) {
  594. if (e[childrenAtt] && e[childrenAtt].length > 0) {
  595. const current = recursionFn(e.children, val, valueAtt, childrenAtt);
  596. if (current) {
  597. return current;
  598. }
  599. }
  600. } else {
  601. return e;
  602. }
  603. }
  604. };
  605. const selectDataEcho = (item, val) => {
  606. if (item.type === "picker" && item.itemType === "onePicker") {
  607. const textAtt = item.fieldNames
  608. ? item.fieldNames.text
  609. : onePickerFieldNames.text;
  610. const valueAtt = item.fieldNames
  611. ? item.fieldNames.value
  612. : onePickerFieldNames.value;
  613. const current = item.data.find((x) => x[valueAtt] === val);
  614. return current ? current[textAtt] : "";
  615. } else if (item.type === "cascader" && item.itemType === "common") {
  616. const textAtt = item.fieldNames ? item.fieldNames.text : fieldNames.text;
  617. const valueAtt = item.fieldNames ? item.fieldNames.value : fieldNames.value;
  618. const childrenAtt = item.fieldNames
  619. ? item.fieldNames.children
  620. : fieldNames.children;
  621. const arr = item.data ? item.data : [];
  622. const current = recursionFn(arr, val, valueAtt, childrenAtt);
  623. return current ? current[textAtt] : "";
  624. }
  625. };
  626. let btnConfigCopy = {};
  627. let callNum = ref(0);
  628. let callListNum = ref(0);
  629. const formDataListShowLabel = () => {
  630. for (let i = 0; i < formData.value[btnConfigCopy.prop].length; i++) {
  631. const iele = formData.value[btnConfigCopy.prop][i];
  632. for (let j = 0; j < btnConfigCopy.listConfig.length; j++) {
  633. const jele = btnConfigCopy.listConfig[j];
  634. if (jele.type === "picker" && jele.itemType !== "datePicker") {
  635. if (jele.data && jele.data.length > 0) {
  636. formData.value[btnConfigCopy.prop][i][jele.prop + "Name"] =
  637. selectDataEcho(
  638. jele,
  639. formData.value[btnConfigCopy.prop][i][jele.prop]
  640. );
  641. } else {
  642. if (callListNum.value <= 3) {
  643. setTimeout(() => {
  644. callListNum.value++;
  645. return formDataListShowLabel();
  646. }, 1500);
  647. }
  648. return;
  649. }
  650. }
  651. }
  652. }
  653. };
  654. const formDataListShowLabelOne = () => {
  655. for (let i = 0; i < formData.value[btnConfigCopy.prop].length; i++) {
  656. for (let j = 0; j < btnConfigCopy.listConfig.length; j++) {
  657. const jele = btnConfigCopy.listConfig[j];
  658. if (jele.type === "picker" && jele.itemType !== "datePicker") {
  659. if (jele.data && jele.data.length > 0) {
  660. formData.value[btnConfigCopy.prop][i][jele.prop + "Name"] =
  661. selectDataEcho(
  662. jele,
  663. formData.value[btnConfigCopy.prop][i][jele.prop]
  664. );
  665. }
  666. }
  667. }
  668. }
  669. };
  670. // 循环 label值回显
  671. const formDataShowLabel = () => {
  672. for (let i = 0; i < formConfig.value.length; i++) {
  673. const element = formConfig.value[i];
  674. if (element.type === "picker" && element.itemType !== "datePicker") {
  675. if (element.data && element.data.length > 0) {
  676. formData.value[element.prop + "Name"] = selectDataEcho(
  677. element,
  678. formData.value[element.prop]
  679. );
  680. } else {
  681. if (callNum.value <= 3) {
  682. setTimeout(() => {
  683. callNum.value++;
  684. return formDataShowLabel();
  685. }, 1500);
  686. }
  687. return;
  688. }
  689. } else if (element.type === "cascader" && element.itemType === "common") {
  690. if (element.data && element.data.length > 0) {
  691. formData.value[element.prop + "Name"] = selectDataEcho(
  692. element,
  693. formData.value[element.prop]
  694. );
  695. } else {
  696. if (callNum.value <= 3) {
  697. setTimeout(() => {
  698. callNum.value++;
  699. return formDataShowLabel();
  700. }, 1500);
  701. }
  702. return;
  703. }
  704. }
  705. }
  706. };
  707. const formDataShowLabelOne = () => {
  708. for (let i = 0; i < formConfig.value.length; i++) {
  709. const element = formConfig.value[i];
  710. if (element.type === "picker" && element.itemType !== "datePicker") {
  711. if (element.data && element.data.length > 0) {
  712. formData.value[element.prop + "Name"] = selectDataEcho(
  713. element,
  714. formData.value[element.prop]
  715. );
  716. }
  717. } else if (element.type === "cascader" && element.itemType === "common") {
  718. if (element.data && element.data.length > 0) {
  719. formData.value[element.prop + "Name"] = selectDataEcho(
  720. element,
  721. formData.value[element.prop]
  722. );
  723. }
  724. }
  725. }
  726. };
  727. const formDataInit = () => {
  728. var map = {
  729. input: "",
  730. radio: "",
  731. switch: false,
  732. checkbox: [],
  733. date: "",
  734. picker: "",
  735. cascader: "",
  736. upload: [],
  737. };
  738. // 判断是否需要按钮
  739. if (
  740. formOption.value.btnConfig &&
  741. Object.keys(formOption.value.btnConfig).length > 0
  742. ) {
  743. btnConfigCopy = { ...formOption.value.btnConfig };
  744. if (formData.value[btnConfigCopy.prop] === undefined) {
  745. formData.value[btnConfigCopy.prop] = [];
  746. }
  747. }
  748. // 初始化默认值
  749. let cityStatus = true;
  750. for (let i = 0; i < formConfig.value.length; i++) {
  751. const element = formConfig.value[i];
  752. if (
  753. element.type === "cascader" &&
  754. element.itemType === "city" &&
  755. cityStatus
  756. ) {
  757. cityStatus = false;
  758. cityOptionInit();
  759. // formData.value[element.prop] = map[element.type];
  760. // formData.value[element.prop + "Name"] = map[element.type];
  761. }
  762. if (
  763. formData.value[element.prop] === undefined ||
  764. formData.value[element.prop] === ""
  765. ) {
  766. if (element.type === "slot") {
  767. continue;
  768. } else if (element.type === "picker" || element.type === "cascader") {
  769. if (element.itemType !== "datePicker") {
  770. formData.value[element.prop] = map[element.type];
  771. formData.value[element.prop + "Name"] = map[element.type];
  772. }
  773. } else if (map[element.type] != undefined) {
  774. formData.value[element.prop] = map[element.type];
  775. }
  776. }
  777. }
  778. formDataShowLabel();
  779. emit("update:modelValue", formData.value);
  780. };
  781. formDataInit();
  782. // 选择框 确定事件
  783. const onConfirmPicker = (option, item, index) => {
  784. if (option.selectedOptions[0]) {
  785. switch (item.itemType) {
  786. case "onePicker": {
  787. formData.value[item.prop + "Name"] =
  788. option.selectedOptions[0][
  789. item.fieldNames.text
  790. ? item.fieldNames.text
  791. : onePickerFieldNames.text
  792. ];
  793. formData.value[item.prop] =
  794. option.selectedOptions[0][
  795. item.fieldNames.value
  796. ? item.fieldNames.value
  797. : onePickerFieldNames.value
  798. ];
  799. formConfig.value[index].showPicker = false;
  800. }
  801. case "datePicker": {
  802. formData.value[item.prop] = option.selectedValues.join(
  803. item.split ? item.split : "-"
  804. );
  805. formConfig.value[index].showPicker = false;
  806. }
  807. }
  808. } else {
  809. formConfig.value[index].showPicker = false;
  810. }
  811. };
  812. const currentIndex = ref(-1);
  813. const currentSonIndex = ref(-1);
  814. const handleListItemClick = (i, index, sonIndex) => {
  815. if (i.readonly !== undefined && i.readonly === true) {
  816. return;
  817. } else {
  818. if (formOption.value.readonly) {
  819. return;
  820. } else {
  821. currentIndex.value = index;
  822. currentSonIndex.value = sonIndex;
  823. btnConfigCopy.listConfig[sonIndex].showPicker = true;
  824. }
  825. }
  826. };
  827. const onConfirmListPicker = (option, item) => {
  828. switch (item.itemType) {
  829. case "onePicker": {
  830. formData.value[btnConfigCopy.prop][currentIndex.value][
  831. item.prop + "Name"
  832. ] =
  833. option.selectedOptions[0][
  834. item.fieldNames.text ? item.fieldNames.text : onePickerFieldNames.text
  835. ];
  836. formData.value[btnConfigCopy.prop][currentIndex.value][item.prop] =
  837. option.selectedOptions[0][
  838. item.fieldNames.value
  839. ? item.fieldNames.value
  840. : onePickerFieldNames.value
  841. ];
  842. btnConfigCopy.listConfig[currentSonIndex.value].showPicker = false;
  843. }
  844. case "datePicker": {
  845. formData.value[btnConfigCopy.prop][currentIndex.value][item.prop] =
  846. option.selectedValues.join(item.split ? item.split : "-");
  847. btnConfigCopy.listConfig[currentSonIndex.value].showPicker = false;
  848. }
  849. }
  850. };
  851. // push事件
  852. const handlePush = () => {
  853. if (btnConfigCopy.clickFn && typeof btnConfigCopy.clickFn == "function") {
  854. btnConfigCopy.clickFn();
  855. }
  856. };
  857. // remove
  858. const handleRemove = (index, item) => {
  859. if (item.deleteFn) {
  860. item.deleteFn(index);
  861. } else {
  862. formData.value[btnConfigCopy.prop].splice(index, 1);
  863. }
  864. };
  865. // 拉去城市最近数据及处理
  866. const getAreaInfo = (selectedOptions, item, index) => {
  867. showLoadingToast("加载中...");
  868. proxy
  869. .post("/customizeArea/list", { parentId: selectedOptions.value })
  870. .then((res) => {
  871. let countryIndex = selectedOptions.selectedOptions[0].index;
  872. let provinceIndex =
  873. selectedOptions.tabIndex === 1
  874. ? selectedOptions.selectedOptions[1].index
  875. : null;
  876. let cityIndex =
  877. selectedOptions.tabIndex === 2
  878. ? selectedOptions.selectedOptions[2].index
  879. : null;
  880. //已经没有下级数据
  881. if (res.data.length === 0) {
  882. if (selectedOptions.tabIndex === 1) {
  883. formData.value[item.prop + "Name"] = selectedOptions.selectedOptions
  884. .map((item) => item.text)
  885. .join(" ");
  886. formConfig.value[index].showPicker = false;
  887. formData.value.cityObj = selectedOptions;
  888. return;
  889. }
  890. }
  891. if (selectedOptions.tabIndex === 2) {
  892. formData.value[item.prop + "Name"] = selectedOptions.selectedOptions
  893. .map((item) => item.text)
  894. .join(" ");
  895. formConfig.value[index].showPicker = false;
  896. formData.value.cityObj = selectedOptions;
  897. return;
  898. }
  899. if (selectedOptions.tabIndex === 0) {
  900. cityOption.value[countryIndex].children = res.data.map(
  901. (item, index) => {
  902. return {
  903. ...item,
  904. index: index,
  905. text: item.name,
  906. value: item.id,
  907. };
  908. }
  909. );
  910. } else if (selectedOptions.tabIndex === 1) {
  911. cityOption.value[countryIndex].children[provinceIndex].children =
  912. res.data.map((item, index) => {
  913. return {
  914. ...item,
  915. index: index,
  916. text: item.name,
  917. value: item.id,
  918. };
  919. });
  920. } else if (selectedOptions.tabIndex === 2) {
  921. cityOption.value[countryIndex].children[provinceIndex].children[
  922. cityIndex
  923. ].children = res.data.map((item, index) => {
  924. return {
  925. ...item,
  926. index: index,
  927. text: item.name,
  928. value: item.id,
  929. };
  930. });
  931. }
  932. closeToast();
  933. });
  934. };
  935. // 城市变动事件
  936. const cityOnChange = (options, item, index) => {
  937. getAreaInfo(options, item, index);
  938. };
  939. const commonOnChange = ({ selectedOptions }, item, index) => {
  940. const textAtt = item.fieldNames ? item.fieldNames.text : fieldNames.text;
  941. formData.value[item.prop + "Name"] =
  942. selectedOptions[selectedOptions.length - 1][textAtt];
  943. };
  944. const handleCommonFinish = (index) => {
  945. formConfig.value[index].showPicker = false;
  946. };
  947. // 文件上传
  948. const onOversize = () => {
  949. showToast("文件大小不能超过 5MB");
  950. };
  951. const afterRead = (file) => {
  952. if (file && file.length > 0) {
  953. for (let i = 0; i < file.length; i++) {
  954. file[i].status = "uploading";
  955. file[i].message = "上传中...";
  956. proxy.post("/fileInfo/getSing", { fileName: file[i].file.name }).then(
  957. (res) => {
  958. let forms = new FormData();
  959. forms.append("file", file[i].file);
  960. proxy
  961. .post("https://winfaster.obs.cn-south-1.myhuaweicloud.com", {
  962. ...res.data.uploadBody,
  963. file: forms.get("file"),
  964. })
  965. .then(
  966. () => {
  967. file[i].id = res.data.id;
  968. file[i].url = res.data.fileUrl;
  969. file[i].fileName = res.data.fileName;
  970. delete file[i].status;
  971. delete file[i].message;
  972. },
  973. () => {
  974. file[i].status = "failed";
  975. file[i].message = "上传失败";
  976. }
  977. );
  978. },
  979. () => {
  980. file[i].status = "failed";
  981. file[i].message = "上传失败";
  982. }
  983. );
  984. }
  985. } else {
  986. file.status = "uploading";
  987. file.message = "上传中...";
  988. proxy.post("/fileInfo/getSing", { fileName: file.file.name }).then(
  989. (res) => {
  990. let forms = new FormData();
  991. forms.append("file", file.file);
  992. proxy
  993. .post("https://winfaster.obs.cn-south-1.myhuaweicloud.com", {
  994. ...res.data.uploadBody,
  995. file: forms.get("file"),
  996. })
  997. .then(
  998. () => {
  999. file.id = res.data.id;
  1000. file.url = res.data.fileUrl;
  1001. file.fileName = res.data.fileName;
  1002. delete file.status;
  1003. delete file.message;
  1004. },
  1005. () => {
  1006. file.status = "failed";
  1007. file.message = "上传失败";
  1008. }
  1009. );
  1010. },
  1011. () => {
  1012. file.status = "failed";
  1013. file.message = "上传失败";
  1014. }
  1015. );
  1016. }
  1017. };
  1018. const testForm = ref(null); // 延迟使用,因为还没有返回跟挂载
  1019. watch(
  1020. formData.value,
  1021. (val) => {
  1022. emit("update:modelValue", val);
  1023. },
  1024. {
  1025. deep: true,
  1026. }
  1027. );
  1028. watch(
  1029. () => formData.value[btnConfigCopy.prop],
  1030. (val) => {
  1031. if (
  1032. formOption.value.btnConfig !== undefined &&
  1033. !formOption.value.btnConfig.isNeed &&
  1034. val &&
  1035. val.length > 0
  1036. ) {
  1037. formDataListShowLabel();
  1038. }
  1039. }
  1040. );
  1041. const changeCheckboxGroup = (form, label, data, fieldNames) => {
  1042. let text = "";
  1043. if (form[label] && form[label].length > 0) {
  1044. form[label].map((item) => {
  1045. let list = data.filter((itemData) => itemData[fieldNames.value] === item);
  1046. if (list && list.length > 0) {
  1047. if (text) {
  1048. text = text + "," + list[0][fieldNames.text];
  1049. } else {
  1050. text = list[0][fieldNames.text];
  1051. }
  1052. }
  1053. });
  1054. }
  1055. form[label + "Name"] = text;
  1056. };
  1057. const datePickerDateArr = ref([]);
  1058. const datePickerTimeArr = ref([]);
  1059. const datePickerTimeConfirm = (item, index) => {
  1060. formData.value[item.prop] =
  1061. datePickerDateArr.value.join("-") + " " + datePickerTimeArr.value.join(":");
  1062. formConfig.value[index].showPicker = false;
  1063. };
  1064. const defaultTimeFn = (item, index) => {
  1065. datePickerDateArr.value = formatDate(
  1066. new Date(formData.value[item.prop]),
  1067. "yyyy-MM-dd"
  1068. ).split("-");
  1069. datePickerTimeArr.value = formatDate(
  1070. new Date(formData.value[item.prop]),
  1071. "hh:mm:ss"
  1072. ).split(":");
  1073. formConfig.value[index].showPicker = true;
  1074. };
  1075. defineExpose({
  1076. formDataShowLabelOne,
  1077. formDataListShowLabelOne,
  1078. btnConfigCopy,
  1079. testForm,
  1080. });
  1081. // onMounted(() => {});
  1082. </script>
  1083. <style lang="scss" scoped>
  1084. .btn-box {
  1085. width: 100%;
  1086. text-align: center;
  1087. background: #f2f2f2;
  1088. padding: 10px 0;
  1089. }
  1090. .row {
  1091. color: #999999;
  1092. background: #ecebeb;
  1093. height: 32px;
  1094. line-height: 32px;
  1095. display: flex;
  1096. justify-content: space-between;
  1097. align-items: center;
  1098. padding: 0 15px;
  1099. }
  1100. ._title {
  1101. font-size: 14px;
  1102. font-weight: 700;
  1103. }
  1104. ::v-deep {
  1105. .multipleChoice {
  1106. margin-bottom: 60px;
  1107. .van-checkbox {
  1108. justify-content: center;
  1109. margin: 10px 40px;
  1110. }
  1111. }
  1112. .van-form {
  1113. margin-top: 0px !important;
  1114. }
  1115. }
  1116. </style>