index.vue 30 KB

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