Vue学习及总结

Vue学习

Vue学习的准备知识-HTML ,JS,CSS

Vue学习的准备知识-ES6语法

Vue学习的准备知识-原型与原型链

Vue学习的准备知识-Promise

Vue学习的准备知识-Axios

学习地址 https://axios.nodejs.cn/docs/intro 或 https://axios.js.cn/

Vue前端显示后端异常信息

    cancelUser() {
      cancel().then((res) => {
        if (res.msgCd === '001' || res.msgCd === '002') {
          this.logout().then(() => { console.log('logout successfully', res) })
        } else {
           // 后端错误,显示后端异常信息
          this.$message.error(res.msgInfo)
          this.tip = false
          this.loading = false
        }
      }).catch(() => {
        this.tip = false
        this.loading  = false
      })
    },

Vue组件引入和使用

  1. 定义如下:

    <template>
    	...
    	<!-- 引入组件时,取了别名,使用时以大写字母为界[大写变小写,加-];
    		别称是AModal;使用组件时为<a-modal>  -->
    	<a-modal>
            ...
    	</a-modal>
    </template>
    <script>
     import { Modal,} from 'ant-design-vue'
      ...
     export default {
        // 文件名和这里的name保持一致,单词首字母都大写
     	name: 'OtherTips',
        components: {
            // 引入组件时,可以取别名;AModal是Modal的别称【别称在前,组件名在后】
        	AModal: Modal,
     	},
     	props: {
            // 组件的属性值
        	otherTypeVisible: {
        	  type: Boolean,
        	  default: false,
        	},
            ...
     	},
         ...
     }
    </script>
    
    
  2. 组件使用

    <template>
    	... 
    	<!-- 引入时组件名为OtherTypeTips,使用时如下:大写变小写,加-分割符
    		:other-type-visible 设置组件自己的属性值
    		ref 在<script></script>中使用时,渲染组件
    	-->
    	<other-type-tips :other-type-visible="otherTypeVisible" ref="otherTypeTips" @cancel="cancelBus"></other-type-tips>
    </template>
    <script>
    	import OtherTypeTips from'@/components/OtherTypeTips.vue'
        export default {
      		name: 'CheckedBusForBid',
      		components: {
               // 声明引入的组件
      		  OtherTypeTips,
      		  ...
      		},
            methods: {
                goPay(params) {
                    switch(params) {
                        case '03':
                            // 调接口传回的方式是其他
                            this.otherTypeVisible = true
                            // 使用this.$refs.otherTypeTips获取到组件
                         	// init是组件初始化,组件中有init这个方法 
                            this.$refs.otherTypeTips.init(this.indexId)
                            break
                        default:
                            this.$message.warning('请选择')
                            break
                    }
                }
            }
            ...
        }
    </script>
    

ant-design-vue组件库

  1. 页面弹出弹窗需求,ant-design-vue没有对应的Dialog弹窗组件, 使用的是AModal组件【弹窗组件】

只显示字段或隐藏

<a-col :span="12">
  <a-form-item label="金额:" class="form-item left-col">
    <a-input
   	// disabled为原生属性值, :disabled为vue的属性值,可以通过js代码来控制a-input是否可填
      :disabled="true"
      autocomplete="new"
      suffix="元"
    />
  </a-form-item>
</a-col>

