antd form 代码模板
form 表单取值赋值
this.form.getFieldDecorator('createTimeArr', { initialValue: [moment(createDateObj.startDate, 'YYYY-MM-DD hh:mm:ss'), moment(createDateObj.endDate, 'YYYY-MM-DD hh:mm:ss')] }) this.form.getFieldDecorator('payPeriods', { initialValue: record.payPeriods }) this.form.setFieldsValue({ taxType: this.contractInfo.taxType }) this.form.getFieldValue('password') this.form.getFieldDecorator('planPayTime', { initialValue: moment(record.planPayTime, 'YYYY-MM-DD hh:mm:ss') }) this.form.setFieldsValue({ 'createTimeArr': [moment(createDateObj.startDate, 'YYYY-MM-DD'), moment(createDateObj.endDate, 'YYYY-MM-DD')] }) this.form.setFieldsValue({ planPayTime: moment(record.planPayTime, 'YYYY-MM-DD') }) this.cachePayData.planPayTime.format('YYYY-MM-DD')
下拉框初始化赋值
<a-select placeholder="请选择开票类型" v-decorator="[ 'taxType', {rules: [{ required: true, message: '请选择开票类型'}]} ]"> <a-select-option :value="0">增值税普票</a-select-option> <a-select-option :value="1">增值税专票</a-select-option> </a-select>
<script> create: { this.form.getFieldDecorator('taxType', { initialValue: this.contractInfo.taxType }) } </script>
<a-select placeholder="请选择支付单位" v-decorator="['paymentCardId',{rules: [{required: true, message: '请选择支付单位!'}]}]"> <a-select-option value="-1">全部</a-select-option> <a-select-option v-for="emPayment in emPayments" :key="emPayment.id" :value="emPayment.id">{{ emPayment.cardName }}</a-select-option> </a-select>
<script> data () { return { emPayments: [] } }, create: { queryPaymentCard({ cardState: 0 }) .then(res => { this.emPayments = [] this.emPayments = res.data }) } </script>
<a-select v-model="queryParam.createEmpId" :dropdownStyle="{ position: 'fixed' }" :options="employees" placeholder="全部"> </a-select>
<script> data () { return { employees: [{ value: '-1', label: '全部' }], } }, create: { sysEmployee.querySysEmployee({ delState: 0 }) .then(res => { this.employees = res.data this.employees.unshift({ value: '-1', label: '全部' }) }) } </script>
<!-- 下面是sql语句 --> select id value, emp_name label from sys_employee
单选框
<a-radio-group :value="taxNature" style="margin-left:24px;" name="taxNature" @change="handleTaxNature"> <a-radio-button :value="1">企业</a-radio-button> <a-radio-button :value="0">个人</a-radio-button> </a-radio-group> <script> data () { return { taxNature: 1 } }, method: { handleTaxNature (e) { this.taxNature = e.target.value } } </script>
ref属性与refs
在Vue中一般很少会用到直接操作DOM,但不可避免有时候需要用到,这时我们可以通过ref和$refs这两个来实现。
ref 被用来给DOM元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例。
关于ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们,它们还不存在!$refs也不是响应式的,因此你不应该试图用它在模板中做数据绑定。
注意:只要想要在Vue中直接操作DOM元素,就必须用ref属性进行注册。
普通DOM
通过原生 JavaScript 方式来获取:
<span id="demo">演示JS操作DOM节点</span> console.log(document.getElementById("demo").innerHTML);
通过 ref 属性来获取:
<span id="demo" ref="mydemo">演示ref操作DOM节点</span> console.log(this.$refs.mydemo.innerHTML);
添加组件
在子组件中使用 ref 属性,会将子组件添加到父组件的$refs对象中。
<create-form ref="createModal" />
$refs 中绑定有我们的 createModal 组件,而且还看到了对应的组件中的 msg属性和 show方法,那这样我们可以调用了
this.$refs.createModal.show(); console.log(this.$refs.createModal.msg);
vue中父组件调用子组件的方法并传递参数
1. 利用 $refs 将数据作为参数传递给子组件并调用子组件的方法。
父组件:
<a @click="handleEdit(record)">编辑</a> <!-- 编辑组件 --> <edit-form ref="editModal" /> handleEdit (record) { // 调用ref为editModal的组件中的add方法 this.$refs.editModal.add(record) }
子组件:
add (editObj) { this.editData = editObj }
2. 子组件中 watch(监听)父组件数据的变化。
注意:如果 watch 监听的数据不是基本类型,而是对象、数组,需要进行深度监听
监听数组
data() { return { showData: new Array(11).fill(0) } }, watch: { showData: { // 监视showData属性的变化 handler(newValue, oldValue) { // 变化后的回调函数 this.newData = newValue }, deep: true // deep为true,会监视showData的属性及属性中的对象属性变化 } }
监听对象
data() { return { bet: { pokerState: 53, pokerHistory: 'local' } } }, watch: { bet: { handler(newValue, oldValue) { console.log(newValue) }, deep: true } }
只要 bet 中的属性发生变化(可被监测到的),便会执行handler函数;
但是 watch 只能监控整个对象,如果只想监测该对象某一个具体的属性的变化,如 pokerHistory 变化时,才执行handler函数,则可以利用计算属性 computed 做中间层。
data() { return { bet: { pokerState: 53, pokerHistory: 'local' } } }, computed: { pokerHistory() { return this.bet.pokerHistory } }, watch: { pokerHistory(newValue, oldValue) { console.log(newValue) } }
注意:
- 父组件改变props,子组件如果直接使用props,会触发子组件更新。
- 父组件改变props,子组件如果将props放进data中再使用,不会触发子组件更新。
- 父组件改变props,子组件如果将props放进computed中再使用,会触发子组件更新。
- data,props和computed的变化都会触发组件更新
子组件中调用父组件中的方法,并传递参数
父组件:
<!-- 在父组件中引入的子组件 --> <create-form ref="createModal" @choose="handleChooseOk" /> // 父组件中的方法 handleChooseOk (record) { this.contractInfo.title = record.title this.contractInfo.taxNature = record.taxNature this.contractInfo.taxType = record.taxType this.contractInfo.taxCode = record.taxCode this.contractInfo.phone = record.phone this.initIm() }
子组件:
choose (record) { this.visible = false //调用父组件中的方法并传递record参数 this.$emit('choose', record) }
页面间方法调用
方法一:以员工信息管理为例,在员工信息编辑页(empEdit)中调用员工信息管理页面(empMain)的方法。
1. 在empMain 的data属性中增加字段 id, id的值应是唯一的(建议与页面的名称一致)
export default { name: 'sysEmployeeMain' data () { return { id: 'sysEmployeeMain' }
2. 在empMain 的methods中定义提供给 empEdit 调用的方法 test()
method: {
test (param){
alert(param)
}
}
3. 在empEdit的created方法中获取empMain对象:
data () { return { mainForm: {} } }, created () { this.mainForm = this.$parent.$children.find(item => item.id === 'empMain') }
4. 调用empMain中的test()方法
methods: { initDefaultValue () { this.mainForm.test(111) } }
方法二:
this.$router.push({ name: 'pmProjectContractApproval', params: { opt: 'pmProjectContractApproval', mainForm: this } })
pmProjectContractApproval.vue
data () { return { mainForm: {} } } methods: { init () { this.mainForm = this.$route.params.mainForm }, submit () { this.mainForm.refreshTable(false) } }
vue-router 携带参数跳转页面
query方式传参和接收参数
// 传参: this.$router.push({ path:'/xxx', query:{ id:id } }) // 接收参数: this.$route.query.id
params方式传参和接收参数
// 传参: this.$router.push({ name:'xxx', params:{ id:id } }) // 接收参数: this.$route.params.id
- 1.$router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
- 2.$route为当前router跳转对象,里面可以获取name、path、query、params等
nextTick()
vue是依靠数据驱动视图更新的,该更新的过程是异步的。即:当侦听到你的数据发生变化时, Vue将开启一个队列(该队列被Vue官方称为异步更新队列)。视图需要等队列中所有数据变化完成之后,再统一进行更新。
nextTick()的用法有两种:
Vue.nextTick([callback, context])
vm.$nextTick([callback])
两个方法的作用都是在DOM更新循环结束之后执行延迟回调。当我们改变了数据的时候,DOM的渲染需要时间,然而我们希望去操作DOM元素,就需要等待渲染完成后再去操作。就需要用到 nextTick,将等待DOM渲染完成后需要的操作放在回调函数里。
ant design vue中表格指定格式渲染
注意点:定义的columns一定要写在data中,否则在加载过程中由于渲染顺序会导致其中的渲染函数无法识别
指定渲染函数:
{ title: '审核状态', dataIndex: 'isReview', customRender: (text, row, index) => { if (row.isReview === 0) { return '未审核' } else { return '已审核' } } }, { title: '搜索关键词', dataIndex: 'keyword', customRender: (text, row, index) => { if (index < 4) { // 前4行数据以a标签的形式被渲染 return <a href="javascript:;">{text}</a> } return { // 否则以独占4列的文本形式被渲染 children: text, attrs: { colSpan: 4 } } } }
调用插槽模板:
<a-table :columns="columns" :dataSource="data" :pagination='pagination'> <template slot="operation"> <a-select placeholder="选择操作" style="width: 100%" @change="listHandleChange"> <a-select-option value="1">项目进度</a-select-option> <a-select-option value="2">质量管控</a-select-option> <a-select-option value="3">运维监控</a-select-option> </a-select> </template> <template slot='progress' slot-scope="text,record"> <span>{{text}}</span> <span v-if='record.progressstatus'><a-icon type="arrow-up" /></span> <span v-if='!record.progressstatus'><a-icon type="arrow-down" /></span> </template> </a-table>
{ title: '项目进度', dataIndex: 'progress', // 模板中对应的slot-scope可以用来传递参数,其中第一个参数是当前字段progress对应的值, // 第二个参数是当前字段对应的所有值对象,即整个data[n] scopedSlots: { customRender: 'progress' } }, { title: '操作', dataIndex: 'operate', scopedSlots: { customRender: 'operation' } // 直接对应插槽名为operation的模板 }
antd 多行表单操作(遇到的一个大难题)
曾经接到这样的需求:
1期款和2期款都分别有各自的本次回款和本次优惠,并且上面的本次回款金额和本次优惠金额会统计两期本次回款和本次优惠的和。
点击结清按钮,如果选是,则自动将本次回款填满并禁用该 input 组件
上图中的本次回款我使用了插槽slot来处理。
<span slot="nowBackMoney" slot-scope="text, record, index"> <!-- 一定要设置不同的key,要不然多行表单的v-decorator相同,会造成数据互相干扰 --> <a-form :key="index" :form="form"> <a-form-item style="margin-bottom: -2px"> <a-input placeholder="请输入本次回款" @change="e => handleInputChange(e.target.value, record)" v-decorator="[ 'nowBackMoney' ]" /> </a-form-item> </a-form> </span>
问题:碰到第一个难题就是输入本次回款时,上下两行数据互相干扰,输入上一行数据,下一行也自动输入了这个数据 :
解决办法:使用 key 唯一令牌解决表单值混乱
我要将各自的本次回款存到各自的对象中,这个通过监听方法监听输入的内容,并传入代表当前行数据的 record 参数就可以做到了。
// 保存输入的本次回款并计算总回款 handleInputChange (value, record) { record.nowBackMoney = value //record就是当前行的数据。这样当前行某单元格输入的数据不用太多复杂的操作就存到了各自行中的nowBackMoney属性中 var nowBackMoneySum = 0 this.data.forEach(item => { nowBackMoneySum += parseFloat(item.nowBackMoney) }) this.form.setFieldsValue({ nowBackMoneySum: nowBackMoneySum }) }
问题:点击结清按钮的时候,我发现又出现了数据同步的问题。我点上面的是,下面的也选是。代码如下:
解决办法:isBackAll 变量是我声明在data中的变量。两个按钮都用的它一个。正是因为这个产生的互相干扰。于是我又想像上面的解决方式那样利用 record 来解决这个问题。具体就是在后台对应类中定义一个变量 isBackAll,单选框的value值用record.isBackAll 的方式来赋值,这样就不会互相干扰了。
这样,利用下面的办法,就可以各自统计各自的数据了:
表格设置选中行自动选中复选框
<s-table ref="table" size="default" rowKey="id" :columns="columns" :data="loadData" :alert="options.alert" :rowSelection="options.rowSelection" :customRow="rowClick" // 点击每一行的事件 showPagination="auto" >
在 data 中定义好下面这些属性及方法
selectedRowKeys: [], selectedRows: [], options: { alert: { show: true, clear: () => { this.selectedRowKeys = [] } }, rowSelection: { selectedRowKeys: this.selectedRowKeys, onChange: this.onSelectChange } }, optionAlertShow: false, // 行点击事件 rowClick: (record, index) => ({ on: { // 注:不仅可以添加点击事件还可以添加其他的事件 click: () => { let flag = false if (this.selectedRowKeys.findIndex(item => item === record.id) >= 0) { flag = true } if (flag) { this.selectedRowKeys.splice(this.selectedRowKeys.findIndex(item => item === record.id), 1) this.selectedRows.splice(this.selectedRows.findIndex(item => item.id === record.id), 1) } else { this.selectedRowKeys.push(record.id) this.selectedRows.push(record) } } } })
这么写有个问题:选中行时只更新数组, 但不会显示选中复选框, 需要手动点击一次复选框之后点行选框才起作用, 所以要继续改进。
methods: { onSelectChange (selectedRowKeys, selectedRows) { this.selectedRowKeys = selectedRowKeys this.selectedRows = selectedRows // 在监听选择事件里加入下面这段代码即可解决这个问题 this.options = { rowSelection: { selectedRowKeys: selectedRowKeys, onChange: this.onSelectChange } } } }
数组根据YYYY-MM-DD格式时间字符串排序
changeDate () { this.resList.sort((a, b) => { let aTimeString = a.planPayTime let bTimeString = b.planPayTime aTimeString = aTimeString.replace(/-/g, '/') bTimeString = bTimeString.replace(/-/g, '/') let aTime = new Date(aTimeString).getTime() let bTime = new Date(bTimeString).getTime() return aTime - bTime // 升序排列,把它俩顺序换一下就是降序了 }) }
禁止弹出层的底层的页面随着鼠标滑动
show () { // 显示弹出层 this.visible = true this.stopMove() }, cancel () { // 关闭弹出层 this.visible = false this.move() }, stopMove () { // 禁止滚动 const m = function (e) { e.preventDefault() } document.body.style.overflow = 'hidden' document.addEventListener('touchmove', m, { passive: false }) // 禁止页面滑动 }, move () { // 恢复滚动 var m = function (e) { e.preventDefault() } document.body.style.overflow = '' // 出现滚动条 document.removeEventListener('touchmove', m, false) },
下拉框增加搜索功能
<a-select v-model="queryParam.createEmpId" placeholder="请选择创建人" showSearch optionFilterProp="children" :filterOption="this.filterOption" > // 单选下拉,搜索回调 filterOption (input, option) { return ( option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0 ) }
DatePicker 设置结束日期不可小于开始日期
<a-date-picker :disabledDate="disabledStartDate" v-model="queryParam.createTimeS" placeholder="请输入开始时间" /> <a-date-picker :disabledDate="disabledEndDate" placeholder="请输入结束时间" v-model="queryParam.createTimeE" />
disabledStartDate (createTimeS) { const createTimeE = this.queryParam.createTimeE if (!createTimeS || !createTimeE) { return false } return createTimeS.valueOf() > createTimeE.valueOf() }, disabledEndDate (createTimeE) { const createTimeS = this.queryParam.createTimeS if (!createTimeE || !createTimeS) { return false } return createTimeS.valueOf() >= createTimeE.valueOf() },
validator 方法实现表单验证
比如说验证两次密码输入是否一样
<a-form-item :label="msgTool.labelSpace('旧密码')"> <a-input v-decorator="[ 'oldPwd', {rules: [{ required: true, message: '请输入密码'}]} ]" /> </a-form-item> <a-form-item :label="msgTool.labelSpace('确认密码')"> <a-input v-decorator="['newPwd', {rules: [{validator: (rules, value, callback) =>{handleCheckPwd(rules, value, callback)}}]}]" /> </a-form-item>
handleCheckPwd (rules, value, callback) { var newPwd = this.form.getFieldValue('newPwd') var oldPwd = this.form.getFieldValue('oldPwd') if (newPwd != oldPwd) { callback(new Error('两次的密码不一样')) } else { callback() }