index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. <template>
  2. <div class="by-form">
  3. <el-form
  4. :model="formData"
  5. :label-width="formOption.labelWidth"
  6. :inline="formOption.inline || false"
  7. :rules="rules"
  8. :labelPosition="formOption.labelPosition || 'top'"
  9. ref="byForm"
  10. :disabled="formOption.disabled || false"
  11. >
  12. <el-form-item
  13. :label="i.label"
  14. v-for="i in formConfig"
  15. :key="i.model"
  16. :prop="i.prop"
  17. v-show="i.isShow || i.isShow == undefined"
  18. :style="
  19. i.type == 'title'
  20. ? 'width:100%'
  21. : i.itemWidth
  22. ? 'width:' + i.itemWidth + '%'
  23. : formOption.itemWidth
  24. ? 'width:' + formOption.itemWidth + '%'
  25. : '100%'
  26. "
  27. :class="i.isHide ? 'dn' : ''"
  28. >
  29. <el-input
  30. v-if="i.type == 'input'"
  31. v-model="formData[i.prop]"
  32. :placeholder="i.placeholder || '请输入'"
  33. @input="(e) => commonsEmit(e, i)"
  34. :type="i.itemType ? i.itemType : 'text'"
  35. :disabled="i.disabled ? i.disabled : false"
  36. :max="i.max"
  37. :min="i.min"
  38. :maxlength="i.maxlength"
  39. :readonly="i.readonly ? i.readonly : false"
  40. />
  41. <el-input
  42. v-if="i.type == 'selectInput'"
  43. v-model="formData[i.prop]"
  44. :placeholder="i.placeholder || '请输入'"
  45. @input="(e) => commonsEmit(e, i)"
  46. :type="i.itemType ? i.itemType : 'text'"
  47. :disabled="i.disabled ? i.disabled : false"
  48. :max="i.max"
  49. :min="i.min"
  50. :maxlength="i.maxlength"
  51. :readonly="i.readonly ? i.readonly : false"
  52. >
  53. <template #prepend>
  54. <el-select
  55. v-model="formData[i.selectProp]"
  56. :placeholder="i.selectPlaceholder || '请选择'"
  57. @change="(e) => commonsEmit(e, i)"
  58. :disabled="i.disabled ? i.disabled : false"
  59. :readonly="i.readonly ? i.readonly : false"
  60. style="width: 80px"
  61. >
  62. <el-option
  63. :label="j.title || j.name || j.label"
  64. :value="j.id || j.value"
  65. v-for="j in i.data"
  66. :key="j.id"
  67. >
  68. </el-option>
  69. </el-select>
  70. </template>
  71. </el-input>
  72. <el-select
  73. v-model="formData[i.prop]"
  74. :multiple="i.multiple || false"
  75. v-else-if="i.type == 'select'"
  76. :placeholder="i.placeholder || '请选择'"
  77. @change="(e) => commonsEmit(e, i)"
  78. :disabled="i.disabled ? i.disabled : false"
  79. :filterable="i.filterable ? true : false"
  80. :style="i.style"
  81. :readonly="i.readonly ? i.readonly : false"
  82. >
  83. <el-option
  84. :label="j.title || j.name || j.label"
  85. :value="j.id || j.value"
  86. v-for="j in i.data"
  87. :key="j.id"
  88. >
  89. </el-option>
  90. </el-select>
  91. <el-tree-select
  92. v-model="formData[i.prop]"
  93. v-else-if="i.type == 'treeSelect'"
  94. :data="i.data"
  95. :readonly="i.readonly ? i.readonly : false"
  96. :props="{
  97. value: i.propsTreeValue || 'id',
  98. label: i.propsTreeLabel || 'label',
  99. children: i.propsTreeChildren || 'children',
  100. }"
  101. value-key="id"
  102. :placeholder="i.placeholder || '请选择'"
  103. check-strictly
  104. />
  105. <el-date-picker
  106. v-model="formData[i.prop]"
  107. :readonly="i.readonly ? i.readonly : false"
  108. v-else-if="i.type == 'date'"
  109. :type="i.itemType"
  110. :placeholder="i.placeholder || '请选择时间'"
  111. @change="(e) => commonsEmit(e, i)"
  112. :disabled="i.disabled ? i.disabled : false"
  113. :format="i.format ? i.format : dateFormatInit(i.itemType)"
  114. :value-format="i.format ? i.format : dateFormatInit(i.itemType)"
  115. />
  116. <el-switch
  117. :disabled="i.disabled ? i.disabled : false"
  118. v-else-if="i.type == 'switch'"
  119. :readonly="i.readonly ? i.readonly : false"
  120. v-model="formData[i.prop]"
  121. />
  122. <el-checkbox-group
  123. v-else-if="i.type == 'checkbox'"
  124. v-model="formData[i.prop]"
  125. :readonly="i.readonly ? i.readonly : false"
  126. :disabled="i.disabled ? i.disabled : false"
  127. >
  128. <el-checkbox
  129. v-for="j in i.data"
  130. :key="j.id || j.value"
  131. :label="j.id || j.value"
  132. name="type"
  133. >
  134. {{ j.name || j.label }}
  135. </el-checkbox>
  136. </el-checkbox-group>
  137. <el-radio-group
  138. v-else-if="i.type == 'radio'"
  139. v-model="formData[i.prop]"
  140. :readonly="i.readonly ? i.readonly : false"
  141. :disabled="i.disabled ? i.disabled : false"
  142. >
  143. <el-radio
  144. :border="i.border ? i.border : false"
  145. v-for="j in i.data"
  146. :key="j.id || j.value"
  147. :label="j.id || j.value"
  148. name="type"
  149. >
  150. {{ j.name || j.label }}
  151. </el-radio>
  152. </el-radio-group>
  153. <el-input-number
  154. v-else-if="i.type == 'number'"
  155. v-model="formData[i.prop]"
  156. :readonly="i.readonly ? i.readonly : false"
  157. :placeholder="i.placeholder || '请输入'"
  158. @change="(e) => commonsEmit(e, i)"
  159. :disabled="i.disabled ? i.disabled : false"
  160. :min="i.min ? i.min : 0"
  161. :max="i.max ? i.max : 9999999999"
  162. :step="i.step ? i.step : 1"
  163. :precision="i.precision !== '' ? i.precision : 2"
  164. :controls="i.controls === false ? false : true"
  165. :style="i.style"
  166. >
  167. </el-input-number>
  168. <el-tree
  169. v-else-if="i.type == 'tree'"
  170. :data="i.data"
  171. :props="i.props"
  172. :readonly="i.readonly ? i.readonly : false"
  173. :show-checkbox="i.showCheckbox || true"
  174. >
  175. </el-tree>
  176. <el-cascader
  177. v-else-if="i.type == 'cascader'"
  178. :options="i.data"
  179. :props="i.props"
  180. :readonly="i.readonly ? i.readonly : false"
  181. :placeholder="i.placeholder || '请选择'"
  182. @change="(e) => commonsEmit(e, i)"
  183. :disabled="i.disabled ? i.disabled : false"
  184. :style="i.style"
  185. >
  186. </el-cascader>
  187. <div class="form-title" v-else-if="i.type == 'title'">
  188. {{ i.title }}
  189. </div>
  190. <slot :name="i.slotName" v-else-if="i.type == 'slot'">
  191. {{ i.slotName }}插槽占位符
  192. </slot>
  193. <div class="upload" v-else-if="i.type == 'upload'">
  194. <el-upload
  195. v-model="formData[i.prop]"
  196. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  197. :data="uploadData"
  198. list-type="picture-card"
  199. :on-remove="handleRemove"
  200. :on-success="handleSuccess"
  201. :before-upload="handleBeforeUpload"
  202. >
  203. <!-- <el-icon class="el-icon--upload"><upload-filled /></el-icon> -->
  204. <el-icon><Plus /></el-icon>
  205. </el-upload>
  206. </div>
  207. <div
  208. v-else-if="i.type == 'table'"
  209. class="by-form-table"
  210. style="width: 100%"
  211. >
  212. <el-table :data="formData[i.prop]" style="width: 100%">
  213. <el-table-column
  214. :prop="j.prop"
  215. :label="j.label"
  216. :width="i.width"
  217. v-for="(j, jindex) in i.column"
  218. >
  219. <template #default="scope" v-if="j.type">
  220. <component
  221. @change="(e) => formTableChange(e, scope, j)"
  222. v-model="scope.row[j.prop]"
  223. :is="formTableObj[j.type]"
  224. :placeholder="j.placeholder || '请输入'"
  225. :type="j.type == 'number' ? 'number' : 'text'"
  226. >
  227. <el-option
  228. :label="n.title || n.name || n.label"
  229. :value="n.id || n.value"
  230. v-for="n in j.data"
  231. :key="n.id"
  232. v-if="j.type == 'select'"
  233. >
  234. </el-option>
  235. </component>
  236. </template>
  237. </el-table-column>
  238. </el-table>
  239. </div>
  240. <div v-else-if="i.type == 'json'" class="by-form-json">
  241. <byForm
  242. :formConfig="i.json"
  243. :formOption="formOption"
  244. v-model="formData[i.prop]"
  245. ref="byform"
  246. :rules="rules"
  247. >
  248. </byForm>
  249. </div>
  250. </el-form-item>
  251. </el-form>
  252. </div>
  253. </template>
  254. <script>
  255. export default {
  256. name: "byForm",
  257. };
  258. </script>
  259. <script setup>
  260. import { set } from "@vueuse/shared";
  261. import { reactive } from "vue";
  262. defineProps({
  263. modelValue: {
  264. type: Object,
  265. default: false,
  266. },
  267. formConfig: {
  268. type: Array,
  269. default: false,
  270. },
  271. disabled: {
  272. type: Boolean,
  273. default: false,
  274. },
  275. formOption: {
  276. type: Object,
  277. default: false,
  278. },
  279. rules: {
  280. type: Object,
  281. default: false,
  282. },
  283. });
  284. const formTableChange = (e, scope, column) => {
  285. if (column.fn) {
  286. column.fn(e, scope);
  287. }
  288. console.log(formData);
  289. console.log(e, scope);
  290. };
  291. const formTableObj = {
  292. number: "el-input",
  293. text: "el-input",
  294. select: "el-select",
  295. input: "el-input",
  296. };
  297. const fileList = ref([]);
  298. const fileListCopy = ref([]);
  299. const uploadData = ref({});
  300. //文件上传相关方法
  301. const handleSuccess = (res, file, files) => {
  302. emit("update:modelValue", formData.value);
  303. };
  304. const handleRemove = (file) => {
  305. const index = fileListCopy.value.findIndex(
  306. (x) => x.uid === file.uid || x.id === file.id
  307. );
  308. fileListCopy.value.splice(index, 1);
  309. };
  310. const handleBeforeUpload = async (file) => {
  311. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  312. uploadData.value = res.uploadBody;
  313. fileListCopy.value.push({
  314. id: res.id,
  315. fileName: res.fileName,
  316. path: res.fileUrl,
  317. url: res.fileUrl,
  318. uid: file.uid,
  319. });
  320. };
  321. const isInit = ref(false);
  322. const { proxy } = getCurrentInstance();
  323. const emit = defineEmits(["update:modelValue"]);
  324. const formData = computed(() => {
  325. return proxy.modelValue;
  326. });
  327. const formDataReset = ref({ ...proxy.modelValue });
  328. const commonsEmit = (prop, item) => {
  329. if (item.fn) {
  330. item.fn(prop);
  331. }
  332. emit("update:modelValue", formData.value);
  333. };
  334. const loadInit = () => {
  335. const v = this;
  336. for (let i = 0; i < proxy.formConfig.length; i++) {
  337. const element = proxy.formConfig[i];
  338. if (element.isLoad) {
  339. commonGetdata(element.isLoad, i);
  340. }
  341. }
  342. };
  343. const dateFormatInit = (itemType) => {
  344. const formatObj = {
  345. year: "YYYY",
  346. month: "YYYY-MM",
  347. date: "YYYY-MM-DD hh:mm:ss",
  348. dates: "YYYY-MM-DD",
  349. datetime: "YYYY-MM-DD hh:mm:ss",
  350. monthrange: "YYYY-MM-DD hh:mm:ss",
  351. datetimerange: "YYYY-MM-DD hh:mm:ss",
  352. daterange: "YYYY-MM-DD hh:mm:ss",
  353. };
  354. return formatObj[itemType];
  355. };
  356. //公用递归,保证key,val统一
  357. const commonRecursive = (arr, labelKey, labelVal, childrenName) => {
  358. for (let i = 0; i < arr.length; i++) {
  359. if (labelKey == "stringArray") {
  360. arr[i] = {
  361. label: arr[i],
  362. value: arr[i],
  363. id: arr[i],
  364. title: arr[i],
  365. };
  366. } else {
  367. arr[i].title = arr[i].label = arr[i][labelKey];
  368. arr[i].id = arr[i].value = arr[i][labelVal];
  369. }
  370. if (childrenName) {
  371. arr[i].children = arr[i][childrenName];
  372. }
  373. arr[i].checked = false;
  374. typeof arr[i][labelVal] == String
  375. ? (arr[i].key = arr[i][labelVal])
  376. : (arr[i].key = JSON.stringify(arr[i][labelVal]));
  377. if (childrenName) {
  378. this.commonRecursive(
  379. arr[i][childrenName],
  380. labelKey,
  381. labelVal,
  382. childrenName
  383. );
  384. }
  385. }
  386. };
  387. //请求form表单所需数据字典
  388. const commonGetdata = (isLoad, i) => {
  389. proxy[isLoad.method](isLoad.url, isLoad.req).then((message) => {
  390. console.log(message);
  391. if (getFormat(isLoad.resUrl, message) == undefined) {
  392. console.log("请查看isLoad配置是否正确url:" + isLoad.url);
  393. return;
  394. }
  395. proxy.formConfig[i].data = getFormat(isLoad.resUrl, message);
  396. if (isLoad.labelKey) {
  397. commonRecursive(
  398. proxy.formConfig[i].data,
  399. isLoad.labelKey,
  400. isLoad.labelVal,
  401. isLoad.childrenName
  402. );
  403. }
  404. console.log(proxy.formConfig[i].data);
  405. });
  406. };
  407. //根据resurl获取数据
  408. const getFormat = (formatStr, props) => {
  409. if (!formatStr) return props;
  410. return formatStr
  411. .split(".")
  412. .reduce((total, cur) => (!total ? "" : total[cur]), props);
  413. };
  414. //初始化所有表单
  415. const formDataInit = () => {
  416. var map = {
  417. input: "",
  418. radio: null,
  419. select: null,
  420. checkbox: [],
  421. date: "",
  422. datetime: "",
  423. daterange: [],
  424. datetimerange: [],
  425. year: null,
  426. month: null,
  427. switch: false,
  428. inputNumber: 0,
  429. cascader: [],
  430. Solt: null,
  431. Transfer: [],
  432. Upload: { path: null, id: null, name: null },
  433. password: "",
  434. treeSelect: "",
  435. json: {},
  436. };
  437. const formDataCopy = { ...formData.value };
  438. for (let i = 0; i < proxy.formConfig.length; i++) {
  439. const element = proxy.formConfig[i];
  440. if (formDataCopy[element.prop] || element.type === "slot") {
  441. continue;
  442. }
  443. if (map[element.itemType] != undefined) {
  444. formData.value[element.prop] = map[element.itemType];
  445. } else {
  446. formData.value[element.prop] = element.multiple ? [] : map[element.type];
  447. }
  448. }
  449. emit("update:modelValue", formData.value);
  450. };
  451. const handleSubmit = async (onSubmit) => {
  452. try {
  453. const flag = await proxy.$refs["byForm"].validate();
  454. if (flag) {
  455. const form = { ...formData.value };
  456. proxy.formConfig.map((item) => {
  457. if (item.type == "json") {
  458. form[item.prop] = JSON.stringify(form[item.prop]);
  459. }
  460. });
  461. emit("update:modelValue", form);
  462. onSubmit();
  463. return true;
  464. }
  465. } catch (err) {
  466. console.log("请检查表单!", err);
  467. return false;
  468. }
  469. };
  470. const byform = ref(null); // 延迟使用,因为还没有返回跟挂载
  471. onMounted(() => {});
  472. defineExpose({
  473. handleSubmit,
  474. });
  475. formDataInit();
  476. loadInit();
  477. </script>
  478. <style>
  479. .form-title {
  480. font-size: 14px;
  481. font-weight: bold;
  482. margin-top: 22px;
  483. color: #333333;
  484. }
  485. .by-form .el-form--inline .el-form-item {
  486. margin-right: 0px;
  487. padding-right: 32px;
  488. box-sizing: border-box;
  489. }
  490. .dn {
  491. display: none !important;
  492. }
  493. .by-form-json .by-form .el-form .el-form-item {
  494. margin-bottom: 18px;
  495. }
  496. </style>