问题及解决方案[基于Vue2]

  1. Vue中常用的属性

    1. 单向绑定使用: 号,比如 columns为原始元素,:columns=“fromColumns” fromColumns为JS中定义的columns

       <a-table
              :pagination="false"
              :columns="fromColumns"
              :loading="loading"
              :data-source="frameData"
              :rowKey="(record) => record.id"
              bordered
            >
       </a-table>
      
    2. 双向绑定使用v-model

      <a-col :span="12">
      	<upload
      		// ref 绑定自己编写的helloLicense组件
                      ref="helloLicense"
      		// v-model 来实现与JS中的license进行双向绑定
                      v-model="license"
                      label="***"
                      required
                      custom-type="13"
                      :max-size="10"
                      :params="uploadParams"
                      tips="支持:png,jpg,jpeg,docx,pdf,zip格式,文件大小最大为10M"
                    >
           </upload>
      </a-col>
      
    3. 元素的属性值,要使用JS的值,要用``来包裹 或者在属性中直接用,同时,要进行单向绑定

      1. js中用``包裹

        // 标签如下
        <a-tab-pane :tab="bidTabText">
         </a-tab-pane>
        
        // js代码中如下
        `hello(${this.bidData.length})`
        
      2. 在元素属性中直接使用

        <a-form-item :label="`中国${list.length > 1 ? index+1 : ''}名称`">
        </a-form-item>
        
  2. 在VSCode中,文件名字大小写更新之后; 重新引入会报错

    1. 报错信息:Already included file name …
    2. 报错原因: 因为文件在VSCode中已经存在并缓存了;所以报这个错误
    3. 解决办法: 快捷键:Ctrl + shift + P,打开:“命令面板”,输入:重新加载
  3. 出现eslint循环依赖的错误

    1. 报错信息:Dependency cycle via @/components/CheckedBusDialog:13eslintimport/no-cycle

    2. 报错原因: 在components下的组件,引入components的其他组件,只能一个个引入。不同同时引入多个;

    import { CheckedBusForBid, CheckedBusForPerform } from '@/components'
    
    1. 解决办法: 一个个分别引入
    import CheckedBusForBid from '@/components/CheckedBusForBid'
    import CheckedBusForPerform from '@/components/CheckedBusForPerform'
    
  4. 出现ESLint的简写错误

    1. 出现的错误: Expected property shorthand

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    2. 错误原因:引入组件的时候,别称与组件名不能完全一致,否则会出现这个错误

    3. 解决方法:(1)不起别称 或(2)取一个不同名字的别称

  5. 数组循环遍历并退出循环

    1. 使用for循环数组,并退出【退出循环时,暂时推荐使用】
     const visibleList = ['false', 'true', 'true']
     for (let i = 0; i < visibleList.length; i += 1) {
       if (visibleList[i] === 'true') {
        	this.visible = visibleList[i]
        	// 退出循环                                   
        	break
       }
     }
    
    1. 使用Foreach【退出循环时,暂不推荐使用此方法】
    try {
        const visibleList = ['false', 'true', 'true']
        // forEach遍历数组
        visibleList.forEach((item) => {
          if (item === 'true') {
            throw new Error('false')
          }
        })
     } catch (e) {
        if (e.message === 'false') {
          return
        }
     }
    
    1. 使用some
    const visibleList = ['false', 'true', 'true']      
    visibleList.some((v) => {
      if (v === 'true') {
        return true
      }
      return true
     })
    
    1. 使用every
    const visibleList = ['false', 'true', 'true']      
    visibleList.every((item) => {
     if (item === 'true') {
       this.visible = item	
    	// 退出循环
          return false
      }
       return true
    })
    
    1. 参考文档

    2. https://www.cnblogs.com/lemperor/p/17271948.html

    3. https://cloud.tencent.com/developer/article/2094721

    4. https://www.zhihu.com/tardis/zm/art/601625100?source_id=1005

  6. Promise对象有if-else时,要在else中添加resolve(true)或reject()结束Promise对象

     getBidUnpayList() {
          return new Promise((resolve, reject) => {
            if (this.getRegisterResource === '01') {
              this.loading = true
              unPayListQuery(this.getEgId).then((res) => {
                if (res.waitPaymentList.length > 0) {
                  this.bidUnpayData = res.waitPaymentList
                  this.data = res.waitPaymentLists
                  this.loading = false
                  this.visibleList.push(true)
                } else {
                  this.visibleList.push(false)
                  this.loading = false
                  this.$store.commit(REMOVE_EGID)
                }
                // 用resolve结束
                resolve(true)
              }).catch((error) => {
                reject(error)
                console.log('获取失败', error)
                this.loading = false
                this.visibleList.push(false)
                this.$store.commit(REMOVE_EGID)
              })
            } else {
              // else中也利用resvole结束掉
              resolve(true)
            }
          })
        }
    
  7. 利用Promise.all同步接口调用

    Promise.all([
               // 获取bid待办列表1
               this.getBidUnpayList(),
               // 获取待办列表2
               this.getPerformUnpayList(),
               // 获取待办列表3
               this.getFrameUnpayList(),
             ]).then((res) => {
               // 设置投标bid tab的数据
               this.bidData = this.bidUnpayData
               // 设置bid tab显示文本
               this.bidTabText = this.bidData ? `投标(${this.bidData.length})` : 0
               // 设置perform tab的数据
               this.performData = this.performUnpayData
               // 设置perform tab显示文本
               this.performTabText = this.performData ? `履约(${this.performData.length})` : 0
               // 设置frame tab的数据
               this.frameData = this.frameUnpayData
               // 设置frame tab的显示文本
               this.frameTabText = this.frameData ? `年度(${this.frameData.length})` : 0
               // 获取弹窗最终显示与否
               for (let i = 0; i < this.visibleList.length; i += 1) {
                 if (this.visibleList[i] === 'true' || this.visibleList[i] === true) {
                   this.visible = this.visibleList[i]
                   break
                 }
               }
             })
    
  8. ant-design-vue 是用Tab-Pane,属性tab显示js的内容。可以用:tab(单向绑定,tab是tab-pane的原属性;:tab就变成了vue的属性,实现单向绑定)

    注:标签属性中,需要用到JS中的动态数据和属性,都可以使用:属性名="JS代码"的形式,在JS中使用js代码 来包裹js代码

    1. vue template中的代码为: :tab=“bidTabText”

      <!--default-active-key 指定默认的显示tab,:tab="bidTabText" 单向绑定js代码-->
      <a-tabs default-active-key="bid" @change="checkBidTab">
       <a-tab-pane key="bid" :tab="bidTabText">
          <checked-bus-for-bid :check-visible="checkVisible" :bidData="bidData" 		ref="checkedBusForBid" @handleVisible="handleVisible">
          </checked-bus-for-bid>
       </a-tab-pane>
         ...
      </a-tabs>
      
    2. vue script中的代码

       // 设置bid tab显示文本,使用``来包括js代码
      this.bidTabText = this.bidData ? `投标(${this.bidData.length})` : 0
      
  9. 保存或者提交, 校验数据,如果没有,则报错;不进行保存或提交;使用return;

    methods:{
        handleSave(){
            ...
    	const tempContractList = this.contractList.filter(
            (item) => item && item.contractName)
    	 if (tempContractList.length === 0) {
             // 页面弹出错误信息
        	 this.$message.error('合同名称不能为空')
         	// 结束handleSave方法
    		return
    	 }
        }
    }
    
  10. ant-design-vue 1.0(适用于vue 2.0版本),在form表单中,使用v-for遍历list,并且用到了a-input标签,在a-input中无法同时使用v-model(双向绑定), v-decorator(双向绑定,校验信息);因为有必填校验,因此,最终使用v-decorator,不使用v-model; 但list无法回显。最终解决如下:

    1. 先在data()中定义list的初始值,不然,页面不能显示

       // 合同名称List
          contractList: [{
            contractName: undefined,
          }],
      
    2. 在template中的form中,遍历contractList, 用div元素,包裹元素

      <div v-for="(item, index) in contractList" :key="index">
                <a-row>
                  <a-form-item
      			// custom-horizontal-layout 来控制当前<a-form-item>为水平布局
                    class="custom-horizontal-layout left-col"
      			// 这样显示合同名称1:`` 包裹
                    :label="`合同名称${index+1}:`"
                  >
                    <a-input
      			  // :disabled, 利用vue函数来控制是否只读或可以输入
                      :disabled="disabledByAppKey()"
                      class="form-item"
      			// 对输入的值,进行处理【index表示list下标,$event表示输入的事件】
                      @input="handleInputChange(index, 'contractName', $event)"
      				// 	`contractList[${index}].contractName` 来表示唯一性,使用`${index}`也能表示唯一性; rules.contractNameCheck,在rules中定义的contractNameCheck的rule规则
                      v-decorator="[`contractList[${index}].contractName`, rules.contractNameCheck]" />
                     // 添加合同的图标元素 通过@click来监听事件,添加合同名称
                    <a-icon
                      type="plus-circle"
                      style="margin-left: 22px; font-size: 25px"
                      @click="addContract()"/>
                     // 删除合同的图标元素 通过@click来监听事件,删除合同名称
                    <a-icon
      			 // 当元素数量大于1时,才显示删除图标
                      v-if="index>0"
                      type="minus-circle"
                      style="margin-left: 11px; font-size: 25px"
                      @click="delContract(index)" />
                  </a-form-item>
                </a-row>
              </div>
      

      注:

      :disabled(单向绑定),来控制是否可以输入;@input(event事件监听),来处理输入的值

      v-decorator(双向绑定)来进行对元素进行校验处理;

    3. 在rules中添加contractNameCheck的rule。 rules是放在data()中,

      data(){
          return {
              rules: {
               // 引入的公共rules
              ...rules,
              contractNameCheck: {
                rules: [
                  { required: true, message: '请输入合同名称' },
                ],
                // 公共的rule(引入使用),类似trigger:blur,内容如下:
                // export const commonInputRule = {
        		 // validateTrigger: 'blur',
        		// validateFirst: true,}
                ...commonInputRule,
              },
            }
          }
      }
      
    4. 在methods方法中,添加disabledByAppKey(),handleInputChange(index, ‘contractName’, $event),addContract(),delContract()等方法

      methods: {
          ... 
           // 通过appKey控制disable
          disabledByAppKey() {
            return this.appKey ? !(this.appKey === '***') : false
          },
          // 增加合同名称
          addContract() {
            if (this.contractList.length > 2) {
              this.$message.error('最多只能添加3个合同名称')
              return
            }
            // push 添加对象
            this.contractList.push({ contractName: undefined })
            // 通过setFieldsValue把list赋值给template要遍历的contractList
            this.form.setFieldsValue({ contractList: this.contractList })
          },
          // 删除合同名称
          delContract(index) {
            if (this.contractList.length > 1) {
              // splice通过index删除list中的对象
              this.contractList.splice(index, 1)
              // 通过setFieldsValue把list赋值给template要遍历的contractList
              this.form.setFieldsValue({ contractList: this.contractList })
            }
          },
         	// 合同名称输入框值改变
          handleInputChange(index, field, event) {
           // field表示传入的属性值,通过event事件,event.target.value来获取输入的值
      	this.contractList[index][field] = event.target.value
      	},
      }
      
    5. 保存之后,点击编辑进来,要回显数据,要遍历并通过const obj,挨个设置【重点】

      1. watch监听数据变化

        watch:{
            info:{
                handler(val){
                    // $nextTick表示立即钓调用,this.setInfo为方法
                    if(val) this.$nextTick(()=>this.setInfo(val))
                }
                // 立即渲染
               immediate: true,
                // 进深渲染
        	   deep: true, 
            }
        }
        
      2. 在methods的setInfo方法中

        setInfo(detail){
           // 设置合同名称
         if (detail.contractNameList) {
          // detail.contractNameList为List<String>的list,把list转化为是对象类型的JS数组,使用map
              this.contractList = detail.contractNameList.map((item) => ({ contractName: item }))
             // 要使用this.$nextTick,立即执行。
                this.$nextTick(() => {
                    // 遍历js的list, 不能直接设置,如: 
                    // this.form.setFieldsValue({contractList, this.contractList}),这样的话,只会显示第一条数据
                    this.contractList.forEach((item, index) => {
                    const obj = {}
                    // 采用如下方式,根据下标设置每个值,才能获取所有的list的数据
                    obj[`contractList[${index}]`] = this.contractList[index]
                    this.form.setFieldsValue(obj)
                  })
                })
              }
        }
        
    6. 显示结果如下:

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  11. 如果column前面添加必填的红色*号,但子元素不是input输入框的话,可以使用新样式来解决

    1. template中的代码如下:

       <a-row>
                <a-col :span="12">
                  <!-- label-required 是用来显示红色 *-->
                  <a-form-item label="名称1:" class="form-item left-col label-required">
                    <p class="content">{{ name2 }}</p>
                  </a-form-item>
                </a-col>
                <a-col :span="12">
                  <a-form-item label="名称2:" class="form-item label-required">
                    <p class="content">{{ name2 }}</p>
                  </a-form-item>
                </a-col>
              </a-row>
      
    2. 在页面用element找到element找到对应的元素和对应的style信息

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    3. 在style中添加红色的*号样式

      // /deep/ 表示二级元素,要用这个
      /deep/ .label-required {
          // ::before表示在之前,添加样式
        .ant-form-item-label label::before {
          display: inline-block;
          margin-right: 4px;
          color: #f5222d;
          font-size: 14px;
          font-family: SimSun, sans-serif;
          line-height: 1;
          content: '*';
      }
      }
      
    4. 调整之后,效果如上图的截图的名称2一样

    5. 如果想在元素之前添加红色*号,因为不是二级元素,不用/deep/

      .span-required::before {
          display: inline-block;
          margin-right: 4px;
          color: #f5222d;
          font-size: 14px;
          font-family: SimSun, sans-serif;
          line-height: 1;
          content: '*';
      }
      
  12. 父组件与之组件之间进行单向数据传递

    1. 父组件引入子组件

      <script>
         import PerformDialog from '@components/PerformDialog'
      	
      	export default{
              name: "Dialog"
              components: {
          		PerformDialog,
        		},
              data() {
              	return {
                     performData: [] 
                  }
         	 	}
          }
      </script>
      
    2. 父组件通过 :performData 传递数据给子组件

      <a-tab-pane key="peformance" :tab="performTabText">
         <perform-dialog :performData="performData" ref="performDialog"></perform-dialog>
      </a-tab-pane>
      
    3. 在子组件中的props中,定义performData,接收来自父组件的数据

      export default {
      	name:"PerformDialog"
          ... 
          props: {
          	performData:{
      			type: Array,
            		default() {
              		return []
           		 },
      		}
      	}
      }
      
  13. 父组件与子组件之间进行双向数据传递

    1. 和上面一样,首先引入子组件;

    2. 在父组件使用子组件的时候,传入值;【使用visible属性;父组件创建事件监听@handleVisible=“handleVisible” handleVisible来处理子组件传回的visible值】

      <a-tab-pane key="peformance" :tab="performTabText">
         <perform-dialog :visible="visible" @handleVisible="handleVisible" ref="performDialog"></perform-dialog>
      </a-tab-pane>
      
    3. 父组件创建的handleVisible方法

      methods: {
          ... 
          // 接收子组件visible的值,并控制父组件弹窗是否显示
          handleVisible(value) {
            if (value === false) {
              this.visible = value
            }
          },
      }
      
    4. 子组件接收父组件传入的值,

      export default {
      	name:"PerformDialog"
          ... 
          props: {
          	visible: {
            		type: Boolean,
            		default: false,
          	},
      	}
      }
      
    5. 并利用this.$emit()方法处理,传回更改之后的值给父组件

      methods:  {
          busiHandle(record) {
              ...
              if(record){
                 this.visible = false
                  // handleVisible父组件定义的监听事件
                  this.$emit('handleVisible', this.visible)
              }
          }
      }
      
    1. 子组件接收父组件传入的值,

      export default {
      	name:"PerformDialog"
          ... 
          props: {
          	visible: {
            		type: Boolean,
            		default: false,
          	},
      	}
      }
      
    2. 并利用this.$emit()方法处理,传回更改之后的值给父组件

      methods:  {
          busiHandle(record) {
              ...
              if(record){
                 this.visible = false
                  // handleVisible父组件定义的监听事件
                  this.$emit('handleVisible', this.visible)
              }
          }
      }
      
  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值