1绪商、select/datepicker/asacader 組件菜單框顯示時苛谷,滾動頁面,菜單框不隨父元素滾動格郁,而是根據(jù)body頁面滾動
原因: ant默認(rèn)append到body
解決: 通過組件的屬性getXxxContainer 修改渲染的父元素
<template>
<a-select
v-decorator="['highestEdu']"
class="highestEdu"
placeholder="請選擇最高學(xué)歷"
:getPopupContainer="() => doc.getElementsByClassName('highestEdu')[0]">
<a-select-option
v-for="edu in eductionOptions"
:value="edu"
:key="edu"> {{ edu }}</a-select-option>
</a-select>
</template>
<script>
export default {
data () {
return {
doc: document
}
}
}
// 自定義封裝級聯(lián)組件遇到直接父組件綁定class無效或直接級聯(lián)組件綁定class腹殿,會出現(xiàn)菜單項一點就消失的問題,需要包裹一層div綁定
// provinceCity.js
<template>
<div :class="popupContainer">
<a-cascader
:options="provinceCity"
:placeholder="placeholder"
v-model="city"
@change="handleCascader"
:getPopupContainer="getPopupContainer"></a-cascader>
</div>
</template>
<script>
export default {
props: {
value: { type: Array },
placeholder: {
type: String
},
popupContainer: {
type: String
}
},
methods: {
getPopupContainer () {
return document.getElementsByClassName(this.popupContainer)[0]
}
}
</script>
// form.vue
<ProvinceCity
v-decorator="['nativePlace']"
placeholder="請選擇籍貫"
popupContainer="nativePlace"
@emitCascader="handleNativePlaceChange"/>
!!! 全局化配置
<a-config-provider :getPopupContainer="getPopupContainer">
<router-view style="min-width: 992px;"/>
</a-config-provider>
// script
methods: {
getPopupContainer (triggerNode) {
// 觸發(fā)節(jié)點 指某個頁面的select例书,tooltip锣尉,menu等含彈框顯示的組件,這些組件顯示下拉框會觸發(fā)該方法决采,不包括datapicker自沧,可全局配置
return triggerNode
}
}
2、ant 內(nèi)引入moment树瞭,日期均返回moment
頁面使用直接引入moment暂幼、不需要npm i
import moment from 'moment'
moment組件值轉(zhuǎn)化
this.form.date.format('YYYY-MM-DD')
3、form表單里upload選擇xlsx文件(只選擇一份移迫,后選擇的會覆蓋前選擇文件)旺嬉,最后提交上傳(整個form表單form-data處理)
// http.js
postByForm (url, params) {
return this.Axios.post(url, params, {
headers: {
'Content-type': 'multipart/form-data'
}
}).catch(e => {
return Promise.reject(new ApiError(
'network error', -1
))
})
}
// apis.js
// 所有傳的參數(shù)formData化
export async function updateMultiMember ({accountID, upload}) {
const formData = new FormData()
upload.forEach((file) => {
formData.append('upload[]', file)
})
formData.append('accountID', accountID)
formData.append('appkey', Global.appkey)
formData.append('channel', Global.channel)
formData.append('chainTokenArray[]', [Global.chainToken])
let res = await saveAgents(formData)
return res
}
// upload.vue
<template>
<ContentWithFooter
@emitCommit="handleUpload">
<a-form
class="multi-upload-form"
:form="multiForm">
<a-form-item
v-bind="formItemLayout"
label="選擇文件">
<a-upload
accept='.xlsx'
name="upload"
:remove="handleRemove"
:beforeUpload="beforeUpload"
v-decorator="['upload',{
valuePropName: 'fileList',
getValueFromEvent: normFile,
rules: [{required: true, message: '請選擇文件'}]
}]">
<a-button>上傳文件</a-button>
</a-upload>
</a-form-item>
</a-form>
</ContentWithFooter>
</template>
<script>
import { Utils } from '@/common'
import ContentWithFooter from '@/components/content-with-footer'
export default {
name: 'member-multi',
components: { ContentWithFooter },
data () {
return {
formItemLayout: Utils.setFormLayout(2, 22),
multiForm: this.$form.createForm(this),
fileList: []
}
},
methods: {
handleRemove (file) {
const index = this.fileList.indexOf(file)
const newFileList = this.fileList.slice()
newFileList.splice(index, 1)
this.fileList = newFileList
},
beforeUpload (file) {
this.fileList = [file]
return false // return false 實現(xiàn)手動上傳
},
handleUpload () {
this.multiForm.validateFields((err, values) => {
if (!err) {
// 這邊出現(xiàn)一個就是,直接傳values.upload,會出現(xiàn)類型錯誤厨埋,雖然打印出來的東西一樣邪媳,但不再是File類型
this.$emit('emitMultiFormSubmit', {upload: this.fileList})
}
})
},
normFile (e) {
if (Array.isArray(e)) {
return e
}
// return e && e.fileList
return e && [e.file] // 如果單單一個upload組件,不在form表單里,則直接beforeUpload方法里this.fileList = [file]則會實現(xiàn)替換文件雨效,只顯示一條記錄的動態(tài)效果迅涮,但form表單里需要在這個方法里設(shè)置
}
}
}
單純的upload組件流程都是正常的:
- 上傳后返回的文件類型是正常的File類型
- beforeUpload或者change事件里直接代碼
this.fileList = [file]
都可以實現(xiàn)選擇文件后 上傳列表永遠(yuǎn)只顯示最后選擇的文件
但是,放入form表單徽龟,不再是uplaod組件上:fileList="fileList"
叮姑,而是通過
v-decorator="['upload',{ // form表單項屬性
valuePropName: 'fileList', // 表示upload組件的fileList屬性
getValueFromEvent: normFile, // 表示upload組件的fileList屬性的值
rules: [{required: true, message: '請選擇文件'}]
}
綁定獲取值,這時會出現(xiàn)2個問題
- 一般form表單處理据悔,都是在
this.multiForm.validateFields ((err, values) => {}
表單校驗通過后传透,直接取values里對應(yīng)的表單項值,但這邊取upload极颓,會出現(xiàn)類型由File變?yōu)镺bject朱盐,導(dǎo)致后端無法解析,下圖可見(前者upload綁定值菠隆,后者form取form表單項綁定值兵琳,雖然瀏覽器控制臺打印出來看上去一樣,但是類型變了:Ь丁G !一定要注意):
image.png
然后就是上傳列表永遠(yuǎn)只顯示最后選擇的文件這個過渡效果破衔,需要在form表單項的v-decorator里配置getValueFromEvent: normFile
normFile (e) {
if (Array.isArray(e)) {
return e
}
// return e && e.fileList
return e && [e.file] // 如果單單一個upload組件清女,不在form表單里,則直接beforeUpload方法里this.fileList = [file]則會實現(xiàn)替換文件运敢,只顯示一條記錄的動態(tài)效果校仑,但form表單里需要在這個方法里設(shè)置
}
}
4忠售、需要form colomu布局传惠,但每項row
image.png
<a-form :form="basicForm">
<a-form-item
label="姓名"
v-bind="formItemLayout">
<a-input
v-decorator="[
'name',
{
rules: [{required: true, message: '請輸入姓名'}]
}]"
placeholder="請輸入姓名"/>
</a-form-item>
</a-form>
<script>
const FORMLAYOUTITEM = {
labelCol: { span: 2 },
wrapperCol: { span: 8 }
}
export default {
data () {
formItemLayout: FORMLAYOUTITEM
}
}
5、table列表分頁實現(xiàn)
<a-table
:columns="columns"
:dataSource="tableData"
:loading="isTableLoading"
:rowKey="record => record.id"
:pagination="pagination" // 分頁配置
@change="handleTableChange"/> // 分頁稻扬、排序卦方、篩選變化時觸發(fā)
<script>
export default {
data () {
return {
pagination: {
showSizeChanger: true, // 顯示當(dāng)前頁顯示幾條
total: 0,
pageSize: 10,
current: 1
},
}
}
}
methods: {
handleTableChange (pagination) {
this.pagination.current = pagination.current
this.pagination.pageSize = pagination.pageSize
this.getLists()
}
}
6、form+table form搜索條件變化泰佳,table分頁當(dāng)前頁參數(shù)重置
image.png
實現(xiàn):通過ant form創(chuàng)鍵的時候設(shè)置onValuesChange這個option
memberQueryForm: this.$form.createForm(this, {onValuesChange: this.onFormValuesChange})
form任一表單項的值發(fā)生變化時的回調(diào)
7盼砍、menu 頁面刷新,保留點擊狀態(tài) + 路由跳轉(zhuǎn)逝她,菜單項選中項狀態(tài)
<template>
<div>
<a-menu
theme="dark"
mode="inline"
@openChange="onOpenChange"
width="auto"
:openKeys="openKeys"
:selectedKeys="selectedKey"
:defaultSelectedKeys="selectedKey">
<a-sub-menu
v-if="item.child"
v-for="item of sidebars"
:key="item.key">
<template slot="title">{{ item.name }}</template>
<a-menu-item
v-for="subItem of item.child"
:key="subItem.key"
@click="$router.push({name:subItem.key})">{{ subItem.name }}</a-menu-item>
</a-sub-menu>
<a-menu-item
v-if="!item.child"
v-for="item of sidebars"
:key="item.key"
@click="$router.push({name:item.key})">{{ item.name }}</a-menu-item>
</a-menu>
</div>
</template>
<script>
import { SIDERBARS } from '@/const'
export default {
name: 'Sidebar',
data () {
return {
sidebars: [],
allSubmenuKeys: [],
openKeys: [], // 展開父菜單項
selectedKey: [] // 選中子菜單項
}
},
created () {
// 頁面刷新 菜單項激活狀態(tài)
this.updateSidebars()
},
watch: {
'$route.name': function () {
// 路由跳轉(zhuǎn) 菜單項激活狀態(tài)
this.updateSidebars()
}
},
methods: {
onOpenChange (openKeys) {
// 只展示當(dāng)前父級菜單
const lastOpenKey = openKeys.find(key => this.openKeys.indexOf(key) === -1)
if (this.allSubmenuKeys.indexOf(lastOpenKey) === -1) {
this.openKeys = openKeys
} else {
this.openKeys = lastOpenKey ? [lastOpenKey] : []
}
},
updateSidebars () {
let that = this
if (this.$route.path.indexOf('admin') !== -1) {
this.sidebars = SIDERBARS.admin
} else if (this.$route.path.indexOf('operation') !== -1) {
this.sidebars = SIDERBARS.operation
} else {
this.sidebars = SIDERBARS.hr
}
_.forEach(this.sidebars, function (val, ind) {
that.allSubmenuKeys.push(val.key)
})
if (this.$route.meta.parent) {
this.openKeys = [this.$route.meta.parent]
}
if (this.$route.name) {
this.$set(this, 'selectedKey', [this.$route.name])
}
}
}
}
</script>
8浇坐、form表單編輯狀態(tài)初始化 通過配置mapPropsToFields
let options = info.chainOrgName ? {
mapPropsToFields: () => {
return {
chainOrgName: this.$form.createFormField({
value: info.chainOrgName
}),
orgDomain: this.$form.createFormField({
value: info.orgDomain
}),
description: this.$form.createFormField({
value: info.description
})
}
}
} : {}
this.nodeForm = this.$form.createForm(this, options)
},