byForm.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <template>
  2. <div class="by-form" :style="{ width: attrs.width }">
  3. <el-descriptions v-if="attrs.showType == 'desc'" :title="attrs.title" :column="attrs.itemCount" :border="attrs.border"
  4. :direction="attrs.direction" :size="attrs.size ? attrs.size : 'medium'" :colon="attrs.colon"
  5. contentClassName="clas123">
  6. <template v-for="(itemChild, index) of columns">
  7. <el-descriptions-item :label="item.label" v-for="(item, _ind) of itemChild" :span="item.colspan"
  8. :key="index + '-' + _ind">
  9. <slot v-if="item.descSlot" :name='item.prop + "_desc"' :value='value'></slot>
  10. <template v-else>{{ value[item.prop] ? value[item.prop] : '--' }}</template>
  11. </el-descriptions-item>
  12. </template>
  13. </el-descriptions>
  14. <el-form class="byForm" v-else :style="{ height: attrs.height ? attrs.height + 'px' : 'auto' }" :model="value"
  15. ref="byForm" :validate-on-rule-change="false" :size="attrs.size ? attrs.size : 'small'" :inline-message="true"
  16. v-bind="$attrs" :disabled="attrs.disabled" :label-width="attrs.labelWidth ? attrs.labelWidth : '100px'"
  17. :rules="attrs.readonly ? {} : attrs.rules" :label-position="attrs.labelPosition ? attrs.labelPosition : ''">
  18. <el-row class="form-row" v-for="(itemChild, index) of columns" :key="index">
  19. <el-col v-for="(item, _ind) of itemChild" :span="item.span" :key="'itemChild' + _ind">
  20. <el-form-item class="by-form-item" :label="item.label" :rules="attrs.readonly ? [] : item.rules" :prop="item.prop"
  21. :required="item.required" :error="item.error" :size="item.size" :label-width="item.labelWidth">
  22. <component v-bind:is="item.component" class="form-comp" :propConfig="item.compConfig"
  23. :ref="item.prop + suffixCode" :parentValue="value" :propValue="value[item.prop]"
  24. @onChange="onChange($event, item)" v-bind="$attrs" v-on="$listeners" />
  25. <slot v-if="item.slot" :name='item.prop' :value='value'></slot>
  26. <!-- <slot :[item.prop]="value" ></slot> -->
  27. <div class="readonly-cover" v-if="attrs.readonly"></div>
  28. </el-form-item>
  29. </el-col>
  30. </el-row>
  31. </el-form>
  32. <!-- <el-button @click="getValueData">获取数据</el-button> -->
  33. </div>
  34. </template>
  35. <script lang="ts">
  36. /*
  37. 基础配置
  38. config:{
  39. attr:{
  40. width:'', //表单宽度
  41. showType: '' //展示形式
  42. title:'' //标题(desc的时候用)
  43. itemCount:'' //每行展示几列(desc的时候用)
  44. border:true/false //是否有边框(desc的时候用)
  45. direction: vertical / horizontal 排列的方向(desc的时候用)
  46. colon:true/false //是否显示冒号 (desc的时候用)
  47. disabled:true/false //表单禁用
  48. readonly:true/false //表单只读
  49. size:medium / small / mini, //表单域下组件的尺寸,
  50. height:'', //高度
  51. labelPosition: right/left/top //标签位置
  52. labelWidth: '' //标签的宽度
  53. rules:[] //验证规则
  54. },
  55. columns: [[{ //表单列
  56. span:'' //分栏,
  57. label:'' //标签
  58. prop:'' //字段
  59. labelWidth:'' 标签的的宽度
  60. rule:{} //验证规则
  61. size:medium / small / mini //表单域下组件的尺寸,
  62. component:'' //组件
  63. compConfig:{} //组件配置
  64. slot:true/false //插槽
  65. colspan:'' //在itemCount的基础上 列的数量(desc的时候用)
  66. descSlot:true/false //插槽(desc的时候用)
  67. }]]
  68. request:{
  69. url:'',
  70. method:'',
  71. headers:{},
  72. data:{}
  73. }
  74. }
  75. */
  76. import { Component, Prop, Vue, Watch } from "vue-property-decorator";
  77. import VueViews from '@/benyun/compVue/VueViews'
  78. @Component
  79. export default class ByForm extends VueViews {
  80. value: any = {}
  81. suffixCode = "--comp"
  82. get columns() {
  83. let columns: Array<any> = this.config?.columns ? this.config.columns : []
  84. //分栏设置
  85. if (columns.length > 0) {
  86. for (const itemChild of columns) {
  87. let spans = 24
  88. let n = itemChild.length
  89. for (const item of itemChild) {
  90. if (item && ((Number(item.span) > 0 && Number(item.span) <= 24))) {
  91. spans = spans - Number(item.span)
  92. n--;
  93. }
  94. }
  95. for (const item of itemChild) {
  96. if (Number(item.span) > 0 && Number(item.span) <= 24) {
  97. item.span = Number(item.span)
  98. } else {
  99. item.span = spans / n
  100. }
  101. }
  102. }
  103. }
  104. return columns
  105. }
  106. created() {
  107. if (this.propConfig) {
  108. this.setConfig(this.propConfig)
  109. }
  110. }
  111. mounted() {
  112. if (this.attrs.data) {
  113. this.setValue(this.attrs.data);
  114. }
  115. }
  116. getValueData() {
  117. console.log('表单数据', this.value)
  118. }
  119. //设置数据
  120. setValue(data: any) {
  121. if (data) {
  122. this.value = (this as any).$lodash.cloneDeep(data);
  123. } else {
  124. this.value = {};
  125. }
  126. this.$nextTick(() => {
  127. this.setChildrenComValue();
  128. })
  129. setTimeout(() => {
  130. this.clearValidate();
  131. }, 100)
  132. this.$forceUpdate();
  133. }
  134. clearValue() {
  135. this.value = {};
  136. this.clearChildrenComp();
  137. this.defaultHandle();
  138. setTimeout(() => {
  139. this.clearValidate();
  140. }, 100)
  141. }
  142. //获取数据
  143. getValue() {
  144. return (this as any).$lodash.cloneDeep(this.value);
  145. }
  146. //设置表单内某组件配置
  147. setCompConfig(code: any, c: any) {
  148. if (this.$refs[code + this.suffixCode] && (this.$refs[code + this.suffixCode] as any)[0] && (this.$refs[code + this.suffixCode] as any)[0].setConfig) {
  149. (this.$refs[code + this.suffixCode] as any)[0].setConfig(c)
  150. }
  151. }
  152. setConfigAfter() {
  153. this.$nextTick(() => {
  154. if (this.columns.length > 0) {
  155. for (const itemData of this.columns) {
  156. for (const item of itemData) {
  157. if (this.$refs[item.prop + this.suffixCode] && (this.$refs[item.prop + this.suffixCode] as any)[0] && (this.$refs[item.prop + this.suffixCode] as any)[0].setConfig) {
  158. (this.$refs[item.prop + this.suffixCode] as any)[0].setConfig(item.compConfig)
  159. }
  160. }
  161. }
  162. }
  163. })
  164. }
  165. //清除下级组件组件值
  166. clearChildrenComp() {
  167. for (const key in this.$refs) {
  168. if (key.indexOf(this.suffixCode) >= 0 && (this as any).$refs[key][0] && (this as any).$refs[key][0].clearValue) {
  169. (this as any).$refs[key][0].clearValue()
  170. }
  171. }
  172. }
  173. //下级组件默认值
  174. defaultHandle() {
  175. for (const key in this.$refs) {
  176. if (key.indexOf(this.suffixCode) >= 0 && (this as any).$refs[key][0] && (this as any).$refs[key][0].defaultHandle) {
  177. (this as any).$refs[key][0].defaultHandle()
  178. }
  179. }
  180. }
  181. //设置下级组件值
  182. setChildrenComValue() {
  183. for (const key in (this as any).$refs) {
  184. if (key.indexOf(this.suffixCode) >= 0 && (this as any).$refs[key] && (this as any).$refs[key][0] && (this as any).$refs[key][0].setValue) {
  185. const code = key.split(this.suffixCode)[0];
  186. if (this.value) {
  187. (this as any).$refs[key][0].setValue(this.value[code])
  188. } else {
  189. (this as any).$refs[key][0].setValue(null)
  190. }
  191. }
  192. }
  193. }
  194. //表单数据变化
  195. onChange(v: any, config: any) {
  196. let code = config.prop;
  197. if (v && (v as any).constructor === Object) {
  198. for (const key in v) {
  199. this.changeSetValue(key, v[key])
  200. }
  201. } else {
  202. this.changeSetValue(code, v)
  203. }
  204. if ((this as any).$refs.byForm) {
  205. (this as any).$refs.byForm.validateField(code);
  206. }
  207. this.$emit('formChange', { value: v, code: code })
  208. }
  209. changeSetValue(code: string, v: any) {
  210. if (this.value[code]) {
  211. this.value[code] = v;
  212. } else {
  213. Vue.set(this.value, code, v);
  214. }
  215. }
  216. //表单验证
  217. validate(parames?: any): Promise<any> {
  218. return new Promise((resolve: Function, reject: Function) => {
  219. if (!this.$refs.byForm) {
  220. resolve(true)
  221. }
  222. const failHandel = () => {
  223. if (!parames || !parames.noMsg) {
  224. (this as any).$message({
  225. message: '验证未通过,请检查!',
  226. type: 'warning',
  227. })
  228. }
  229. reject()
  230. }
  231. (this as any).$refs.byForm.validate((valid: any) => {
  232. if (valid) {
  233. let va = true
  234. for (const col of this.columns) {
  235. for (const item of col) {
  236. if (this.$refs[item.prop + this.suffixCode] && (this.$refs[item.prop + this.suffixCode] as any)[0] && (this.$refs[item.prop + this.suffixCode] as any)[0].validate) {
  237. va = (this.$refs[item.prop + this.suffixCode] as any)[0].validate()
  238. if (!va) break
  239. }
  240. }
  241. if (!va) break
  242. }
  243. if (va) {
  244. resolve(true)
  245. } else {
  246. failHandel()
  247. }
  248. } else {
  249. failHandel()
  250. }
  251. });
  252. })
  253. }
  254. //清除过滤提示
  255. clearValidate() {
  256. if (this.$refs.byForm) {
  257. (this.$refs.byForm as any).clearValidate();
  258. }
  259. }
  260. }
  261. </script>
  262. <style lang="scss" scoped>
  263. .by-form {
  264. width: 100%;
  265. margin: 0 auto;
  266. .form-comp {
  267. width: 100%;
  268. }
  269. .readonly-cover {
  270. position: absolute;
  271. height: 100%;
  272. width: 100%;
  273. left: 0;
  274. top: 0;
  275. }
  276. }
  277. </style>
  278. <style lang="scss">
  279. .by-form-item {
  280. margin-bottom: 10px !important;
  281. }
  282. </style>