ant-design-vue 是蚂蚁金服 Ant Design 官方唯一推荐的 Vue 版 UI 组件库, 它其实是 Ant Design 的 Vue 实现, 组件的风格与 Ant Design 保持同步, 组件的 html 结构和 CSS 样式也保持一致. 用下来发现它的确称得上为数不多的完整的 VUE 组件库与开发方案集成项目.
下面主要总结一些开发过程中比较耗时间去查找, 文档中没有具体说明的常见问题, 同时希望能给新上手此框架的同学提供一些参考作用.
1,Table 对接后台返回数据
针对 Table 数据格式与后他接口返回数据格式不一致问题, 修改 `@/components/table/index.js` 132 行起
主要修改 pageNo,pageSize,totalCount,data 这字段与后台返回字段一致就 OK 了
- result.then(r => {
- this.localPagination = Object.assign({}, this.localPagination, {
- current: r.pageNo, // 这里修改当前分页字段
- total: r.totalCount, // 这里修改总记录数字段
- showSizeChanger: this.showSizeChanger,
- pageSize: (pagination && pagination.pageSize) ||
- this.localPagination.pageSize // 这里修改总记录数当前页数字段
- })
- //r.data 中的 data 修改为返回列表字段
- if (r.data.length == 0 && this.localPagination.current != 1) {
- this.localPagination.current--
- this.loadData()
- return
- }
- !r.totalCount && ['auto', false].includes(this.showPagination) && (this.localPagination = false)
- this.localDataSource = r.data // 返回结果中的数组数据
- this.localLoading = false
- });
2,table 操作栏参数问题
在 table 的 dataSource 中指定的每一个数据中, 都必须包含有 name 为 key 的对象, 而显示出的数据就是相应 key 对应的数据, dataIndex 就用来声明列数据在数据项中对应的 key
然而在操作列中, 我们一般需要传入不知一项数据, 试了一下如下图配置 dataIndex, 数据并不能正确传入 slot-scope 中
- columns: [
- ...
- {
- title: '操作',
- dataIndex: 'id,text',
- key: 'id',
- scopedSlots: { customRender: 'operation' }
- }
- ]
多尝试后发现, 其实只要不配置 dataIndex 就好了...slot-scope 自定义一个字段, 自然就拿到了整行数据
3,table 分页组件展示条数
:pagination="{showTotal: total => ` 共 ${total} 条 `}"
4, 神奇的最后一个标签隐藏问题
使用可编辑 tags 过程中值得注意的问题, 一般删除某个 tag 不止是从 DOM 中删除这个 tag, 而是需要调接口修改数据, 那么这时候如果选择用修改完的数据动态渲染 tag 列表, 并且理所当然地认为动态绑定数据就不需要关心数据手动处理, 问题就出现了:
假如一共有 5 个 tag, 现在删除第一个 tag, 并调用接口返回新数据, 注意 tags 默认的删除操作也不是从 DOM 中删除这个 tag, 而是将这个 tag 设置为 ```display:none```! 这就导致了一个很神奇的问题, 此时新返回的 tags 数组长度已经 -1, 而它仍然认为当前列表的第一个 Tag 是隐藏的, 最后呈现的效果就是只剩 3 个 Tag, 此时再接着删除第一个 tag(其实是第二个), 那么就只剩 1 个 tag 了..
- <a-tag
- v-for="(tag, index) in Tags"
- :key="tag.id"
- :closable="tagCloseable"
- :afterClose="() => handleTagStatus(0,tag)"
- >{{ tag.name }}
- </a-tag>
这个问题貌似没什么好的办法, 只能放弃绑定动态数据, 判断接口调用成功后, 再用文档中的手动操作增减数据的办法:
this.Tags = this.Tags.filter(tag => tag.id !== removeTag.id)
5, 表单的各种常规操作
单独触发某个字段的校验:
this.form.validateFields(['name'], { force: true })
清除某个字段的值:
this.form.resetFields(`name`,'')
设置表单初始值:
this.form.resetFields(`name`,'')
注意: 不初始化的值用 undefined 而非'', 否则下拉框会不显示 placeholder!
自定义文件上传的 action 函数:
- <a-upload :customRequest="upLoad"></a-upload>
- upLoad (info) {
- let file = info.file;
- let param = new FormData(); // 创建 form 对象
- param.append('file',file);// 通过 append 向 form 对象添加数据
- console.log(param.get('file')); //FormData 私有类对象访问不到, 可以通过 get 判断值是否传进去
- let config = {
- headers:{'Content-Type':'multipart/form-data'}
- };
- this.$http.post(url, param, config).then(res => {
- ...
- })
- },
6, 接口跨域携带 cookie 问题
做单点登录时需要在请求头中携带 cookie, 遇到了很坑人的问题, 实际原因是对 mock.JS 的实现不够了解.
还是在 `@/src/utils/request.js`, 这里创建了 axios 实例供全局调用, 根据 axios 文档,** 在创建 ** axios 实例时添加:`withCredentials: true`
- const service = axios.create({
- baseURL: `${process.env.VUE_APP_BASEURL}/backend`,
- withCredentials: true,
- timeout: 6000 })
结果发现接口请求仍然不带 cookie, 无奈试了一下用 fetch 请求 `fetch(url, { credentials: 'include', mode: 'cors' })`, 发现可以携带 cookie, 百思不得其解, 两者都是基于 promise 实现的, 但是 fetch 在写法和拦截请求响应等方面都比较麻烦, 全部替换成 fetch 也不太现实. 最后才发现, 是 mock.JS 没有注释 (`main.js` 中注释掉就好了), 原来 mock.JS 是通过拦截 XHR 请求来实现的接口模拟, Axios 本质上也是对原生 XHR 的封装, 只不过它是 Promise 的实现版本, 所以它当然被拦截了, 而 fetch 脱离了 XHR, 这也是 fetch 请求能正常携带 cookie 的原因, 这里还没有全部梳理清楚, 打算在后一篇中详细介绍一下
7, 单点登录的实现
全局的路由钩子在 `permission.js` 中, 一般单点登录, 权限验证都是在这里处理, 这里也不例外. 没什么特别的, 需要注意的一点就是, 不要忘记对页面其他接口的统一无权限处理, 和 403 请求的响应处理. 同时画个流程图会更快一些, 这里就记录一下吧:
流程图:
路由钩子的处理:
- router.beforeEach((to, from, next) => { // 对 403 无权限的处理
- if (to.path === '/403') {
- next()
- } else { if (roles) {// 已登陆 next()
- } else { // 获取用户信息, GetUserInfo 逻辑如下:
- //status=403 && reject(res), 返回包含 status;
- //status=1005 && reject(res.data) 返回重定向的 URL;
- //status=1000 && resolve() store
- .dispatch('GetUserInfo')
- .then(res => {
- next()
- })
- .catch((e) => { if (e.status) {
- next({ path: '/403' })
- } else { // 拼接 URL 跳去登陆页, 登陆成功会重定向回当前页 (login_redirect)
- const url = e.substring(0, e.lastIndexOf('redirect')) + 'redirect=' + login_redirect
- Windows.location.href = url
- }
- })
- }
- }
- })
`@/ src/utils/request.js` 中接口返回的统一处理:
- service.interceptors.response.use((response) => { if (response.data.status === 1005){ //... 同上跳去登陆页
- }else{ // 为返回数据做统一处理
- return response.data
- }
- }, err)
8, 引入 eCharts
1)NPM install
2) components 下新建 barChart.vue ,import echarts from 'echarts', 正常操作...
3) resize 触发图表自适应
echart 有 resizeAPI, 一般是在图表组件如 barChart.vue 里面手动监听窗口 resize
- mounted() {
- Windows.addEventListener("resize", () => { this.chart.resize();
- });
- },
后面借鉴 element-admin, 利用 mixins 实现了更完善的统一处理方法:
1) 定义一个 mixin:resize.JS
- import { debounce } from '@/utils'// 防抖函数 export default {
- data() { return {
- $_sidebarElm: null
- }
- },
- mounted() { this.__resizeHandler = debounce(() => { if (this.chart) { this.chart.resize()
- }
- }, 100)
- Windows.addEventListener('resize', this.__resizeHandler) this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
- },
- beforeDestroy() {
- Windows.removeEventListener('resize', this.__resizeHandler) this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
- },
- methods: {
- $_sidebarResizeHandler(e) { if (e.propertyName === 'width') { this.__resizeHandler()
- }
- }
- }
- }
2)@/components/_utils/util.JS 中添加防抖函数
- export const debounce = (func, wait, immediate) => {
- let timeout, args, context, timestamp, result
- const later = function() { // 据上一次触发时间间隔
- const last = +new Date() - timestamp // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
- if (last <wait && last> 0) {
- timeout = setTimeout(later, wait - last)
- } else {
- timeout = null
- // 如果设定为 immediate===true, 因为开始边界已经调用过了此处无需调用
- if (!immediate) {
- result = func.apply(context, args) if (!timeout) context = args = null
- }
- }
- } return function(...args) {
- context = this
- timestamp = +new Date()
- const callNow = immediate && !timeout // 如果延时不存在, 重新设定延时
- if (!timeout) timeout = setTimeout(later, wait) if (callNow) {
- result = func.apply(context, args)
- context = args = null
- } return result
- }
- }
3)resize 监听方法混入图表组件即可
mixins: [resize]
更多前端开发 https://www.html.cn/ 知识, 请查阅 HTML 中文网 !!
来源: http://www.css88.com/qa/vue-js/14771.html