Bootstrap

Vant2+Vue2前端项目经验总结

vant:
vant中文官网
使用版本 “version”: “2.13.2”

一、引入vant组件
全局引入按需引入
①在项目路径中的plugins目录下新建vant.js文件,里面写入需要的组件

// 按需全局引入 vant组件(常用的,没有的自行添加)
import Vue from 'vue'
import {
  Button,
  List,
  Cell,
  Tabbar,
  TabbarItem,
  Toast,
  Notify,
  Dialog,
  DatetimePicker,
  Popup,
  DropdownMenu,
  DropdownItem,
  Col,
  Row,
  CellGroup,
  Tab,
  Field,
  Tabs,
  Uploader,
  Picker,
  Search,
  ImagePreview,
  Image as VanImage,
  Icon,
  Sticky,
  TreeSelect,
  PullRefresh,
  Swipe,
  SwipeItem,
  Progress,
  Rate,
  Pagination,
  Checkbox,
  CheckboxGroup,
  SubmitBar,
  Step,
  Steps,
  RadioGroup,
  Radio,
  Tag,
  ActionSheet,
  Form,
  Collapse,
  CollapseItem,
  Grid,
  GridItem,
  Sidebar,
  SidebarItem,
  Empty,
  NavBar,
  Loading
} from 'vant'

Dialog.setDefaultOptions({
  confirmButtonColor: '#1989fa'
})

Vue.use(Button)
Vue.use(Cell)
Vue.use(List)
Vue.use(Tabbar).use(TabbarItem)
Vue.use(Toast)
Vue.use(Notify)
Vue.use(Dialog)
Vue.use(DatetimePicker)
Vue.use(Popup)
Vue.use(DropdownMenu).use(DropdownItem)
Vue.use(Col)
Vue.use(Row)
Vue.use(CellGroup)
Vue.use(Tab)
Vue.use(Tabs)
Vue.use(Field)
Vue.use(Uploader)
Vue.use(Picker)
Vue.use(VanImage)
Vue.use(ImagePreview)
Vue.use(Search)
Vue.use(Icon)
Vue.use(Sticky)
Vue.use(TreeSelect)
Vue.use(PullRefresh)
Vue.use(Swipe)
Vue.use(SwipeItem)
Vue.use(Progress)
Vue.use(Rate)
Vue.use(Pagination)
Vue.use(Checkbox)
Vue.use(CheckboxGroup)
Vue.use(SubmitBar)
Vue.use(Step)
Vue.use(Steps)
Vue.use(Radio)
Vue.use(RadioGroup)
Vue.use(Tag)
Vue.use(ActionSheet)
Vue.use(Form)
Vue.use(Collapse)
Vue.use(CollapseItem)
Vue.use(Grid)
Vue.use(GridItem)
Vue.use(Sidebar)
Vue.use(SidebarItem)
Vue.use(Empty)
Vue.use(NavBar)
Vue.use(Loading)

二、移动端适配工具:
①postcss-px-to-viewport
②amfe-flexible
对于①:

第一步:安装依赖 npm i postcss-px-to-viewport -D
第二步:在项目路径下:创建.postcssrc.js文件,写入

module.exports = ({ file }) => {
  return {
    plugins: {
      autoprefixer: {},
      'postcss-px-to-viewport': {
        unitToConvert: 'px', // 要转化的单位
        viewportWidth: file.includes('vant') ? 375 : 750, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750+
        // viewportWidth: 750,
        viewportHeight: 812, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
        unitPrecision: 6, // 指定`px`转换为视窗单位值的小数位数
        viewportUnit: 'vw', //指定需要转换成的视窗单位,建议使用vw
        selectorBlackList: [], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
        minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
        mediaQuery: false // 允许在媒体查询中转换`px`
      }
    }
  }
}

对于②:

第一步:npm install amfe-flexible
第二步:在main.js中引入 import 'amfe-flexible'
第三步:修改public/index.html
<meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no,viewport-fit=cover'>

注意:配置完后需要重启项目才会生效;另外适配不支持行间适配,即不能这样写样式:<div style='width:200px;height:200px;'></div> 这样不生效,在浏览器中不会被解析为rw 除非自己写的时候就写rem或rw单位,建议行间仅写颜色样式

三、如果需要添加项目上下文,需要修改vue.config.js中的publicPath值
一般设置为./,此时,需要在dist里面打包。
假设设置为/hsa-h5/,则需要在打包为dist后,将dist重命名为hsa-h5,压缩成hsa-h5.zip,再重命名为hsa-h5.war

四、在移动端无法使用gap,gap:可以设置相邻兄弟之间的间距,在父元素设置;在web端可以使用

五、如果在移动端有使用echarts的情况,echarts中的fontSize, itemHeight,itemWidth,barWidth 均需要手动将px转rem或vw等

六、文件上传
新建imageUpload.vue 文件上传组件

