index.vue 15 KB

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