• element-ui实现一个动态布局的对话框


     我把组件上传到npm了,具体使用方法请看:https://gitee.com/zengyanfang/tablelist-way/blob/master/README.md#%E5%8F%82%E6%95%B0

    前言:在工作中有各种各样的对话框,最多就是填写信息这些的,一般这样的内容都是el-input输入框,el-select选择框等等之内的,这时我们就可以封装成一个组件,想要什么内容就传一个json配置,像其他组件那样,这样可以极大的简化开发,不至于每次都要去写el-input这些细微根节的东西,那问题来了

    问题:就是布局,元素少还好,要是多的话,比如有几个输入框,几个选择框,又有几个多选框等等,那就是一排排的排下来难看死了,

    解决:我自己琢磨了好久,改进了好多遍,终于是搞出来一个这样的,判断当元素也就是输入框选择框这些加起来大于4个的话就改为一行两个,如图 

     当四个的时候

    当大于四个的时候

    虽然我觉得这样也没有多好看,但比一行一个的好看多了,没办法,封装组件就是这样,细节很难各个把控到位

     有些东西就是那么长,比如这个待缴费金额,光字就五个,如果它所在行搞成两个的话,就太狭小了,所以我除了组件灵活布局外,参数还可以配置是否独占一行,所以当自己可以设置独占一行的时候问题就来了,没有外界的干扰下,我组件布局的逻辑是,元素大于4个时就添加一个class到根元素上,设置下面元素的宽度为50%,display为inline-block,再判断元素个数是否为奇数,为基数最后一个就独占一行,但是我给某个元素设置了独占一行的属性,那它的判断就不准确了,所以最后一个就不会独占一行了,而且设置的独占一行的元素在中间的话还会影响上面的元素,因为如果设置的独占一行的元素是偶数时说明它上面还有一个宽度为50%的元素,所以这个时候又要判断,总之至少要三种判断,话不多说,贴代码

    组件代码

    1. <script>
    2. import repeat from "@/components/topOperation/components/repeat.vue"
    3. import {
    4. deepClone,
    5. judgeType
    6. } from "@/utils/deepClone.js"
    7. export default {
    8. name: "dialogA",
    9. props: {
    10. options: {
    11. type: Array
    12. },
    13. rules: {
    14. type: Object
    15. },
    16. openSenior: {
    17. type: Boolean,
    18. default: false
    19. },
    20. submit: {
    21. type: Function
    22. },
    23. confrimLabel: {
    24. type: String
    25. },
    26. contentP: {
    27. type: Object,
    28. default: function() {
    29. return {}
    30. }
    31. },
    32. },
    33. model: {
    34. prop: "openSenior", //用来接收 父组件 传给子组件的值
    35. event: "openSenior-event" //用来触发的事件
    36. },
    37. watch: {
    38. openSenior: {
    39. handler() {
    40. this.openSenior1 = this.openSenior
    41. },
    42. immediate: true
    43. },
    44. openSenior1: {
    45. handler() {
    46. this.$emit("openSenior-event", this.openSenior1);
    47. },
    48. immediate: true
    49. },
    50. contentP: {
    51. handler() {
    52. this.content = this.contentP
    53. },
    54. immediate: true,
    55. deep: true
    56. },
    57. options: {
    58. handler() {
    59. let o = deepClone(this.options);
    60. let len = 0;
    61. let oo = []
    62. for (var k in o) {
    63. if (o[k].options1 && judgeType(o[k].options1) == "string") {
    64. o[k].options1 = JSON.parse(o[k].options1)
    65. }
    66. if (o[k].options2 && judgeType(o[k].options2) == "string") {
    67. o[k].options2 = JSON.parse(o[k].options2)
    68. }
    69. }
    70. for(let k in oo){
    71. if ((k < oo.length - 1 && (parseInt(k) + 1) % 2 != 0 && (oo[parseInt(k) + 1].leGes || oo[parseInt(k) + 1].wyb ||
    72. oo[parseInt(k) + 1].type == 5 || oo[parseInt(k) + 1].type == 10 || oo[parseInt(k) + 1].type == 3)) || oo[k]
    73. .leGes || oo[k].wyb || oo[k].type == 5 || oo[k].type == 10 || oo[k].type == 3) {
    74. oo[k].yes = true
    75. } else {
    76. len++;
    77. if (k == oo.length - 1 && len % 2 != 0) {
    78. oo[k].yes = true
    79. }
    80. }
    81. }
    82. this.options1 = oo;
    83. },
    84. deep: true,
    85. immediate: true,
    86. }
    87. },
    88. data() {
    89. return {
    90. content: {},
    91. openSenior1: false,
    92. options1: [],
    93. fullscreen1: false,
    94. }
    95. },
    96. computed: {
    97. fullscreen() {
    98. if (this.$store.state.app.device == 'mobile') {
    99. return true;
    100. } else {
    101. return false;
    102. }
    103. },
    104. },
    105. created() {},
    106. components: {
    107. repeat
    108. }
    109. }
    110. script>
    111. <style lang="scss">
    112. .full {
    113. float: right;
    114. margin-right: 30px;
    115. cursor: pointer;
    116. color: #909399;
    117. font-family: element-icons !important;
    118. speak: none;
    119. font-style: normal;
    120. font-weight: 400;
    121. font-variant: normal;
    122. text-transform: none;
    123. line-height: 1;
    124. vertical-align: baseline;
    125. display: inline-block;
    126. -webkit-font-smoothing: antialiased;
    127. margin-top: 1px;
    128. &:hover {
    129. color: #409EFF;
    130. }
    131. }
    132. div[_wid="wdialog"] {
    133. .ax {
    134. .el-form-item__content {
    135. width: 75% !important;
    136. }
    137. }
    138. .el-upload {
    139. width: 100%;
    140. .el-upload-dragger {
    141. width: 100%;
    142. .el-upload__text {
    143. padding: 20px;
    144. }
    145. }
    146. }
    147. .noMobile {
    148. .w100 {
    149. width: 100% !important;
    150. .el-form-item__content {
    151. width: 75% !important;
    152. .el-select {
    153. width: 100% !important;
    154. }
    155. .el-input {
    156. width: 100% !important;
    157. }
    158. }
    159. }
    160. .senior1 {
    161. width: 100%;
    162. .el-form-item {
    163. width: 100%;
    164. .el-input {
    165. width: 100% !important;
    166. }
    167. .el-form-item__content {
    168. width: 75%;
    169. }
    170. }
    171. }
    172. .el-select {
    173. width: 100%;
    174. }
    175. .senior2 {
    176. .w100 {
    177. .el-form-item__label {
    178. width: 16% !important;
    179. }
    180. }
    181. .el-form-item {
    182. width: 45%;
    183. .el-form-item__content {
    184. width: 62%;
    185. .el-input {
    186. width: 100%;
    187. }
    188. }
    189. }
    190. .el-form-item__label {
    191. width: 36%;
    192. }
    193. }
    194. .el-dialog__body {
    195. .el-form {
    196. padding: 0 30px;
    197. }
    198. }
    199. }
    200. .el-dialog__body {
    201. .el-form-item {
    202. padding: 10px;
    203. }
    204. .el-form-item__label {
    205. width: 100px;
    206. }
    207. }
    208. .footer {
    209. clear: both;
    210. float: none !important;
    211. display: block;
    212. text-align: right;
    213. width: 100% !important;
    214. position: absolute;
    215. bottom: 0;
    216. left: 0;
    217. .el-form-item__content {
    218. float: none !important;
    219. width: 100% !important;
    220. }
    221. padding: 20px !important;
    222. }
    223. .el-form-item__error {
    224. bottom: 100%;
    225. top: unset;
    226. }
    227. $tinput:35px;
    228. .el-input {
    229. width: 120px;
    230. .el-input__suffix {
    231. cursor: pointer;
    232. .el-input__icon {
    233. line-height: $tinput;
    234. }
    235. }
    236. .el-input__inner {
    237. height: $tinput;
    238. line-height: $tinput;
    239. }
    240. }
    241. .el-button {
    242. padding: 9px 15px;
    243. }
    244. .el-form {
    245. display: inline-block;
    246. .el-form-item {
    247. float: left;
    248. margin-right: 10px;
    249. margin-bottom: 0;
    250. .el-form-item__content {
    251. float: left;
    252. }
    253. .el-select__caret {
    254. line-height: 40px;
    255. }
    256. }
    257. }
    258. .el-date-editor {
    259. width: 100%;
    260. .el-range-input {
    261. width: 100%;
    262. }
    263. }
    264. }
    265. style>

    写的有点乱,如果觉得可以删的话就自己删几个,因为这是我项目里面的,还穿插一些跟其他组件联动的代码,简单解释一下

    这个是对话框组件,里面的

    repeat:统一输入框,选择框等等组件,传item里面有个type确定类型

    options:参数json数组,里面描述的就是多少个输入框,选择框这些,然后每个对象还有专门的配置

    rules:校验,我这个对话框直接是填写信息,提交信息一起搞定了

    openSenior:这个东西是控制对话框的打开和关闭,openSenior1是组件里面的,少个1的都是父组件传过来的,因为vue子组件不能修改父组件传过来的,所以子组件里面需要改的变量都要在data里面定义然后使其=父组件传过来的

    content:数据,填写的数据存放的对象,提交就是把这个对象提交过去

    fullscreen1:控制全屏

    fullscreen:是否是手机上打开的,是手机上的话铺满屏幕

    deepClone:克隆一个新对象
    judgeType:校验类型

    里面最核心的就是这段代码

    1. for(let k in oo){
    2. if ((k < oo.length - 1 && (parseInt(k) + 1) % 2 != 0 && (oo[parseInt(k) + 1].leGes || oo[parseInt(k) + 1].wyb ||
    3. oo[parseInt(k) + 1].type == 5 || oo[parseInt(k) + 1].type == 10 || oo[parseInt(k) + 1].type == 3)) || oo[k]
    4. .leGes || oo[k].wyb || oo[k].type == 5 || oo[k].type == 10 || oo[k].type == 3) {
    5. oo[k].yes = true
    6. } else {
    7. len++;
    8. if (k == oo.length - 1 && len % 2 != 0) {
    9. oo[k].yes = true
    10. }
    11. }
    12. }

    这段代码看起来很沉重,是因为我没改,哈哈,因为我想您可能用得着 

    这段代码就是控制元素是否独占一行的逻辑判断

    deepClone.js

    1. export function judgeType(obj) {
    2. // tostring会返回对应不同的标签的构造函数
    3. const toString = Object.prototype.toString;
    4. const map = {
    5. '[object Boolean]': 'boolean',
    6. '[object Number]': 'number',
    7. '[object String]': 'string',
    8. '[object Function]': 'function',
    9. '[object Array]': 'array',
    10. '[object Date]': 'date',
    11. '[object RegExp]': 'regExp',
    12. '[object Undefined]': 'undefined',
    13. '[object Null]': 'null',
    14. '[object Object]': 'object',
    15. };
    16. if (obj instanceof Element) {
    17. return 'element';
    18. }
    19. return map[toString.call(obj)];
    20. }
    21. export function deepClone(data) {
    22. const type = judgeType(data);
    23. let obj;
    24. if (type === 'array') {
    25. obj = [];
    26. } else if (type === 'object') {
    27. obj = {};
    28. } else {
    29. // 不再具有下一层次
    30. return data;
    31. }
    32. if (type === 'array') {
    33. // eslint-disable-next-line
    34. for (let i = 0, len = data.length; i < len; i++) {
    35. obj.push(deepClone(data[i]));
    36. }
    37. } else if (type === 'object') {
    38. // 对原型上的方法也拷贝了....
    39. // eslint-disable-next-line
    40. for (const key in data) {
    41. obj[key] = deepClone(data[key]);
    42. }
    43. }
    44. return obj;
    45. }

     repeat.vue

    1. <template v-if="item.type == 1">
    2. <el-select filterable :disabled="item.disabled" :style="{width: item.width}" clearable v-change:[valueTemp]="item.change" v-model="valueTemp"
    3. :placeholder="item.placeholder">
    4. <el-option v-for="item in item.options1" :key="item.value" :label="item.label" :value="item.value">
    5. el-option>
    6. el-select>
    7. template>
  • <script>
  • export default {
  • name: "repeat",
  • directives: {
  • change: {
  • // 指令的定义
  • update: function(el, binding) {
  • if (binding.value.change) {
  • binding.value(binding.arg)
  • }
  • }
  • }
  • },
  • props: {
  • value: "",
  • sign: "",
  • item: {
  • type: Object
  • },
  • },
  • model: {
  • prop: 'value',
  • event: 'change'
  • },
  • data() {
  • return {
  • valueTemp: "",
  • }
  • },
  • watch: {
  • valueTemp(val) {
  • this.$emit('change', val)
  • },
  • value: {
  • handler(val) {
  • this.valueTemp = val
  • },
  • immediate: true
  • },
  • },
  • }
  • script>
  • <style lang="scss">
  • .w100 {
  • width: 100%;
  • .el-form-item__content {
  • width: 45%;
  • }
  • }
  • div[_wid="repeat"] {
  • .el-radio {
  • margin-right: 15px;
  • }
  • .xs {
  • .el-input {
  • width: 42% !important;
  • }
  • }
  • .leGes {
  • div[_wid='numberInput'] {
  • display: inline-block;
  • margin: 1px;
  • .el-input{
  • width: 100%;
  • height: 33px;
  • line-height: 33px;
  • input{
  • height: 33px;
  • line-height: 33px;
  • }
  • }
  • }
  • .el-input__inner1{
  • position: absolute;
  • top: 0;
  • left: 0;
  • height: 35px;
  • }
  • box-sizing: border-box;
  • display: inline-block;
  • width: 220px;
  • border-radius: 4px;
  • line-height: 35px;
  • .el-select {
  • width: 20% !important;
  • .el-input {
  • width: 100% !important;
  • line-height: 35px;
  • input {
  • border: none;
  • text-align: center;
  • }
  • }
  • .el-input__suffix {
  • display: none;
  • }
  • }
  • .el-date-editor {
  • margin: 1px;
  • .el-input__prefix {
  • display: none;
  • }
  • .el-input__suffix {
  • display: none;
  • }
  • height: 33px;
  • line-height: 33px;
  • input {
  • cursor: pointer;
  • padding: 0;
  • padding-left: 20px;
  • padding-right: 20px;
  • height: 33px;
  • line-height: 33px;
  • }
  • }
  • .el-input {
  • .el-input__prefix {
  • }
  • .el-input__suffix{
  • top: 1px;
  • }
  • input {
  • border: none;
  • }
  • }
  • }
  • .close {
  • text-align: center;
  • cursor: pointer;
  • position: absolute;
  • right: 20px;
  • color: #C0C4CC;
  • top: 1px;
  • }
  • .hover{
  • border-color: #C0C4CC;
  • }
  • }
  • .leGesDrop {
  • margin: 0;
  • }
  • style>
  • 里面就实现了type=0 输入框和type=1的选择框,我自己的是有10个类型了,但涉及太多,所以就放了这两个

    使用

    1. <script>
    2. export default{
    3. data(){
    4. return{
    5. options: [{
    6. name: "name",
    7. label: "名称",
    8. placeholder: "名称",
    9. type: 0, //类型 0 输入框 1 选择框
    10. width: "120px",
    11. }, {
    12. name: "status",
    13. label: "状态",
    14. placeholder: "状态",
    15. type: 1, //类型 0 输入框 1 选择框
    16. width: "120px",
    17. options1: [{
    18. value: 0,
    19. label: "未通过"
    20. }, {
    21. value: 1,
    22. label: "通过"
    23. }],
    24. }],
    25. openSenior: false
    26. }
    27. }
    28. }
    29. script>

    这是简单示例

    options 其他配置:

    wyb:当前元素独占一行

    change:选择框值改变触发,可以传多个值,在

    v-change:[valueTemp]="{change:item.change,sign:sign}"
    

    里设置,valueTemp是当前绑定的参数,item.change是配置的函数,sign就是你想传的其他参数,在自定义指令里面这样写就行

    1. directives: {
    2. change: {
    3. // 指令的定义
    4. update: function(el, binding) {
    5. if (binding.value.change) {
    6. binding.value.change(binding.arg,binding.value.sign)
    7. }
    8. }
    9. }
    10. },

    这就有关自定义指令的学习了,也简单,想了解的可以去官网看看

    自定义指令 — Vue.js 中文文档

    好了,也没啥好说的了,最核心的就是那段判断是否独占一行的代码,另外自行扩展

  • 相关阅读:
    最近opencv又报了啥错(一)
    网络安全工程师面试题整理
    Android-短信验证码
    Web前端开发技术课程大作业: 关于美食的HTML网页设计——HTML+CSS+JavaScript在线美食订餐网站html模板源码30个页面:
    三十分钟学会zookeeper
    说说你对promise的理解?
    JL100-P100防护型拉线式位移编码器
    C++: multiple and virtual inheritance under the hood
    python中的NumPy和Pandas往往都是同时使用,NumPy和Pandas的在数据分析中的联合使用
    【mysql篇-进阶篇】SQL优化
  • 原文地址:https://blog.csdn.net/weixin_43900374/article/details/128150320