vue 移动端项目总结(mint-ui)
回头看自己的代码,犹如鸡肋!!!里面有很多问题,建议大家不要看。我没时间整理╮(╯▽╰)╭
跨域解决方案
config/dev.env.js
- 'use strict'
- const merge = require('webpack-merge')
- const prodEnv = require('./prod.env')
- module.exports = merge(prodEnv, {
- NODE_ENV: '"development"',
- API_ROOT: '"/api"'
- })
config/prod.env.js ,生产的服务器(你线上运行时的服务器)
- 'use strict'
- module.exports = {
- NODE_ENV: '"production"',
- API_ROOT: '"http://api.xxx.com/"'
- }
config/index.js
- proxyTable: {
- '/api': {
- // target: 'https://www.xxx.com/',
- // target: 'http://m.xxx.com/',
- target: 'http://api.xxx.com/',
- changeOrigin: true,
- secure: false,
- pathRewrite: {
- '^/api': ''
- }
- }
- },
- // Various Dev Server settings
- // host: 'xxx.xxx.xx.x', // can be overwritten by process.env.HOST //公司本地IP
- host: 'localhost', // can be overwritten by process.env.HOST
- port: 8085, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
接口请求的时候
- export const _HomeNavList = params => {
- return req('post', rootUrl+ '/xxxx/xxxxx/xxxxx/xxx',params)
- }
rem设置
- /*rem设置*/
- html{
- font-size: calc(100vw/7.5); /*1rem=100px*/
- }
js 设置 rem,如下。
- (function (doc, win) {
- var docEl = doc.documentElement,
- // 手机旋转事件,大部分手机浏览器都支持 onorientationchange 如果不支持,可以使用原始的 resize
- resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
- recalc = function () {
- //clientWidth: 获取对象可见内容的宽度,不包括滚动条,不包括边框
- var clientWidth = docEl.clientWidth;
- if (!clientWidth) return;
- docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
- };
- recalc();
- if (!doc.addEventListener) return;
- //注册翻转事件
- win.addEventListener(resizeEvt, recalc, false);
- })(document, window);
axios 的封装
刚开始我也是设置 axios 的 baseURL ,以及在拦截器里把数据用 qs 序列化。后来又改回来了。
最后有个需求是要把图片上传到七牛云(后面会讲),那么 axios 就不能在拦截器里设置了。
- import axios from 'axios'
- import qs from 'qs'
- axios.defaults.timeout = 5000;
- // axios.defaults.baseURL = process.env.API_ROOT; //填写域名
- //http request 拦截器
- axios.interceptors.request.use(
- config => {
- // config.data = qs.stringify(config.data);
- config.headers = {
- 'Content-Type': 'application/x-www-form-urlencoded'
- }
- return config;
- },
- error => {
- return Promise.reject(err);
- }
- );
- //响应拦截器即异常处理
- axios.interceptors.response.use(response => {
- return response
- }, err => {
- if (err && err.response) {
- switch (err.response.status) {
- case 400:
- console.log('错误请求')
- break;
- case 401:
- console.log('未授权,请重新登录')
- break;
- case 403:
- console.log('拒绝访问')
- break;
- case 404:
- console.log('请求错误,未找到该资源')
- break;
- case 405:
- console.log('请求方法未允许')
- break;
- case 408:
- console.log('请求超时')
- break;
- case 500:
- console.log('服务器端出错')
- break;
- case 501:
- console.log('网络未实现')
- break;
- case 502:
- console.log('网络错误')
- break;
- case 503:
- console.log('服务不可用')
- break;
- case 504:
- console.log('网络超时')
- break;
- case 505:
- console.log('http版本不支持该请求')
- break;
- default:
- console.log(`连接错误${err.response.status}`)
- }
- } else {
- console.log('连接到服务器失败')
- }
- return Promise.resolve(err.response)
- })
- // 通用公用方法
- export const req = (method, url, params) => {
- return axios({
- method: method,
- url: url,
- data: qs.stringify(params) ,
- // traditional: true,
- }).then(res => res.data);
- };
底部菜单栏
mint-ui的底部菜单栏,我个人觉得用的不是很习惯,找了很多资料才勉强填上这个坑,如下:
- <mt-tabbar class="bottom-tab" v-model="tabSelected">
- <mt-tab-item id="home">
- <span class="iconfont icon-zhuye"></span>
- <p>首页</p>
- </mt-tab-item>
- <mt-tab-item id="study">
- <span class="iconfont icon-xianshanghuodong"></span>
- <p>学习</p>
- </mt-tab-item>
- <mt-tab-item id="ask">
- <span class="iconfont icon-kefu"></span>
- <p>咨询</p>
- </mt-tab-item>
- <mt-tab-item id="user">
- <span class="iconfont icon-wode"></span>
- <p>我的</p>
- </mt-tab-item>
- </mt-tabbar>
路由嵌套,底部 tabbar 是第一层组件,点击底部元素,可切换不同模块
- {
- path: '/bottomTab',
- component: bottomTab,
- children: [{
- path: '/home',
- name: 'home',
- component: home,
- meta: {
- keepAlive: true,
- }
- },
- {
- path: '/study',
- name: 'study',
- component: study,
- meta: {
- RequireLogin: true
- }
- },
- ...
- }
最后我是没有设置默认选中,默认的是组件创建的时候的路由名称,然后在路由变化时,直接 watch 监控路由的名称,并作出相关操作
- export default {
- data() {
- return {
- tabSelected: "",
- routerPath: ""
- };
- },
- created() {
- this.tabSelected = this.$route.path.slice(1);
- },
- mounted() {},
- beforeDestroy() {},
- watch: {
- $route(to, from) {
- if (
- to.name == "home" ||
- to.name == "study" ||
- to.name == "ask" ||
- to.name == "user"
- ) {
- this.routerPath = this.$route.path;
- this.tabSelected = this.routerPath.slice(1);
- }
- },
- tabSelected: function(val, oldVal) {
- this.$router.push({
- name: val
- });
- }
- },
- methods: {},
- computed: {}
- };
返回上一页
顶部返回上一页,有的需求是直接发个详情页的链接给别人,然后客户在点击返回的时候,是没有本站的浏览记录的。那么可能会退出到一个空白页,造成不必要的客户流失,我们的需求是让他去首页或者本站的其他页面,留住客户。
- <mt-header :title="headTitle">
- <mt-button icon="back" slot="left" @click="goBack">返回</mt-button>
- </mt-header>
浏览记录最少要有2条,否则去首页。我刚开始写的1,最后发现空白页也是记录。
- methods: {
- goBack() {
- if (window.history.length <= 2) {
- this.$router.push({ path: "/" });
- return false;
- } else {
- this.$router.back();
- }
- }
- },
路由守卫
有些页面是需要登录了之后才能进的,这样的页面如果多了,就可以用路由守卫来判断
- router.beforeEach((to, from, next) => {
- // 登录页、不需要登陆的和已登录的页面直接跳转
- if (to.path == "/login" || !to.meta.RequireLogin || localStorage.getItem("user")) {
- next();
- } else {
- next({
- path: "/login",
- query: {
- redirect: to.fullPath
- }
- })
- }
- })
路由守卫跳转过来的登录页,是带参的(目标页面的路径)在登录成功之后,就自动进入目标页面
- if (r.Code == 0) {
- this.LOGIN(r.Data)
- if (this.$route.query.redirect) {
- this.$router.push({
- path: this.$route.query.redirect
- });
- } else {
- this.$router.push("/");
- }
- }
列表页上拉加载,下拉刷新
课程列表页做了这个功能,待验证,使用 mt-loadmore
视频格式 m3u8
这个格式的视频还是挺多的,但是实现播放的话,就有点复杂了(要装两个插件,还一堆问题),折腾了两天,这速度算快还是慢呢。
- import 'video.js/dist/video-js.css'
- import 'vue-video-player/src/custom-theme.css'
- import videojs from 'video.js'
//得手动绑定对象,不然找不到- window.videojs = videojs
//这里写成 import 还不行,必须得 require- require('videojs-contrib-hls/dist/videojs-contrib-hls');
写元素的时候,可以不用写 source
- <video id="video-wrap" class="video-js vjs-custom-skin vjs-big-play-centered">
- <!-- <source
- src="http://xxx.m3u8"
- type="application/x-mpegURL"
- > -->
- </video>
如果多格式类型的视频,可能需要自动判断
- mounted() {// 创建播放器
- this.videoPlay = videojs('video-wrap', {
- playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
- autoplay: false, //如果true,浏览器准备好时开始播放
- muted: false, //默认情况下将会消除任何音频
- loop: false, //导致视频一结束就重新开始
- preload: 'auto', //建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
- aspectRatio: '4:3', // 16:9 不会自动放中间
- // fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
- // width:720,
- // height:540,
- notSupportedMessage: '此视频无法播放',
- controls: true,
- // sources: [{
- // // type: "application/x-mpegURL", //video/mp4
- // // type: "", //video/mp4
- // // src: "http://37273.long-vod.cdn.aodiany210ad87667dd544439b28733e.m3u8",
- // }],
- // bigPlayButton: true,
- // textTrackDisplay: false,
- // posterImage: true,
- // errorDisplay: false,
- // controlBar: true
- })
- },
- watch: {
- onlineVideoVideoId(){
- this.videoPlayUrl(this.onlineVideoInfo)
- }
- },
- methods: {
- videoPlayUrl(info) {
- // 视频观看
- // console.log(info);
- if (this.user) {
- if (info.bought == true) {
- // 获取课程视频
- _StageItemPlayUrl({
- courseId: this.$route.query.id,
- memberId: this.user.id,
- classId: info.videoId,
- }).then(res => {
- // console.log(res)
- if (res.Code === "0") {
- this.showVideoPlayer = true;
- this.changeVideo(res.Data.positiveUrl)
- }
- }).catch((err) => {
- console.log(err)
- })
- } else {
- console.log('没有购买')
- this.$toast({
- message: '请购买课程',
- position: 'bottom',
- duration: 2000
- });
- }
- } else {
- this.$router.push({
- path: '/login',
- query: {
- redirect: to.fullPath
- }
- })
- }
- },
- changeVideo(vdSrc) {
- // 切换视频路径及类型
- if (/\.m3u8$/.test(vdSrc)) {
- this.videoPlay.src({
- src: vdSrc.replace("http://", "https://"),
- type: 'application/x-mpegURL'
- })
- } else {
- this.videoPlay.src({
- src: vdSrc,
- type: 'video/mp4'
- })
- }
- this.videoPlay.load();
- this.videoPlay.play(); //pause()暂停 销毁 dispose()
- },
- },
- beforeDestroy() {
- this.videoPlay.dispose();
- console.log("video destroy");
- }
在 build/webpack.base.conf.js 的 moudel 拿了要加上 noParse: [/videojs-contrib-hls/],不然可能会报错 t is not definded 之类的错误。
但是我在移动端没写上面这个操作,也播放成功了,PC端写了。现在不确定这段代码是否有必要。
- module: {
- noParse: [/videojs-contrib-hls/],
- rules: [
- {
- test: /\.vue$/,
- loader: 'vue-loader',
- options: vueLoaderConfig
- },
- ...
mt-radio 的使用
需求是做一个单选的数据列表,但是 mt-radio 的 options 的数据结构是 label 和 value ,其中 label 是显示的名称, value 是值。
那么我们的数据结构就也得是 value 和 label 了,如果不是,就必须得手动转换。
- Object.values(this.majorList).map(value => {
- value.value = value.id;
- value.label = value.majorName;
- // console.log(value)
- // console.log(this.majorList)
- });
自适应多层级目录
使用迭代,
调用:
- <DetailMultiMenu
- v-for="(classItem,index) in this.classListData"
- :key="index"
- :item="classItem"
- @videoInfo="videoPlayUrl"
- ></DetailMultiMenu>
组件:
- <template>
- <ul class="multi-menu">
- <li v-if="item.stageName == ''">
- <div
- class="class-title-wrap"
- @click="toggleItemList = !toggleItemList"
- :style="{marginLeft: 0.3*(item.level-1) +'rem'}"
- >
- <span>
- <i class="iconfont icon-caidan"></i>
- <span>{{item.classjName}}</span>
- </span>
- <i :class="[toggleItemList?'iconfont icon-xiangshang':'iconfont icon-xiangxia']"></i>
- </div>
- <ul v-for="(child,index) in item.sub" :key="index" v-show="toggleItemList">
- <DetailMultiMenu v-if="child.stageName == ''" :item="child" :key="child.classjName"></DetailMultiMenu>
- <li
- v-else
- :key="child.id"
- :class="activeLi+index == child.id+index ? 'activeLi': ''"
- @click="videoInfo(child.id,child.isPay,child.id)"
- >
- <div class="class-item-wrap" :style="{marginLeft: 0.3*(child.level-1) +'rem'}">
- <span class="class-title">
- <i class="iconfont icon-zhibo11"></i>
- <span>{{child.classjName}}</span>
- </span>
- </div>
- </li>
- </ul>
- </li>
- <li v-else class="class-item-wrap">
- <div>
- <i class="iconfont icon-zhibo11"></i>
- {{item.classjName}}
- </div>
- </li>
- </ul>
- </template>
- <script>
- import {
- // mapGetters,
- mapMutations
- // mapState
- } from "vuex";
- export default {
- name: "DetailMultiMenu",
- data() {
- return {
- toggleItemList: false,
- activeLi: -1
- };
- },
- props: {
- item: {
- type: Object,
- required: true
- }
- },
- computed: {},
- created() {},
- mounted() {},
- methods: {
- ...mapMutations([
- "ONLINE_VIDEO_VIDEOID",
- "ONLINE_VIDEO_BOUGHT",
- "ONLINE_VIDEO_PLAY"
- ]),
- videoInfo(itemId, bought, videoId) {
- if (bought) {
- this.activeLi = itemId;
- }
- this.ONLINE_VIDEO_BOUGHT(bought);
- this.ONLINE_VIDEO_VIDEOID(videoId);
- // this.ONLINE_VIDEO_PLAY(true);
- // this.$emit("videoInfo",{bought, videoId} );
- }
- },
- watch: {},
- components: {}
- };
- </script>
- <style scoped>
- /* .activeLi {
- background: #26a2ff;
- } */
- /* 课程目录 */
- .multi-menu {
- font-size: 0.3rem;
- }
- .class-title-wrap {
- border-bottom: 1px solid #ddd;
- line-height: 1rem;
- height: 1rem;
- display: flex;
- justify-content: space-between;
- }
- .class-item-wrap {
- border-bottom: 1px solid #ddd;
- overflow: hidden;
- line-height: 1rem;
- height: 1rem;
- display: -webkit-box;
- /*! autoprefixer: off */
- -webkit-box-orient: vertical;
- /* autoprefixer: on */
- -webkit-line-clamp: 1;
- overflow: hidden;
- white-space: pre-line;
- font-size: 0.24rem;
- }
- </style>
行内切换 class 名
- <i :class="[toggleItemList?'iconfont icon-xiangshang':'iconfont icon-xiangxia']"></i>
- <li
- v-else
- :key="child.id"
- :class="activeLi+index == child.id+index ? 'activeLi': ''"
- @click="videoInfo(child.id,child.isPay,child.id)"
- ></li>
行内样式自动计算
- <div class="class-item-wrap" :style="{marginLeft: 0.3*(child.level-1) +'rem'}"></div>
七牛云上传图片
- <input
- type="file"
- id="avatarUpload"
- ref="imgInput"
- accept="image/*"
- @change="PreviewImage"
- >
可以不用七牛云的插件,但要手动创建一个 formData,并且设置请求头
- PreviewImage(event) {
- let file = event.target.files[0];
- let formData = new FormData();
- formData.append("file", file);
- formData.append("token", this.qiniutoke);
- this.$http({
- url: "https://up-z2.qiniup.com",
- method: "POST",
- headers: { "Content-Type": "multipart/form-data" },
- data: formData
- })
- // _UploadQiniu({
- // file:file,
- // token:this.qiniutoke
- // })
- .then(res => {
- console.log(res);
- this.currentUser.handUrl = res.data.url + res.data.key;
- console.log(this.currentUser.handUrl);
- })
- .catch(err => {
- console.log(err);
- });
- }
vue 移动端项目总结(mint-ui)的更多相关文章
- 从零开始搭建vue移动端项目到上线的步骤
初始化项目 1.在安装了node.js的前提下,使用以下命令 npm install --g vue-cli 2.在将要构建项目的目录下 vue init webpack myproject(项目目录 ...
- 从零开始搭建vue移动端项目到上线
先来看一波效果图 初始化项目 1.在安装了node.js的前提下,使用以下命令 npm install --g vue-cli 2.在将要构建项目的目录下 vue init webpack mypro ...
- Vue移动端项目模板
一个集成移动端开发插件的Vue移动端模板包含1.css: 使用stylus开发css 集成reset样式文件 修改UI组件文件 统一样式处理(如主题色等)2.UI组件 使用热门的vant与mint-u ...
- Vue移动端项目总结
使用Vue项目写了一个移动端项目,然后又把项目硬生生的抽离了组件,一直忙着写RN项目没有时间总结心得,今天上午终于下定决心,写点总结. 1.position:absolute: 定位的时候不同手机的浏 ...
- vue 移动端项目切换页面,页面置顶
之前项目是pc端是使用router的方式实现置顶的 //main.js router.afterEach((to, from, next) => { window.scrollTo(0, 0) ...
- vue PC端项目中解决userinfo问题
在vue2 中用脚手架建立的项目,后端提供接口获取数据.在公司做第一个项目的时候不清楚公司里的对接流程,结果后续代码被一个接口整的乱七八糟,这个接口是获取用户信息的接口——'usre/info'. 如 ...
- 将Vue移动端项目打包成手机app---HBuilder
将移动端页面打包成app 1.使用 HBuilder 直接编译打包 点击左上角 文件>打开目录>选择目录 选择用Webpack打包好的dist文件目录 由于我添加到项目了,所以会显示该项 ...
- vue移动端项目
用vue mint-ui jquery-weui写了一个移动端demo 技术栈 vue2.0 vue-router axios mint-ui jquery-weui webpack 页面截图 最后 ...
- vue移动端项目在手机上调试
1.电脑和手机要连同一个wifi 一定是复制无线网的IP,而不是以太网的IP 2.在你的电脑上查找无线网络的ipv4地址: 查找方法:windows+r 然后再输入框里输入cmd 回车 会出现这 ...
随机推荐
- @EnableFeignClients 注解
feignClents在spring容器里找不到的原因 当使用的feignClents 来自引用别的工程时,需要指定包名,如果不指定就算使用ComponentScan 扫描也不行 import org ...
- RecyclerView的Item的单击事件
RecyclerView 的每个Item的点击事件并没有像ListView一样封装在组件中,需要Item的单击事件时就需要自己去实现,在Adapter中为RecyclerView添加单击事件参考如下: ...
- commons-lang3之StringUtils
字符串是一种在开发中经常使用到的数据类型,对字符串的处理也变得非常重要,字符串本身有一些方法,但都没有对null做处理,而且有时可能还需要做一些额外处理才能满足我们的需求,比如,要判断某个字符串中是否 ...
- 有关CSS的overflow和border-radius的那些事,你的圆角被覆盖了吗?
事件起因 最初是网友的一个提问,来自于我的知识星球社区: 说实话,不得不佩服这个网友的眼力,这么小的细节都能发现.不过这也正是 FineUI 一直前进的动力,来自社区的监督和促进. 从截图上看,貌似圆 ...
- Carthage下没有Build文件夹
问题描述: 用Carthage管理项目时,执行Carthage upate --platform iOS后发现Carthage目录下没有Build文件夹 解决方案: 在Xcode > Prefe ...
- RMQ区间最大值与最小值查询
RMQ复杂度:建表$O\left ( nlgn \right ) $,查询$O\left ( 1 \right )$ ll F_Min[maxn][20],F_Max[maxn][20]; void ...
- 腾讯通信云服务端使用心得,腾讯云IM
腾讯通信云服务端使用心得 1.腾讯通信服务入口并创建应用 方便使用保留url地址 : https://cloud.tencent.com/product/im 注册账号腾讯云账号->通过审核 ...
- Photoshop调出田园照片唯美手绘油画效果
先看看效果图 原片分析:妹子脸上的光不够通透,有些灰暗,整体色调不够分明. 后期思路:色彩往油画风格调整,让画面色彩更加油润.丰富. 基础调整 (1)曝光根据照片的实际情况进行调整 (2)增加阴影部分 ...
- linux 下ab压力测试
1.ab的简介 ab是apachebench命令的缩写. ab是apache自带的压力测试工具.ab非常实用,它不仅可以对apache服务器进行网站访问压力测试,也可以对或其它类型的服务器进行压力测试 ...
- 2016湖南省赛 [Cloned]
A.2016 给出正整数 n 和 m,统计满足以下条件的正整数对 (a,b) 的数量: 1. 1≤a≤n,1≤b≤m; 2. a×b 是 2016 的倍数. Input 输入包含不超过 30 组数 ...