index.vue 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  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. <van-cell-group inset>
  12. <div v-for="(i, index) in formConfig" :key="index" :style="i.style || ''">
  13. <van-field v-if="i.type == 'title'" style="background: #ecebeb">
  14. <template #input>
  15. <div class="_title">
  16. {{ i.title }}
  17. </div>
  18. </template>
  19. </van-field>
  20. <van-field
  21. v-if="i.type == 'input'"
  22. v-model="formData[i.prop]"
  23. :label="i.label"
  24. :name="i.prop"
  25. :type="i.itemType ? i.itemType : 'text'"
  26. :placeholder="i.placeholder ? i.placeholder : '请输入'"
  27. :clearable="i.clearable ? i.clearable : false"
  28. :readonly="getFieldReadonly(i)"
  29. :rules="getRules(i.prop)"
  30. :required="getRequired(i.prop)"
  31. :right-icon="i.isNeedRightBtn ? i.rightIcon : ''"
  32. @click-right-icon="i.isNeedRightBtn ? i.rightIconClick() : () => {}"
  33. @blur="i.isNeedBlurMethon ? i.blurMethon(formData[i.prop]) : () => {}">
  34. </van-field>
  35. <!-- switch -->
  36. <van-field v-if="i.type == 'switch'" :label="i.label" :name="i.prop" :required="getRequired(i.prop)">
  37. <template #input>
  38. <van-switch v-model="formData[i.prop]" />
  39. </template>
  40. </van-field>
  41. <!-- 多选checkbox -->
  42. <van-field v-if="i.type == 'checkbox'" :label="i.label" :name="i.prop" :rules="getRules(i.prop)" :required="getRequired(i.prop)">
  43. <template #input>
  44. <van-checkbox-group v-model="formData[i.prop]" direction="horizontal">
  45. <van-checkbox shape="square" v-for="j in i.data" :key="j.value" :name="j.value">{{ j.text }}</van-checkbox>
  46. </van-checkbox-group>
  47. </template>
  48. </van-field>
  49. <!-- 单选radio -->
  50. <van-field v-if="i.type == 'radio'" :label="i.label" :name="i.prop" :rules="getRules(i.prop)" :required="getRequired(i.prop)">
  51. <template #input>
  52. <van-radio-group v-model="formData[i.prop]" direction="horizontal">
  53. <van-radio v-for="j in i.data" :key="j.value" :name="j.value || j.id">{{ j.label || j.title }}</van-radio>
  54. </van-radio-group>
  55. </template>
  56. </van-field>
  57. <!-- 单选 -->
  58. <van-field
  59. v-if="i.type == 'picker' && i.itemType == 'onePicker'"
  60. v-show="!i.showStatus"
  61. :label="i.label"
  62. :name="i.prop"
  63. v-model="formData[i.prop + 'Name']"
  64. is-link
  65. :readonly="true"
  66. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  67. @click="() => (formOption.readonly || i.readonly ? '' : (i.showPicker = true))"
  68. :rules="getRules(i.prop)"
  69. :required="getRequired(i.prop)">
  70. </van-field>
  71. <van-popup v-model:show="i.showPicker" round position="bottom" v-if="i.type == 'picker' && i.itemType == 'onePicker'">
  72. <van-picker
  73. :columns="i.data"
  74. :columns-field-names="i.fieldNames ? i.fieldNames : onePickerFieldNames"
  75. @cancel="i.showPicker = false"
  76. @confirm="(option) => (i.changeFn ? i.changeFn(option, i, index) : onConfirmPicker(option, i, index))" />
  77. </van-popup>
  78. <!-- 多选 -->
  79. <van-field
  80. v-if="i.type == 'multipleChoice' && i.itemType == 'multiple'"
  81. v-show="!i.showStatus"
  82. :label="i.label"
  83. :name="i.prop"
  84. v-model="formData[i.prop + 'Name']"
  85. is-link
  86. :readonly="true"
  87. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  88. @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
  89. :rules="getRules(i.prop)"
  90. :required="getRequired(i.prop)">
  91. </van-field>
  92. <van-popup
  93. v-model:show="i.showPicker"
  94. round
  95. position="bottom"
  96. :style="{ height: '40%' }"
  97. v-if="i.type == 'multipleChoice' && i.itemType == 'multiple'">
  98. <van-checkbox-group v-model="formData[i.prop]" @change="changeCheckboxGroup(formData, i.prop, i.data, i.fieldNames)" class="multipleChoice">
  99. <van-checkbox v-for="item in i.data" :key="item.value" :name="item.value">{{ item.text }}</van-checkbox>
  100. </van-checkbox-group>
  101. </van-popup>
  102. <!-- 时间选择器 -->
  103. <van-field
  104. v-if="i.type == 'picker' && i.itemType == 'datePicker'"
  105. :label="i.label"
  106. :name="i.prop"
  107. v-model="formData[i.prop]"
  108. is-link
  109. :readonly="true"
  110. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  111. @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
  112. :rules="getRules(i.prop)"
  113. :required="getRequired(i.prop)">
  114. </van-field>
  115. <van-popup v-model:show="i.showPicker" round position="bottom" v-if="i.type == 'picker' && i.itemType == 'datePicker'">
  116. <van-date-picker
  117. @confirm="(option) => onConfirmPicker(option, i, index)"
  118. @cancel="i.showPicker = false"
  119. :min-date="i.minDate"
  120. :max-date="i.maxDate"
  121. :columns-type="i.columnsType" />
  122. </van-popup>
  123. <!-- 级联 城市 -->
  124. <van-field
  125. v-if="i.type == 'cascader' && i.itemType == 'city'"
  126. :label="i.label"
  127. :name="i.prop"
  128. v-model="formData[i.prop + 'Name']"
  129. is-link
  130. :readonly="true"
  131. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  132. @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
  133. :rules="getRules(i.prop)"
  134. :required="getRequired(i.prop)" />
  135. <van-popup v-if="i.type == 'cascader' && i.itemType == 'city'" v-model:show="i.showPicker" round position="bottom">
  136. <van-cascader
  137. v-model="formData[i.prop]"
  138. :title="i.title ? i.title : '请选择'"
  139. :options="cityOption"
  140. @close="i.showPicker = false"
  141. @change="(option) => cityOnChange(option, i, index)"
  142. @finish="(option) => (i.finishFn ? i.finishFn(option) : () => (i.showPicker = false))" />
  143. </van-popup>
  144. <!-- 级联 公共 -->
  145. <van-field
  146. v-if="i.type == 'cascader' && i.itemType == 'common'"
  147. :label="i.label"
  148. :name="i.prop"
  149. v-model="formData[i.prop + 'Name']"
  150. is-link
  151. :readonly="true"
  152. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  153. @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
  154. :rules="getRules(i.prop)"
  155. :required="getRequired(i.prop)" />
  156. <van-popup v-if="i.type == 'cascader' && i.itemType == 'common'" v-model:show="i.showPicker" round position="bottom">
  157. <van-cascader
  158. v-model="formData[i.prop]"
  159. :title="i.title ? i.title : '请选择'"
  160. :options="i.data"
  161. :field-names="i.fieldNames ? i.fieldNames : fieldNames"
  162. @close="i.showPicker = false"
  163. @change="(option) => commonOnChange(option, i, index)"
  164. @finish="(option) => (i.finishFn ? i.finishFn(i, option) : handleCommonFinish(index))" />
  165. </van-popup>
  166. <!-- 文件上传 -->
  167. <van-field name="uploader" v-if="i.type == 'upload'" :label="i.label" :readonly="i.readonly ? true : false">
  168. <template #input>
  169. <van-uploader v-model="formData[i.prop]" :after-read="afterRead" multiple :max-count="9" :max-size="5 * 1024 * 1024" @oversize="onOversize" />
  170. </template>
  171. </van-field>
  172. <!-- 插槽 -->
  173. <van-field v-if="i.type == 'slot'" :label="i.label" :rules="getRules(i.prop)" :required="getRequired(i.prop)">
  174. <template #input>
  175. <div style="width: 100%">
  176. <slot :name="i.slotName"> {{ i.slotName }}插槽占位符 </slot>
  177. </div>
  178. </template>
  179. </van-field>
  180. </div>
  181. </van-cell-group>
  182. <!-- 循环业务数据 -->
  183. <van-cell-group inset v-for="(item, index) in formData[btnConfigCopy.prop]" :key="index">
  184. <div class="row">
  185. <div>{{ btnConfigCopy.listTitle || "明细" }}{{ index + 1 }}</div>
  186. <van-button
  187. plain
  188. type="primary"
  189. @click="handleRemove(index, btnConfigCopy)"
  190. size="mini"
  191. style="border: none; background: #ecebeb"
  192. v-if="formOption.btnConfig !== undefined && formOption.btnConfig.isNeed"
  193. >删除</van-button
  194. >
  195. </div>
  196. <!-- 循环表单数据 -->
  197. <div v-for="(i, sonIndex) in btnConfigCopy.listConfig" :key="i.prop">
  198. <van-field
  199. v-if="i.type == 'input'"
  200. v-model="formData[btnConfigCopy.prop][index][i.prop]"
  201. :label="i.label"
  202. :name="i.prop"
  203. :type="i.itemType ? i.itemType : 'text'"
  204. :placeholder="i.placeholder ? i.placeholder : '请输入'"
  205. :clearable="i.clearable ? i.clearable : false"
  206. :readonly="getFieldReadonly(i)"
  207. :rules="getRules(i.prop)"
  208. :required="getRequired(i.prop)"
  209. @change="
  210. (val) => {
  211. return i.changeFn ? i.changeFn(index, val) : () => {};
  212. }
  213. ">
  214. </van-field>
  215. <!-- 单选 -->
  216. <van-field
  217. v-if="i.type == 'picker' && i.itemType == 'onePicker'"
  218. :label="i.label"
  219. :name="i.prop"
  220. v-model="formData[btnConfigCopy.prop][index][i.prop + 'Name']"
  221. is-link
  222. :readonly="true"
  223. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  224. @click="handleListItemClick(i, index, sonIndex)"
  225. :rules="getRules(i.prop)"
  226. :required="getRequired(i.prop)">
  227. <template #input v-if="i.isShowScanCode">
  228. <div style="display: flex; height: 24px">
  229. <div style="width: calc(100vw - 100px)">
  230. {{ formData[btnConfigCopy.prop][index][i.prop + "Name"] }}
  231. </div>
  232. <div style="width: 100px; float: right; margin-top: -20px">
  233. <van-button plain type="primary" @click.native.stop="i.scanCode(index)" size="mini" style="border: none">扫码</van-button>
  234. </div>
  235. </div>
  236. </template>
  237. </van-field>
  238. <!-- 时间选择器 -->
  239. <van-field
  240. v-if="i.type == 'picker' && i.itemType == 'datePicker'"
  241. :label="i.label"
  242. :name="i.prop"
  243. v-model="formData[btnConfigCopy.prop][index][i.prop]"
  244. is-link
  245. :readonly="true"
  246. :placeholder="i.placeholder ? i.placeholder : '请选择'"
  247. @click="handleListItemClick(i, index, sonIndex)"
  248. :rules="getRules(i.prop)"
  249. :required="getRequired(i.prop)">
  250. </van-field>
  251. </div>
  252. </van-cell-group>
  253. <!-- 单独写个循环,保证弹窗唯一 -->
  254. <div v-for="(item, index) in btnConfigCopy.listConfig" :key="index">
  255. <van-popup v-model:show="item.showPicker" round position="bottom" v-if="item.type == 'picker' && item.itemType == 'onePicker'">
  256. <van-picker
  257. :columns="item.data"
  258. :columns-field-names="item.fieldNames ? item.fieldNames : onePickerFieldNames"
  259. @cancel="item.showPicker = false"
  260. @confirm="(option) =>(item.changeFn ? item.changeFn(option, item, currentIndex , currentSonIndex) : onConfirmListPicker(option, item))"
  261. />
  262. <!-- @confirm="(option) => onConfirmListPicker(option, item)" -->
  263. </van-popup>
  264. <van-popup v-model:show="item.showPicker" round position="bottom" v-if="item.type == 'picker' && item.itemType == 'datePicker'">
  265. <van-date-picker
  266. @confirm="(option) => onConfirmListPicker(option, item)"
  267. @cancel="item.showPicker = false"
  268. :min-date="item.minDate"
  269. :max-date="item.maxDate"
  270. :columns-type="item.columnsType" />
  271. </van-popup>
  272. </div>
  273. <!-- 按钮 -->
  274. <div class="btn-box" v-if="formOption.btnConfig !== undefined && formOption.btnConfig.isNeed">
  275. <van-button
  276. :plain="btnConfigCopy.plain ? btnConfigCopy.plain : false"
  277. :type="btnConfigCopy.itemType ? btnConfigCopy.itemType : 'primary'"
  278. :size="btnConfigCopy.size ? btnConfigCopy.size : 'small'"
  279. style="width: 100%; border: none"
  280. @click="handlePush()">
  281. <template #icon>
  282. <van-icon :name="btnConfigCopy.icon ? btnConfigCopy.icon : 'plus'" :size="12" />
  283. </template>
  284. {{ btnConfigCopy.btnName ? btnConfigCopy.btnName : "添加" }}</van-button
  285. >
  286. </div>
  287. <div style="margin: 16px" v-show="!formOption.hiddenSubmitBtn">
  288. <van-button round block type="primary" native-type="submit">
  289. {{ formOption.submitBtnText || "提交" }}
  290. </van-button>
  291. </div>
  292. <div style="margin: 16px" v-show="formOption.otherBtn">
  293. <van-button round block type="warning" @click="handleOtherBtnClick">
  294. {{ formOption.otherBtnText || "其他" }}
  295. </van-button>
  296. </div>
  297. </van-form>
  298. </div>
  299. </template>
  300. <script setup>
  301. import { showLoadingToast, closeToast, showNotify } from "vant";
  302. import { ref, getCurrentInstance, onMounted, reactive, computed, toRefs, watch, defineExpose } from "vue";
  303. const props = defineProps({
  304. modelValue: {
  305. type: Object,
  306. default: false,
  307. },
  308. formConfig: {
  309. type: Array,
  310. default: false,
  311. },
  312. formOption: {
  313. type: Object,
  314. default: false,
  315. },
  316. rules: {
  317. type: Object,
  318. default: false,
  319. },
  320. });
  321. const proxy = getCurrentInstance().proxy;
  322. const { formConfig, formOption, rules } = toRefs(props);
  323. const formData = computed(() => {
  324. return proxy.modelValue;
  325. });
  326. const cityOption = ref([]);
  327. const fileList = ref([]);
  328. const fieldNames = { text: "label", value: "id", children: "children" };
  329. const onePickerFieldNames = {
  330. text: "text",
  331. value: "value",
  332. };
  333. const emit = defineEmits(["update:modelValue"]);
  334. const onSubmit = () => {
  335. emit("onSubmit");
  336. };
  337. const handleOtherBtnClick = () => {
  338. emit("otherBtnClick");
  339. };
  340. // 获取验证规则
  341. const getRules = (prop) => {
  342. if (rules.value.hasOwnProperty(prop) && rules.value[prop]) {
  343. return rules.value[prop];
  344. }
  345. };
  346. const getRequired = (prop) => {
  347. if (rules.value.hasOwnProperty(prop) && rules.value[prop]) {
  348. return true;
  349. }
  350. return false;
  351. };
  352. const getReadonly = (i) => {
  353. return i.readonly ? i.readonly : i.name == "picker" ? true : false;
  354. };
  355. const getFieldReadonly = (i) => {
  356. if (i.readonly && i.readonly === true) {
  357. return true;
  358. } else {
  359. if (formOption.value.readonly && formOption.value.readonly === true) {
  360. return true;
  361. } else {
  362. return false;
  363. }
  364. }
  365. };
  366. // 国家初始化
  367. const cityOptionInit = () => {
  368. proxy.post("/customizeArea/list", { parentId: "0" }).then((res) => {
  369. cityOption.value = res.data.map((item, index) => {
  370. return {
  371. ...item,
  372. index: index,
  373. text: item.name,
  374. value: item.id,
  375. children: [],
  376. };
  377. });
  378. });
  379. };
  380. const recursionFn = (arr, val, valueAtt, childrenAtt) => {
  381. for (let i = 0; i < arr.length; i++) {
  382. const e = arr[i];
  383. if (e[valueAtt] !== val) {
  384. if (e[childrenAtt] && e[childrenAtt].length > 0) {
  385. const current = recursionFn(e.children, val, valueAtt, childrenAtt);
  386. if (current) {
  387. return current;
  388. }
  389. }
  390. } else {
  391. return e;
  392. }
  393. }
  394. };
  395. const selectDataEcho = (item, val) => {
  396. if (item.type === "picker" && item.itemType === "onePicker") {
  397. const textAtt = item.fieldNames ? item.fieldNames.text : onePickerFieldNames.text;
  398. const valueAtt = item.fieldNames ? item.fieldNames.value : onePickerFieldNames.value;
  399. const current = item.data.find((x) => x[valueAtt] === val);
  400. return current ? current[textAtt] : "";
  401. } else if (item.type === "cascader" && item.itemType === "common") {
  402. const textAtt = item.fieldNames ? item.fieldNames.text : fieldNames.text;
  403. const valueAtt = item.fieldNames ? item.fieldNames.value : fieldNames.value;
  404. const childrenAtt = item.fieldNames ? item.fieldNames.children : fieldNames.children;
  405. const arr = item.data ? item.data : [];
  406. const current = recursionFn(arr, val, valueAtt, childrenAtt);
  407. return current ? current[textAtt] : "";
  408. }
  409. };
  410. let btnConfigCopy = {};
  411. let callNum = ref(0);
  412. let callListNum = ref(0);
  413. const formDataListShowLabel = () => {
  414. for (let i = 0; i < formData.value[btnConfigCopy.prop].length; i++) {
  415. const iele = formData.value[btnConfigCopy.prop][i];
  416. for (let j = 0; j < btnConfigCopy.listConfig.length; j++) {
  417. const jele = btnConfigCopy.listConfig[j];
  418. if (jele.type === "picker" && jele.itemType !== "datePicker") {
  419. if (jele.data && jele.data.length > 0) {
  420. formData.value[btnConfigCopy.prop][i][jele.prop + "Name"] = selectDataEcho(jele, formData.value[btnConfigCopy.prop][i][jele.prop]);
  421. } else {
  422. if (callListNum.value <= 3) {
  423. setTimeout(() => {
  424. callListNum.value++;
  425. return formDataListShowLabel();
  426. }, 1500);
  427. }
  428. return;
  429. }
  430. }
  431. }
  432. }
  433. };
  434. const formDataListShowLabelOne = () => {
  435. for (let i = 0; i < formData.value[btnConfigCopy.prop].length; i++) {
  436. for (let j = 0; j < btnConfigCopy.listConfig.length; j++) {
  437. const jele = btnConfigCopy.listConfig[j];
  438. if (jele.type === "picker" && jele.itemType !== "datePicker") {
  439. if (jele.data && jele.data.length > 0) {
  440. formData.value[btnConfigCopy.prop][i][jele.prop + "Name"] = selectDataEcho(jele, formData.value[btnConfigCopy.prop][i][jele.prop]);
  441. }
  442. }
  443. }
  444. }
  445. };
  446. // 循环 label值回显
  447. const formDataShowLabel = () => {
  448. for (let i = 0; i < formConfig.value.length; i++) {
  449. const element = formConfig.value[i];
  450. if (element.type === "picker" && element.itemType !== "datePicker") {
  451. if (element.data && element.data.length > 0) {
  452. formData.value[element.prop + "Name"] = selectDataEcho(element, formData.value[element.prop]);
  453. } else {
  454. if (callNum.value <= 3) {
  455. setTimeout(() => {
  456. callNum.value++;
  457. return formDataShowLabel();
  458. }, 1500);
  459. }
  460. return;
  461. }
  462. } else if (element.type === "cascader" && element.itemType === "common") {
  463. if (element.data && element.data.length > 0) {
  464. formData.value[element.prop + "Name"] = selectDataEcho(element, formData.value[element.prop]);
  465. } else {
  466. if (callNum.value <= 3) {
  467. setTimeout(() => {
  468. callNum.value++;
  469. return formDataShowLabel();
  470. }, 1500);
  471. }
  472. return;
  473. }
  474. }
  475. }
  476. };
  477. const formDataShowLabelOne = () => {
  478. for (let i = 0; i < formConfig.value.length; i++) {
  479. const element = formConfig.value[i];
  480. if (element.type === "picker" && element.itemType !== "datePicker") {
  481. if (element.data && element.data.length > 0) {
  482. formData.value[element.prop + "Name"] = selectDataEcho(element, formData.value[element.prop]);
  483. }
  484. } else if (element.type === "cascader" && element.itemType === "common") {
  485. if (element.data && element.data.length > 0) {
  486. formData.value[element.prop + "Name"] = selectDataEcho(element, formData.value[element.prop]);
  487. }
  488. }
  489. }
  490. };
  491. const formDataInit = () => {
  492. var map = {
  493. input: "",
  494. radio: "",
  495. switch: false,
  496. checkbox: [],
  497. date: "",
  498. picker: "",
  499. cascader: "",
  500. upload: [],
  501. };
  502. // 判断是否需要按钮
  503. if (formOption.value.btnConfig && Object.keys(formOption.value.btnConfig).length > 0) {
  504. btnConfigCopy = { ...formOption.value.btnConfig };
  505. if (formData.value[btnConfigCopy.prop] === undefined) {
  506. formData.value[btnConfigCopy.prop] = [];
  507. }
  508. }
  509. // 初始化默认值
  510. for (let i = 0; i < formConfig.value.length; i++) {
  511. const element = formConfig.value[i];
  512. if (element.type === "cascader" && element.itemType === "city") {
  513. cityOptionInit();
  514. // formData.value[element.prop] = map[element.type];
  515. // formData.value[element.prop + "Name"] = map[element.type];
  516. }
  517. if (formData.value[element.prop] === undefined || formData.value[element.prop] === "") {
  518. if (element.type === "slot") {
  519. continue;
  520. } else if (element.type === "picker" || element.type === "cascader") {
  521. if (element.itemType !== "datePicker") {
  522. formData.value[element.prop] = map[element.type];
  523. formData.value[element.prop + "Name"] = map[element.type];
  524. }
  525. } else if (map[element.type] != undefined) {
  526. formData.value[element.prop] = map[element.type];
  527. }
  528. }
  529. }
  530. formDataShowLabel();
  531. emit("update:modelValue", formData.value);
  532. };
  533. formDataInit();
  534. // 选择框 确定事件
  535. const onConfirmPicker = (option, item, index) => {
  536. if (option.selectedOptions[0]) {
  537. switch (item.itemType) {
  538. case "onePicker": {
  539. formData.value[item.prop + "Name"] = option.selectedOptions[0][item.fieldNames.text ? item.fieldNames.text : onePickerFieldNames.text];
  540. formData.value[item.prop] = option.selectedOptions[0][item.fieldNames.value ? item.fieldNames.value : onePickerFieldNames.value];
  541. formConfig.value[index].showPicker = false;
  542. }
  543. case "datePicker": {
  544. formData.value[item.prop] = option.selectedValues.join(item.split ? item.split : "-");
  545. formConfig.value[index].showPicker = false;
  546. }
  547. }
  548. } else {
  549. formConfig.value[index].showPicker = false;
  550. }
  551. };
  552. const currentIndex = ref(-1);
  553. const currentSonIndex = ref(-1);
  554. const handleListItemClick = (i, index, sonIndex) => {
  555. if (i.readonly !== undefined && i.readonly === true) {
  556. return;
  557. } else {
  558. if (formOption.value.readonly) {
  559. return;
  560. } else {
  561. currentIndex.value = index;
  562. currentSonIndex.value = sonIndex;
  563. btnConfigCopy.listConfig[sonIndex].showPicker = true;
  564. }
  565. }
  566. };
  567. const onConfirmListPicker = (option, item) => {
  568. switch (item.itemType) {
  569. case "onePicker": {
  570. formData.value[btnConfigCopy.prop][currentIndex.value][item.prop + "Name"] =
  571. option.selectedOptions[0][item.fieldNames.text ? item.fieldNames.text : onePickerFieldNames.text];
  572. formData.value[btnConfigCopy.prop][currentIndex.value][item.prop] =
  573. option.selectedOptions[0][item.fieldNames.value ? item.fieldNames.value : onePickerFieldNames.value];
  574. btnConfigCopy.listConfig[currentSonIndex.value].showPicker = false;
  575. }
  576. case "datePicker": {
  577. formData.value[btnConfigCopy.prop][currentIndex.value][item.prop] = option.selectedValues.join(item.split ? item.split : "-");
  578. btnConfigCopy.listConfig[currentSonIndex.value].showPicker = false;
  579. }
  580. }
  581. };
  582. // push事件
  583. const handlePush = () => {
  584. if (btnConfigCopy.clickFn && typeof btnConfigCopy.clickFn == "function") {
  585. btnConfigCopy.clickFn();
  586. }
  587. };
  588. // remove
  589. const handleRemove = (index, item) => {
  590. if (item.deleteFn) {
  591. item.deleteFn(index);
  592. } else {
  593. formData.value[btnConfigCopy.prop].splice(index, 1);
  594. }
  595. };
  596. // 拉去城市最近数据及处理
  597. const getAreaInfo = (selectedOptions, item, index) => {
  598. showLoadingToast("加载中...");
  599. proxy.post("/customizeArea/list", { parentId: selectedOptions.value }).then((res) => {
  600. let countryIndex = selectedOptions.selectedOptions[0].index;
  601. let provinceIndex = selectedOptions.tabIndex === 1 ? selectedOptions.selectedOptions[1].index : null;
  602. let cityIndex = selectedOptions.tabIndex === 2 ? selectedOptions.selectedOptions[2].index : null;
  603. //已经没有下级数据
  604. if (res.data.length === 0) {
  605. if (selectedOptions.tabIndex === 1) {
  606. formData.value[item.prop + "Name"] = selectedOptions.selectedOptions.map((item) => item.text).join(" ");
  607. formConfig.value[index].showPicker = false;
  608. formData.value.cityObj = selectedOptions;
  609. return;
  610. }
  611. }
  612. if (selectedOptions.tabIndex === 2) {
  613. formData.value[item.prop + "Name"] = selectedOptions.selectedOptions.map((item) => item.text).join(" ");
  614. formConfig.value[index].showPicker = false;
  615. formData.value.cityObj = selectedOptions;
  616. return;
  617. }
  618. if (selectedOptions.tabIndex === 0) {
  619. cityOption.value[countryIndex].children = res.data.map((item, index) => {
  620. return {
  621. ...item,
  622. index: index,
  623. text: item.name,
  624. value: item.id,
  625. };
  626. });
  627. } else if (selectedOptions.tabIndex === 1) {
  628. cityOption.value[countryIndex].children[provinceIndex].children = res.data.map((item, index) => {
  629. return {
  630. ...item,
  631. index: index,
  632. text: item.name,
  633. value: item.id,
  634. };
  635. });
  636. } else if (selectedOptions.tabIndex === 2) {
  637. cityOption.value[countryIndex].children[provinceIndex].children[cityIndex].children = res.data.map((item, index) => {
  638. return {
  639. ...item,
  640. index: index,
  641. text: item.name,
  642. value: item.id,
  643. };
  644. });
  645. }
  646. closeToast();
  647. });
  648. };
  649. // 城市变动事件
  650. const cityOnChange = (options, item, index) => {
  651. getAreaInfo(options, item, index);
  652. };
  653. const commonOnChange = ({ selectedOptions }, item, index) => {
  654. const textAtt = item.fieldNames ? item.fieldNames.text : fieldNames.text;
  655. formData.value[item.prop + "Name"] = selectedOptions[selectedOptions.length - 1][textAtt];
  656. };
  657. const handleCommonFinish = (index) => {
  658. formConfig.value[index].showPicker = false;
  659. };
  660. // 文件上传
  661. const onOversize = () => {
  662. showToast("文件大小不能超过 5MB");
  663. };
  664. const afterRead = (file) => {
  665. if (file && file.length > 0) {
  666. for (let i = 0; i < file.length; i++) {
  667. file[i].status = "uploading";
  668. file[i].message = "上传中...";
  669. proxy.post("/fileInfo/getSing", { fileName: file[i].file.name }).then(
  670. (res) => {
  671. let forms = new FormData();
  672. forms.append("file", file[i].file);
  673. proxy
  674. .post("https://winfaster.obs.cn-south-1.myhuaweicloud.com", {
  675. ...res.data.uploadBody,
  676. file: forms.get("file"),
  677. })
  678. .then(
  679. () => {
  680. file[i].id = res.data.id;
  681. file[i].url = res.data.fileUrl;
  682. file[i].fileName = res.data.fileName;
  683. delete file[i].status;
  684. delete file[i].message;
  685. },
  686. () => {
  687. file[i].status = "failed";
  688. file[i].message = "上传失败";
  689. }
  690. );
  691. },
  692. () => {
  693. file[i].status = "failed";
  694. file[i].message = "上传失败";
  695. }
  696. );
  697. }
  698. } else {
  699. file.status = "uploading";
  700. file.message = "上传中...";
  701. proxy.post("/fileInfo/getSing", { fileName: file.file.name }).then(
  702. (res) => {
  703. let forms = new FormData();
  704. forms.append("file", file.file);
  705. proxy
  706. .post("https://winfaster.obs.cn-south-1.myhuaweicloud.com", {
  707. ...res.data.uploadBody,
  708. file: forms.get("file"),
  709. })
  710. .then(
  711. () => {
  712. file.id = res.data.id;
  713. file.url = res.data.fileUrl;
  714. file.fileName = res.data.fileName;
  715. delete file.status;
  716. delete file.message;
  717. },
  718. () => {
  719. file.status = "failed";
  720. file.message = "上传失败";
  721. }
  722. );
  723. },
  724. () => {
  725. file.status = "failed";
  726. file.message = "上传失败";
  727. }
  728. );
  729. }
  730. };
  731. const testForm = ref(null); // 延迟使用,因为还没有返回跟挂载
  732. watch(
  733. formData.value,
  734. (val) => {
  735. emit("update:modelValue", val);
  736. },
  737. {
  738. deep: true,
  739. }
  740. );
  741. watch(
  742. () => formData.value[btnConfigCopy.prop],
  743. (val) => {
  744. if (formOption.value.btnConfig !== undefined && !formOption.value.btnConfig.isNeed && val && val.length > 0) {
  745. formDataListShowLabel();
  746. }
  747. }
  748. );
  749. const changeCheckboxGroup = (form, label, data, fieldNames) => {
  750. let text = "";
  751. if (form[label] && form[label].length > 0) {
  752. form[label].map((item) => {
  753. let list = data.filter((itemData) => itemData[fieldNames.value] === item);
  754. if (list && list.length > 0) {
  755. if (text) {
  756. text = text + "," + list[0][fieldNames.text];
  757. } else {
  758. text = list[0][fieldNames.text];
  759. }
  760. }
  761. });
  762. }
  763. form[label + "Name"] = text;
  764. };
  765. defineExpose({
  766. formDataShowLabelOne,
  767. formDataListShowLabelOne,
  768. btnConfigCopy,
  769. });
  770. // onMounted(() => {});
  771. </script>
  772. <style lang="scss" scoped>
  773. .btn-box {
  774. margin-top: 10px;
  775. width: 100%;
  776. text-align: center;
  777. }
  778. .row {
  779. color: #999999;
  780. background: #ecebeb;
  781. height: 32px;
  782. line-height: 32px;
  783. display: flex;
  784. justify-content: space-between;
  785. align-items: center;
  786. padding: 0 15px;
  787. }
  788. ._title {
  789. font-size: 14px;
  790. font-weight: 700;
  791. }
  792. ::v-deep {
  793. .multipleChoice {
  794. margin-bottom: 60px;
  795. .van-checkbox {
  796. justify-content: center;
  797. margin: 10px 40px;
  798. }
  799. }
  800. .van-form {
  801. margin-top: 0px !important;
  802. }
  803. }
  804. </style>