回头看自己的代码,犹如鸡肋!!!里面有很多问题,建议大家不要看。我没时间整理╮(╯▽╰)╭

跨域解决方案

  config/dev.env.js

  1. 'use strict'
  2. const merge = require('webpack-merge')
  3. const prodEnv = require('./prod.env')
  4.  
  5. module.exports = merge(prodEnv, {
  6. NODE_ENV: '"development"',
  7. API_ROOT: '"/api"'
  8. })

  config/prod.env.js ,生产的服务器(你线上运行时的服务器)

  1. 'use strict'
  2. module.exports = {
  3. NODE_ENV: '"production"',
  4. API_ROOT: '"http://api.xxx.com/"'
  5. }

  config/index.js

  1. proxyTable: {
  2. '/api': {
  3. // target: 'https://www.xxx.com/',
  4. // target: 'http://m.xxx.com/',
  5. target: 'http://api.xxx.com/',
  6. changeOrigin: true,
  7. secure: false,
  8. pathRewrite: {
  9. '^/api': ''
  10. }
  11. }
  12. },
  13.  
  14. // Various Dev Server settings
  15. // host: 'xxx.xxx.xx.x', // can be overwritten by process.env.HOST //公司本地IP
  16. host: 'localhost', // can be overwritten by process.env.HOST
  17. port: 8085, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined

  接口请求的时候

  1. export const _HomeNavList = params => {
  2. return req('post', rootUrl+ '/xxxx/xxxxx/xxxxx/xxx',params)
  3. }

rem设置

  有多种方式,可以 js,也可以 css 设置。
  鉴于 H5 的浏览器都比较高级,可以使用一些最新的属性,这里先介绍 css 的写法。 
  1. /*rem设置*/
  2. html{
  3. font-size: calc(100vw/7.5); /*1rem=100px*/
  4. }

  js 设置 rem,如下。

  1. (function (doc, win) {
  2. var docEl = doc.documentElement,
  3. // 手机旋转事件,大部分手机浏览器都支持 onorientationchange 如果不支持,可以使用原始的 resize
  4. resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
  5. recalc = function () {
  6. //clientWidth: 获取对象可见内容的宽度,不包括滚动条,不包括边框
  7. var clientWidth = docEl.clientWidth;
  8. if (!clientWidth) return;
  9. docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
  10. };
  11. recalc();
  12. if (!doc.addEventListener) return;
  13. //注册翻转事件
  14. win.addEventListener(resizeEvt, recalc, false);
  15. })(document, window);

axios 的封装

  刚开始我也是设置 axios 的 baseURL ,以及在拦截器里把数据用 qs 序列化。后来又改回来了。

  最后有个需求是要把图片上传到七牛云(后面会讲),那么 axios 就不能在拦截器里设置了。

  1. import axios from 'axios'
  2. import qs from 'qs'
  3.  
  4. axios.defaults.timeout = 5000;
  5. // axios.defaults.baseURL = process.env.API_ROOT; //填写域名
  6.  
  7. //http request 拦截器
  8. axios.interceptors.request.use(
  9. config => {
  10. // config.data = qs.stringify(config.data);
  11. config.headers = {
  12. 'Content-Type': 'application/x-www-form-urlencoded'
  13. }
  14. return config;
  15. },
  16. error => {
  17. return Promise.reject(err);
  18. }
  19. );
  20.  
  21. //响应拦截器即异常处理
  22. axios.interceptors.response.use(response => {
  23. return response
  24. }, err => {
  25. if (err && err.response) {
  26. switch (err.response.status) {
  27. case 400:
  28. console.log('错误请求')
  29. break;
  30. case 401:
  31. console.log('未授权,请重新登录')
  32. break;
  33. case 403:
  34. console.log('拒绝访问')
  35. break;
  36. case 404:
  37. console.log('请求错误,未找到该资源')
  38. break;
  39. case 405:
  40. console.log('请求方法未允许')
  41. break;
  42. case 408:
  43. console.log('请求超时')
  44. break;
  45. case 500:
  46. console.log('服务器端出错')
  47. break;
  48. case 501:
  49. console.log('网络未实现')
  50. break;
  51. case 502:
  52. console.log('网络错误')
  53. break;
  54. case 503:
  55. console.log('服务不可用')
  56. break;
  57. case 504:
  58. console.log('网络超时')
  59. break;
  60. case 505:
  61. console.log('http版本不支持该请求')
  62. break;
  63. default:
  64. console.log(`连接错误${err.response.status}`)
  65. }
  66. } else {
  67. console.log('连接到服务器失败')
  68. }
  69. return Promise.resolve(err.response)
  70. })
  71.  
  72. // 通用公用方法
  73. export const req = (method, url, params) => {
  74. return axios({
  75. method: method,
  76. url: url,
  77. data: qs.stringify(params) ,
  78. // traditional: true,
  79. }).then(res => res.data);
  80. };

