index.vue 31 KB

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