<template>
  <div>
    <van-uploader
      ref='upload'
      :accept='accept'
      :before-read='beforeRead'
      :after-read='afterRead'
      :before-delete='beforeDelete'
      :max-size='10 * 1024 * 1024'
      :max-count='maxCount'
      @oversize='onOversize'
      @click-preview='click_preview'
      v-model='imageList'
      type='file'
    >
      <slot name='upload'></slot>
    </van-uploader>
  </div>
</template>

<script>
import axios from 'axios'
import { downloadFile } from '@/utils/fileutil'
export default {
  props: {
    value: {
      default: undefined
    },
    //文件大小
    maxSize: {
      type: Number,
      default: 1024 * 1024 * 10
    },
    //  上传的文件类型
    accept: {
      type: String,
      default: '*'
    },
    maxCount: {
      type: Number,
      default: 1
    },
    //  是否禁用
    disabled: {
      type: Boolean,
      default: false
    },
    //上传地址
    url: {
      type: String,
      default: process.env.VUE_APP_BASE_URL + '/web/fileapi/uploadFile'
    }
  },
  data() {
    return {
      imageList: [], // 上传完成后接口返回的路径
      uploader: [] // 上传完成组件返回的文件信息
    }
  },
  methods: {
    /**
     * @param imageList 后台返回文件地址,多个文件地址用,隔开
     */
    initFileList(imageList) {
      if (!imageList) return
      const imageListTemp = imageList.split(',')
      imageListTemp.forEach(url => {
        const arr = url.split('/')
        this.imageList.push({
          url: arr[arr.length - 1],
          response: {
            data: url
          },
          data: url,
          status: 'done'
        })
      })
    },
    //  上传之前要做的校验
    beforeRead(file) {
      const fileSize = file.size / 1024 / 1024; // 将文件大小转换为MB
      const fileType = file.type

      if (fileSize > 10) {
        this.$toast('文件大小不能超过10MB')
        return false
      }
      //.doc .docx .pdf
      if (fileType === 'application/msword'
        || fileType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        || fileType === 'application/pdf') {
        return true;
      } else {
        this.$toast('请上传.doc、.docx或.pdf格式文件');
        return false;
      }
      return true;
    },
    // 上传文件请求
    afterRead(files) {
      // 上传状态
      files.status = 'uploading'
      files.message = '上传中...'
      // 转换成接口需要的格式
      const formData = new FormData()
      formData.append('text', 'temp')
      formData.append('file', files.file)
      // 请求接口
      // uploaderFile是封装请求,这个可以自己写一个请求方式
      let config = {
        headers: {
          //添加请求头
          'Content-Type': 'multipart/form-data'
        }
      }
      axios
        .post(this.url, formData, config)
        .then((res) => {
          //如果不知道res的值,建议打印一下
          if (res.length > 0) {
            // 请求成功修改为成功状态
            files.status = 'done'
            files.url = res
            files.data = res
            // 把接口返回的数据放到数组里面
            // 转换成以逗号隔开的格式   如果不需要去掉即可
            // this.$emit('input', this.imageList.join(','))
            this.$emit('fileChange', this.imageList)
          } else {
            // 请求未成功  状态修改为失败
            files.status = 'failed'
            files.message = '上传失败'
          }
        })
        .catch((err) => {
          console.log(err)
        })
    },
    // 文件超过大小
    onOversize(file) {
      console.log(file)
      this.$toast('文件大小不能超过 10M')
    },
    /**文件删除 */
    beforeDelete(file, detail) {
      const index = this.imageList.indexOf(file);
      if (index !== -1) {
        this.imageList.splice(index, 1);
      }
      this.$emit('fileChange', this.imageList)
      console.log('删除后imageList为:', this.imageList)
    },
    /* 文件下载 */
    click_preview(file) {
      let fileUrl = file.response && file.response.data
      if (fileUrl) {
        const urls = fileUrl.split('/')
        downloadFile(urls[urls.length - 1])
      }
    },
    // 点击确定
    psnTypeConfirm(val) {
      this.formData.psnType = val.value
    }
  }
}
</script>

使用:

          <van-cell class='upload_file'>
            <template #title>
              上传附件
            </template>
            <template #right-icon>
              <div class='fujian_con'>
                <image-upload ref='upload' v-model='formData.fileList'  @fileChange="handleFileChange">
                  <!-- 利用插槽修改上传样式 不写的话就是vant默认的样式 -->
                  <template slot='upload'>
                    <img class='fujianImg' src='@/assets/img/file/upload.png' alt=''>
                  </template>
                </image-upload>
              </div>
            </template>
          </van-cell>
//在methods中
    handleFileChange(fileList) {
      if (fileList.length > 0) {
        this.formData.fileAddr= fileList[0].url
      } else {
        this.formData.fileAddr= ''
      }
    },
;