index.vue 16 KB

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