底部菜单栏

  mint-ui的底部菜单栏,我个人觉得用的不是很习惯,找了很多资料才勉强填上这个坑,如下:

  1. <mt-tabbar class="bottom-tab" v-model="tabSelected">
  2. <mt-tab-item id="home">
  3. <span class="iconfont icon-zhuye"></span>
  4. <p>首页</p>
  5. </mt-tab-item>
  6. <mt-tab-item id="study">
  7. <span class="iconfont icon-xianshanghuodong"></span>
  8. <p>学习</p>
  9. </mt-tab-item>
  10. <mt-tab-item id="ask">
  11. <span class="iconfont icon-kefu"></span>
  12. <p>咨询</p>
  13. </mt-tab-item>
  14. <mt-tab-item id="user">
  15. <span class="iconfont icon-wode"></span>
  16. <p>我的</p>
  17. </mt-tab-item>
  18. </mt-tabbar>

  路由嵌套,底部 tabbar 是第一层组件,点击底部元素,可切换不同模块

  1. {
  2. path: '/bottomTab',
  3. component: bottomTab,
  4. children: [{
  5. path: '/home',
  6. name: 'home',
  7. component: home,
  8. meta: {
  9. keepAlive: true,
  10. }
  11. },
  12. {
  13. path: '/study',
  14. name: 'study',
  15. component: study,
  16. meta: {
  17. RequireLogin: true
  18. }
  19. },
  20. ...
  21. }

  最后我是没有设置默认选中,默认的是组件创建的时候的路由名称,然后在路由变化时,直接 watch 监控路由的名称,并作出相关操作

  1. export default {
  2. data() {
  3. return {
  4. tabSelected: "",
  5. routerPath: ""
  6. };
  7. },
  8. created() {
  9. this.tabSelected = this.$route.path.slice(1);
  10. },
  11. mounted() {},
  12. beforeDestroy() {},
  13. watch: {
  14. $route(to, from) {
  15. if (
  16. to.name == "home" ||
  17. to.name == "study" ||
  18. to.name == "ask" ||
  19. to.name == "user"
  20. ) {
  21. this.routerPath = this.$route.path;
  22. this.tabSelected = this.routerPath.slice(1);
  23. }
  24. },
  25. tabSelected: function(val, oldVal) {
  26. this.$router.push({
  27. name: val
  28. });
  29. }
  30. },
  31. methods: {},
  32. computed: {}
  33. };

返回上一页

  顶部返回上一页,有的需求是直接发个详情页的链接给别人,然后客户在点击返回的时候,是没有本站的浏览记录的。那么可能会退出到一个空白页,造成不必要的客户流失,我们的需求是让他去首页或者本站的其他页面,留住客户。

  1. <mt-header :title="headTitle">
  2. <mt-button icon="back" slot="left" @click="goBack">返回</mt-button>
  3. </mt-header>

  浏览记录最少要有2条,否则去首页。我刚开始写的1,最后发现空白页也是记录。

  1. methods: {
  2. goBack() {
  3. if (window.history.length <= 2) {
  4. this.$router.push({ path: "/" });
  5. return false;
  6. } else {
  7. this.$router.back();
  8. }
  9. }
  10. },

路由守卫

  有些页面是需要登录了之后才能进的,这样的页面如果多了,就可以用路由守卫来判断

  1. router.beforeEach((to, from, next) => {
  2. // 登录页、不需要登陆的和已登录的页面直接跳转
  3. if (to.path == "/login" || !to.meta.RequireLogin || localStorage.getItem("user")) {
  4. next();
  5. } else {
  6. next({
  7. path: "/login",
  8. query: {
  9. redirect: to.fullPath
  10. }
  11. })
  12. }
  13. })

  路由守卫跳转过来的登录页,是带参的(目标页面的路径)在登录成功之后,就自动进入目标页面

  1. if (r.Code == 0) {
  2. this.LOGIN(r.Data)
  3. if (this.$route.query.redirect) {
  4. this.$router.push({
  5. path: this.$route.query.redirect
  6. });
  7. } else {
  8. this.$router.push("/");
  9. }
  10. }

列表页上拉加载,下拉刷新

  课程列表页做了这个功能,待验证,使用  mt-loadmore

视频格式 m3u8

  这个格式的视频还是挺多的,但是实现播放的话,就有点复杂了(要装两个插件,还一堆问题),折腾了两天,这速度算快还是慢呢。

  1. import 'video.js/dist/video-js.css'
  2. import 'vue-video-player/src/custom-theme.css'
  3. import videojs from 'video.js'
    //得手动绑定对象,不然找不到
  4. window.videojs = videojs
    //这里写成 import 还不行,必须得 require
  5. require('videojs-contrib-hls/dist/videojs-contrib-hls');

  写元素的时候,可以不用写 source

  1. <video id="video-wrap" class="video-js vjs-custom-skin vjs-big-play-centered">
  2. <!-- <source
  3. src="http://xxx.m3u8"
  4. type="application/x-mpegURL"
  5. > -->
  6. </video>

  如果多格式类型的视频,可能需要自动判断

  1. mounted() {// 创建播放器
  2. this.videoPlay = videojs('video-wrap', {
  3. playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
  4. autoplay: false, //如果true,浏览器准备好时开始播放
  5. muted: false, //默认情况下将会消除任何音频
  6. loop: false, //导致视频一结束就重新开始
  7. preload: 'auto', //建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
  8. aspectRatio: '4:3', // 16:9 不会自动放中间
  9. // fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
  10. // width:720,
  11. // height:540,
  12. notSupportedMessage: '此视频无法播放',
  13. controls: true,
  14. // sources: [{
  15. // // type: "application/x-mpegURL", //video/mp4
  16. // // type: "", //video/mp4
  17. // // src: "http://37273.long-vod.cdn.aodiany210ad87667dd544439b28733e.m3u8",
  18. // }],
  19. // bigPlayButton: true,
  20. // textTrackDisplay: false,
  21. // posterImage: true,
  22. // errorDisplay: false,
  23. // controlBar: true
  24. })
  25. },
  26. watch: {
  27. onlineVideoVideoId(){
  28. this.videoPlayUrl(this.onlineVideoInfo)
  29. }
  30. },
  31. methods: {
  32. videoPlayUrl(info) {
  33. // 视频观看
  34. // console.log(info);
  35. if (this.user) {
  36. if (info.bought == true) {
  37. // 获取课程视频
  38. _StageItemPlayUrl({
  39. courseId: this.$route.query.id,
  40. memberId: this.user.id,
  41. classId: info.videoId,
  42. }).then(res => {
  43. // console.log(res)
  44. if (res.Code === "0") {
  45. this.showVideoPlayer = true;
  46. this.changeVideo(res.Data.positiveUrl)
  47. }
  48. }).catch((err) => {
  49. console.log(err)
  50. })
  51. } else {
  52. console.log('没有购买')
  53. this.$toast({
  54. message: '请购买课程',
  55. position: 'bottom',
  56. duration: 2000
  57. });
  58. }
  59. } else {
  60. this.$router.push({
  61. path: '/login',
  62. query: {
  63. redirect: to.fullPath
  64. }
  65. })
  66. }
  67. },
  68. changeVideo(vdSrc) {
  69. // 切换视频路径及类型
  70. if (/\.m3u8$/.test(vdSrc)) {
  71. this.videoPlay.src({
  72. src: vdSrc.replace("http://", "https://"),
  73. type: 'application/x-mpegURL'
  74. })
  75. } else {
  76. this.videoPlay.src({
  77. src: vdSrc,
  78. type: 'video/mp4'
  79. })
  80. }
  81. this.videoPlay.load();
  82. this.videoPlay.play(); //pause()暂停 销毁 dispose()
  83. },
  84. },
  85. beforeDestroy() {
  86. this.videoPlay.dispose();
  87. console.log("video destroy");
  88. }

  在 build/webpack.base.conf.js 的 moudel 拿了要加上 noParse: [/videojs-contrib-hls/],不然可能会报错 t is not definded 之类的错误。

  但是我在移动端没写上面这个操作,也播放成功了,PC端写了。现在不确定这段代码是否有必要。

  1. module: {
  2. noParse: [/videojs-contrib-hls/],
  3. rules: [
  4. {
  5. test: /\.vue$/,
  6. loader: 'vue-loader',
  7. options: vueLoaderConfig
  8. },
  9. ...

mt-radio 的使用

  需求是做一个单选的数据列表,但是 mt-radio 的 options 的数据结构是 label 和 value ,其中 label 是显示的名称, value 是值。

  那么我们的数据结构就也得是 value 和 label 了,如果不是,就必须得手动转换。

  1. Object.values(this.majorList).map(value => {
  2. value.value = value.id;
  3. value.label = value.majorName;
  4. // console.log(value)
  5. // console.log(this.majorList)
  6. });

自适应多层级目录

  使用迭代,

  调用:

  1. <DetailMultiMenu
  2. v-for="(classItem,index) in this.classListData"
  3. :key="index"
  4. :item="classItem"
  5. @videoInfo="videoPlayUrl"
  6. ></DetailMultiMenu>

  组件:

  1. <template>
  2. <ul class="multi-menu">
  3. <li v-if="item.stageName == ''">
  4. <div
  5. class="class-title-wrap"
  6. @click="toggleItemList = !toggleItemList"
  7. :style="{marginLeft: 0.3*(item.level-1) +'rem'}"
  8. >
  9. <span>
  10. <i class="iconfont icon-caidan"></i>
  11. <span>{{item.classjName}}</span>
  12. </span>
  13. <i :class="[toggleItemList?'iconfont icon-xiangshang':'iconfont icon-xiangxia']"></i>
  14. </div>
  15.  
  16. <ul v-for="(child,index) in item.sub" :key="index" v-show="toggleItemList">
  17. <DetailMultiMenu v-if="child.stageName == ''" :item="child" :key="child.classjName"></DetailMultiMenu>
  18. <li
  19. v-else
  20. :key="child.id"
  21. :class="activeLi+index == child.id+index ? 'activeLi': ''"
  22. @click="videoInfo(child.id,child.isPay,child.id)"
  23. >
  24. <div class="class-item-wrap" :style="{marginLeft: 0.3*(child.level-1) +'rem'}">
  25. <span class="class-title">
  26. <i class="iconfont icon-zhibo11"></i>
  27. <span>{{child.classjName}}</span>
  28. </span>
  29. </div>
  30. </li>
  31. </ul>
  32. </li>
  33.  
  34. <li v-else class="class-item-wrap">
  35. <div>
  36. <i class="iconfont icon-zhibo11"></i>
  37. {{item.classjName}}
  38. </div>
  39. </li>
  40. </ul>
  41. </template>
  42. <script>
  43. import {
  44. // mapGetters,
  45. mapMutations
  46. // mapState
  47. } from "vuex";
  48. export default {
  49. name: "DetailMultiMenu",
  50. data() {
  51. return {
  52. toggleItemList: false,
  53. activeLi: -1
  54. };
  55. },
  56. props: {
  57. item: {
  58. type: Object,
  59. required: true
  60. }
  61. },
  62. computed: {},
  63. created() {},
  64. mounted() {},
  65. methods: {
  66. ...mapMutations([
  67. "ONLINE_VIDEO_VIDEOID",
  68. "ONLINE_VIDEO_BOUGHT",
  69. "ONLINE_VIDEO_PLAY"
  70. ]),
  71. videoInfo(itemId, bought, videoId) {
  72. if (bought) {
  73. this.activeLi = itemId;
  74. }
  75. this.ONLINE_VIDEO_BOUGHT(bought);
  76. this.ONLINE_VIDEO_VIDEOID(videoId);
  77. // this.ONLINE_VIDEO_PLAY(true);
  78. // this.$emit("videoInfo",{bought, videoId} );
  79. }
  80. },
  81. watch: {},
  82. components: {}
  83. };
  84. </script>
  85. <style scoped>
  86. /* .activeLi {
  87. background: #26a2ff;
  88. } */
  89. /* 课程目录 */
  90. .multi-menu {
  91. font-size: 0.3rem;
  92. }
  93. .class-title-wrap {
  94. border-bottom: 1px solid #ddd;
  95. line-height: 1rem;
  96. height: 1rem;
  97. display: flex;
  98. justify-content: space-between;
  99. }
  100. .class-item-wrap {
  101. border-bottom: 1px solid #ddd;
  102. overflow: hidden;
  103. line-height: 1rem;
  104. height: 1rem;
  105. display: -webkit-box;
  106. /*! autoprefixer: off */
  107. -webkit-box-orient: vertical;
  108. /* autoprefixer: on */
  109. -webkit-line-clamp: 1;
  110. overflow: hidden;
  111. white-space: pre-line;
  112. font-size: 0.24rem;
  113. }
  114. </style>

行内切换 class 名

  1. <i :class="[toggleItemList?'iconfont icon-xiangshang':'iconfont icon-xiangxia']"></i>
  2.  
  3. <li
  4. v-else
  5. :key="child.id"
  6. :class="activeLi+index == child.id+index ? 'activeLi': ''"
  7. @click="videoInfo(child.id,child.isPay,child.id)"
  8. ></li>

行内样式自动计算

  1. <div class="class-item-wrap" :style="{marginLeft: 0.3*(child.level-1) +'rem'}"></div>

七牛云上传图片

  1. <input
  2. type="file"
  3. id="avatarUpload"
  4. ref="imgInput"
  5. accept="image/*"
  6. @change="PreviewImage"
  7. >

  可以不用七牛云的插件,但要手动创建一个 formData,并且设置请求头

  1. PreviewImage(event) {
  2. let file = event.target.files[0];
  3. let formData = new FormData();
  4. formData.append("file", file);
  5. formData.append("token", this.qiniutoke);
  6. this.$http({
  7. url: "https://up-z2.qiniup.com",
  8. method: "POST",
  9. headers: { "Content-Type": "multipart/form-data" },
  10. data: formData
  11. })
  12. // _UploadQiniu({
  13. // file:file,
  14. // token:this.qiniutoke
  15. // })
  16. .then(res => {
  17. console.log(res);
  18. this.currentUser.handUrl = res.data.url + res.data.key;
  19. console.log(this.currentUser.handUrl);
  20. })
  21. .catch(err => {
  22. console.log(err);
  23. });
  24. }

vue 移动端项目总结(mint-ui)的更多相关文章

  1. 从零开始搭建vue移动端项目到上线的步骤

    初始化项目 1.在安装了node.js的前提下,使用以下命令 npm install --g vue-cli 2.在将要构建项目的目录下 vue init webpack myproject(项目目录 ...

  2. 从零开始搭建vue移动端项目到上线

    先来看一波效果图 初始化项目 1.在安装了node.js的前提下,使用以下命令 npm install --g vue-cli 2.在将要构建项目的目录下 vue init webpack mypro ...

  3. Vue移动端项目模板

    一个集成移动端开发插件的Vue移动端模板包含1.css: 使用stylus开发css 集成reset样式文件 修改UI组件文件 统一样式处理(如主题色等)2.UI组件 使用热门的vant与mint-u ...

  4. Vue移动端项目总结

    使用Vue项目写了一个移动端项目,然后又把项目硬生生的抽离了组件,一直忙着写RN项目没有时间总结心得,今天上午终于下定决心,写点总结. 1.position:absolute: 定位的时候不同手机的浏 ...

  5. vue 移动端项目切换页面,页面置顶

    之前项目是pc端是使用router的方式实现置顶的 //main.js router.afterEach((to, from, next) => { window.scrollTo(0, 0) ...

  6. vue PC端项目中解决userinfo问题

    在vue2 中用脚手架建立的项目,后端提供接口获取数据.在公司做第一个项目的时候不清楚公司里的对接流程,结果后续代码被一个接口整的乱七八糟,这个接口是获取用户信息的接口——'usre/info'. 如 ...

  7. 将Vue移动端项目打包成手机app---HBuilder

    将移动端页面打包成app 1.使用 HBuilder 直接编译打包 点击左上角 文件>打开目录>选择目录  选择用Webpack打包好的dist文件目录 由于我添加到项目了,所以会显示该项 ...

  8. vue移动端项目

    用vue mint-ui  jquery-weui写了一个移动端demo 技术栈 vue2.0 vue-router axios mint-ui jquery-weui webpack 页面截图 最后 ...

  9. vue移动端项目在手机上调试

    1.电脑和手机要连同一个wifi  一定是复制无线网的IP,而不是以太网的IP 2.在你的电脑上查找无线网络的ipv4地址: 查找方法:windows+r   然后再输入框里输入cmd 回车 会出现这 ...

随机推荐

  1. @EnableFeignClients 注解

    feignClents在spring容器里找不到的原因 当使用的feignClents 来自引用别的工程时,需要指定包名,如果不指定就算使用ComponentScan 扫描也不行 import org ...

  2. RecyclerView的Item的单击事件

    RecyclerView 的每个Item的点击事件并没有像ListView一样封装在组件中,需要Item的单击事件时就需要自己去实现,在Adapter中为RecyclerView添加单击事件参考如下: ...

  3. commons-lang3之StringUtils

    字符串是一种在开发中经常使用到的数据类型,对字符串的处理也变得非常重要,字符串本身有一些方法,但都没有对null做处理,而且有时可能还需要做一些额外处理才能满足我们的需求,比如,要判断某个字符串中是否 ...

  4. 有关CSS的overflow和border-radius的那些事,你的圆角被覆盖了吗?

    事件起因 最初是网友的一个提问,来自于我的知识星球社区: 说实话,不得不佩服这个网友的眼力,这么小的细节都能发现.不过这也正是 FineUI 一直前进的动力,来自社区的监督和促进. 从截图上看,貌似圆 ...

  5. Carthage下没有Build文件夹

    问题描述: 用Carthage管理项目时,执行Carthage upate --platform iOS后发现Carthage目录下没有Build文件夹 解决方案: 在Xcode > Prefe ...

  6. RMQ区间最大值与最小值查询

    RMQ复杂度:建表$O\left ( nlgn \right ) $,查询$O\left ( 1 \right )$ ll F_Min[maxn][20],F_Max[maxn][20]; void ...

  7. 腾讯通信云服务端使用心得,腾讯云IM

    腾讯通信云服务端使用心得 1.腾讯通信服务入口并创建应用 方便使用保留url地址 :   https://cloud.tencent.com/product/im 注册账号腾讯云账号->通过审核 ...

  8. Photoshop调出田园照片唯美手绘油画效果

    先看看效果图 原片分析:妹子脸上的光不够通透,有些灰暗,整体色调不够分明. 后期思路:色彩往油画风格调整,让画面色彩更加油润.丰富. 基础调整 (1)曝光根据照片的实际情况进行调整 (2)增加阴影部分 ...

  9. linux 下ab压力测试

    1.ab的简介 ab是apachebench命令的缩写. ab是apache自带的压力测试工具.ab非常实用,它不仅可以对apache服务器进行网站访问压力测试,也可以对或其它类型的服务器进行压力测试 ...

  10. 2016湖南省赛 [Cloned]

    A.2016 给出正整数 n 和 m,统计满足以下条件的正整数对 (a,b) 的数量: 1. 1≤a≤n,1≤b≤m; 2. a×b 是 2016 的倍数. Input   输入包含不超过 30 组数 ...