UniApp官网地址:https://uniapp.dcloud.net.cn/
HBuilderX官网地址:https://www.dcloud.io/hbuilderx.html
微信开发者工具下载地址:
https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
为啥我们要学习uniapp!!!
1、更高的百度指数,跨端完善度更高,真正落地的提高生产力2、平台能力不受限,在跨端的同时,通过条件编译+平台特有API调用,可以优雅的为某平台写个性化代码,调用专有能力而不影响其他平台。支持原生代码混写和原生sdk集成。3、性能体验优秀,体验更好的Hybrid框架,加载新页面速度更快。App端支持weex原生渲染,可支撑更流畅的用户体验。小程序端的性能优于市场其他框架。4、周边生态丰富,丰富的插件市场,各种轮子拿来即用。支持NPM、支持小程序组件和SDK、兼容mpvue组件和项目、兼容weex组件。微信生态的各种sdk可直接用于跨平台App。5、学习成本低,基于通用的前端技术栈,采用vue语法+微信小程序api,无额外学习成本。6、开发成本低,不止开发成本,招聘、管理、测试各方面成本都大幅下降。HBuilderX是高效开发神器,熟练掌握后研发效率至少翻倍(即便只开发一个平台)。
引入uni-request封装request.js,我来给你规范一下!!!
npm install uni-request --save // 安装依赖
request.js | 与axios封装api.js类似
import uniRequest from "uni-request";/* url前缀 */let base = 'http://localhost:9101';/* 是否需要登录 */let needLogin = true;/* 请求拦截器 */uniRequest.interceptors.request.use(request=>{console.log(`请求【${request.url}】${request.needLogin?'需要':'不需要'}登录`);if(request.needLogin){if(uni.getStorageSync('userInfo')){request.headers.Authorization = 'Bearer '+ uni.getStorageSync('userInfo').access_token;}else{uni.showToast({icon:'none',title:'请重新登录'})uni.navigateTo({url:'/pages/login/login.vue'})}}return request;},err=>{return Promise.reject(err);});/* 响应拦截器 */uniRequest.interceptors.response.use(success=> {if (success.status && success.status == 200 && success.data.status == 500){uni.showToast({icon:'none',title:success.data.msg})return;}if (success.status && success.status == 200 && success.data.code == 401){uni.showToast({icon:'error',title:'请重新登录'})uni.navigateTo({url:'/pages/login/login.vue'})return;}if (success.status && success.status == 200 && success.data.code == 403){uni.showToast({icon:'error',title:'权限不足,请联系管理员!'})return;}if (success.data.msg){uni.showToast({icon:'success',title:success.data.msg})}// 下载文件资源 type:文件类型if(success.data.type){return success;}return success.data;}, err=> {if (error.response.status == 504){uni.showToast({icon:'error',title:'技术人员正在更新,请稍后...'})}else if (error.response.status == 403){uni.showToast({icon:'error',title:'权限不足,请联系管理员!'})}else if (error.response.status == 401){uni.showToast({icon:'error',title:'请重新登录'})uni.navigateTo({url:'/pages/login/login.vue'})}else if (error.response.status == 500 && error.response.data.code == 504){uni.showToast({icon:'error',title:error.response.data.message})}else {if (error.response.data.msg){uni.showToast({icon:'error',title:error.response.data.msg})}else {uni.showToast({icon:'error',title:'未知错误!'})}}return;});/*封装请求方式,这里封装两个post请求,一个专门用来登录使用key-value的方式传参,因为Spring Security登录默认不支持Json参数*/export const postKeyValueRequest=(url,params)=>{return uniRequest({needLogin: false,method:'POST',url:`${base}${url}`,data:params,headers:{'Content-Type':'application/x-www-form-urlencoded'}})}// Post请求export const postRequest=(url,params)=>{return uniRequest({needLogin: true,method: 'POST',url:`${base}${url}`,data: params,headers:{'Content-Type':'application/json'}})}// Get请求export const getRequest=(url,params)=>{return uniRequest({needLogin: true,method:'GET',url:`${base}${url}`,data:params})}// Put请求export const putRequest=(url,params)=>{return uniRequest({needLogin: true,method:'PUT',url:`${base}${url}`,data:params,headers:{'Content-Type':'application/json'}})}// Delete请求export const deleteRequest=(url,params)=>{return uniRequest({needLogin: true,method:'DELETE',url:`${base}${url}`,data:params})}
main.js全局挂载request.js中的请求方法
import {postKeyValueRequest} from "./utils/request.js";import {postRequest} from "./utils/request.js";import {getRequest} from "./utils/request.js";import {putRequest} from "./utils/request.js";import {deleteRequest} from "./utils/request.js";Vue.prototype.postKeyValueRequest = postKeyValueRequest;Vue.prototype.postRequest = postRequest;Vue.prototype.getRequest = getRequest;Vue.prototype.putRequest = putRequest;Vue.prototype.deleteRequest = deleteRequest;
uni-section导航组件在哪啊!!!

uni-section导航组件在官网中没有单独拎出来,但是在官网其他组件介绍中有混合使用该组件,在创建uniapp项目时选择默认模板,会发现怎么没有该组件,那对于第一次接触uniapp开发的小白们会懵逼,其实uni-section导航组件是自定义组件。

我们可以重新新建一个项目,这个时候选择uni-app项目创建,会发现在项目自定义components目录下有uni-section组件,那对于使用默认模板创建的也不必惊慌,我给大家粘贴一下代码,手动创建添加即可,至于组件如何应用就不在这里赘述,和在Vue中的方法一模一样。
<template><view class="uni-section"><view class="uni-section-header" nvue><view v-if="type" class="uni-section__head"><view :class="type" class="uni-section__head-tag"/></view><view class="uni-section__content"><text :class="{'distraction':!subTitle}" :style="{color:color}" class="uni-section__content-title">{{ title }}</text><text v-if="subTitle" class="uni-section__content-sub">{{ subTitle }}</text></view></view><view :style="{padding: padding ? '10px' : ''}"><slot/></view></view></template><script>/*** Section 标题栏* @description 标题栏* @property {String} type = [line|circle] 标题装饰类型* @value line 竖线* @value circle 圆形* @property {String} title 主标题* @property {String} subTitle 副标题*/export default {name: 'UniSection',emits:['click'],props: {type: {type: String,default: ''},title: {type: String,default: ''},color:{type: String,default: '#333'},subTitle: {type: String,default: ''},padding: {type: Boolean,default: false}},data() {return {}},watch: {title(newVal) {if (uni.report && newVal !== '') {uni.report('title', newVal)}}},methods: {onClick() {this.$emit('click')}}}</script><style lang="scss" >$uni-primary:.uni-section {background-color:// overflow: hidden;margin-top: 10px;}.uni-section-header {position: relative;/* #ifndef APP-NVUE */display: flex;/* #endif */flex-direction: row;align-items: center;padding: 12px 10px;// height: 50px;font-weight: normal;}.uni-section__head {flex-direction: row;justify-content: center;align-items: center;margin-right: 10px;}.line {height: 12px;background-color: $uni-primary;border-radius: 10px;width: 4px;}.circle {width: 8px;height: 8px;border-top-right-radius: 50px;border-top-left-radius: 50px;border-bottom-left-radius: 50px;border-bottom-right-radius: 50px;background-color: $uni-primary;}.uni-section__content {/* #ifndef APP-NVUE */display: flex;/* #endif */flex-direction: column;flex: 1;color:}.uni-section__content-title {font-size: 14px;color: $uni-primary;}.distraction {flex-direction: row;align-items: center;}.uni-section__content-sub {font-size: 12px;color:line-height: 16px;margin-top: 2px;}</style>
准备工作就绪,我们来看看登录页面!!!

站在后端的角度,不用在乎界面的美观,我们在乎的是功能!!!
登录页login.vue
<template><view><view style="width: 90%; margin: 200rpx auto;"><view style="margin-bottom: 70rpx; font-size: 60rpx; color: royalblue; text-align: center;">程序猿侠</view><uni-forms ref="form" :modelValue="loginForm" :rules="rules"><uni-forms-item name="username"><uni-easyinput v-model="loginForm.username" prefixIcon="person" placeholder="请输入登录账号"></uni-easyinput></uni-forms-item><uni-forms-item name="password"><uni-easyinput type="password" v-model="loginForm.password" prefixIcon="locked" placeholder="请输入登录密码"></uni-easyinput></uni-forms-item></uni-forms><view><button type="primary" @click="loginBtn">登 录</button></view></view></view></template><script>import {Encrypt} from '../../utils/aes.js'export default {data() {return {loginForm: {username:'chengqiang',password:'chengqiang',grant_type:'password',client_id:'client-app',client_secret:'123456'},rules: {username: {rules:[{required: true,errorMessage: '请输入账号'}],validateTrigger:'submit',},password: {rules:[{required: true,errorMessage: '请输入密码'}],validateTrigger:'submit',}}}},onShow() {/* 隐藏底部导航栏 */uni.hideTabBar();},methods: {/* 登录请求 */loginBtn() {this.loginForm.password = Encrypt(this.loginForm.password);this.$refs.form.validate().then(res=>{this.postKeyValueRequest('/authorization/oauth/token',this.loginForm).then(resp=>{if (resp){/* 存储当前登录用户 */uni.setStorageSync('userInfo',resp.data);/* 提示登录成功 */uni.showToast({title:'登陆成功'});/* 登录成功跳转主页 */let timer = setTimeout(()=>{clearTimeout(timer);uni.switchTab({url:'/pages/index/index'});},1000);}})}).catch(err =>{})}}}</script><style scoped></style>
浏览代码发现对登录密码Aes加密了,引入crypto-js封装aes.js
npm install crypto-js
import CryptoJS from 'crypto-js'const KEY = CryptoJS.enc.Utf8.parse("1234567890123456");const IV = CryptoJS.enc.Utf8.parse('1234567890123456');export function Encrypt(word, keyStr, ivStr) {let key = KEYlet iv = IVif (keyStr) {key = CryptoJS.enc.Utf8.parse(keyStr);iv = CryptoJS.enc.Utf8.parse(ivStr);}let srcs = CryptoJS.enc.Utf8.parse(word);var encrypted = CryptoJS.AES.encrypt(srcs, key, {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.ZeroPadding});return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);}export function Decrypt(word, keyStr, ivStr) {let key = KEYlet iv = IVif (keyStr) {key = CryptoJS.enc.Utf8.parse(keyStr);iv = CryptoJS.enc.Utf8.parse(ivStr);}let base64 = CryptoJS.enc.Base64.parse(word);let src = CryptoJS.enc.Base64.stringify(base64);var decrypt = CryptoJS.AES.decrypt(src, key, {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.ZeroPadding});var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);return decryptedStr.toString();}
登录成功,跳转至首页!!!

顶部是轮播图广告位,中间是动态导航栏,底部导航栏
index.vue
<template><view><view><!-- 轮播图 --><uni-swiper-dot class="uni-swiper-dot-box" @clickItem=clickItem :info="info" :current="current" :mode="mode":dots-styles="dotsStyles" field="content"><swiper class="swiper-box" @change="changeImage" :current="swiperDotIndex" :autoplay="true" :duration="1000" :interval="5000"><swiper-item v-for="(item, index) in info" :key="index"><view class="swiper-item" :class="'swiper-item' + index"><navigator open-type="navigate" :url="'/pages/webview/webview?url=' + encodeURIComponent('https://www.btks.cn')"><image style="width: 750rpx" :src="item.url"></image></navigator></view></swiper-item></swiper></uni-swiper-dot></view><view><!-- 动态导航 --><view v-for="(office,pos) in officeList[0].childrenList" :index="pos" :key="pos"><UniSection :title="office.title" type="line" padding><uni-grid :column="4" :highlight="true" :showBorder="false"><uni-grid-item v-for="(item, index) in office.childrenList" :index="index" :key="index"><view class="grid-item-box" style="background-color: #fff;" @click="changeOffice(item)"><uni-icons :type="item.image" :size="30" color="#000" /><text style="font-size: 30rpx;">{{item.title}}</text></view></uni-grid-item></uni-grid></UniSection></view></view></view></template><script>import UniSection from '../../components/uni-section/uni-section'export default {components:{UniSection},data() {return {officeList:[{title:"首页",childrenList:[{title:'综合办公',childrenList:[{path:'/subpages/office/office',title:'待办任务',image:'wallet'},{path:'/subpages/task/task',title:'已办任务',image:'settings-filled'},{path:'/subpages/initiate/initiate',title:'发起流程',image:'paperplane'},{path:'/subpages/relate/relate',title:'我的流程',image:'person-filled'},{path:'/subpages/assignment/assignment',title:'待签收任务',image:'mail-open-filled'}]},{title:'新闻展示',childrenList:[]}]},{title:"个人中心",childrenList:[{title:'个人中心',childrenList:[{path:'/subpages/edit/edit',title:'编辑信息',image:'wallet'},{path:'/subpages/password/password',title:'修改密码',image:'settings-filled'},{path:'/subpages/userface/userface',title:'历史头像',image:'contact-filled'},{path:'/subpages/signature/signature',title:'电子签名',image:'image'}]},{title:'辅助功能',childrenList:[{path:'/subpages/system/system',title:'系统设置',image:'gear'},{path:'/subpages/location/location',title:'共享位置',image:'location-filled'},{path:'/subpages/customer/customer',title:'联系客服',image:'chatbubble'}]}]}],info: [{url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg',imgName: '内容 A'},{url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg',imgName: '内容 B'},{url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg',imgName: '内容 C'}],current: 0,mode: 'indexes',dotsStyles: {backgroundColor: 'rgba(83, 200, 249,0.3)',border: '1px rgba(83, 200, 249,0.3) solid',color: '#fff',selectedBackgroundColor: 'rgba(83, 200, 249,0.9)',selectedBorder: '1px rgba(83, 200, 249,0.9) solid'},swiperDotIndex: 0}},onShow() {},onLoad() {uni.setStorageSync("officeList",this.officeList);},onPullDownRefresh() {},methods: {/* 轮播图当前下标 */changeImage(image) {this.current = image.detail.current;},/* 导航跳转 */changeOffice(item){uni.navigateTo({url:item.path})}}}</script><style lang="scss" scoped>/* 轮播图布局高度 */.swiper-box {height: 400upx;}/* 轮播项样式 */.swiper-item {/* 注释判断终端样式单独设置 *//* #ifndef APP-NVUE */display: flex;/* #endif */flex-direction: column;justify-content: center;align-items: center;height: 400upx;}/* 动态导航样式 */.grid-item-box {flex: 1;display: flex;flex-direction: column;align-items: center;justify-content: center;padding: 10rpx 0;}</style>
个人中心

顶部背景图、头像、昵称,中间是动态导航栏,底部导航栏
personal.vue
<template><view><!-- 头像、名称 --><view class="content-item"><image src="/static/image/person_bck.png"></image><view class="user-info"><view><image src="/static/image/person_face.png"></image></view><view class="name"><text>你好,游客</text></view></view></view><!-- 动态导航 --><view v-for="(office,pos) in officeList[1].childrenList" :index="pos" :key="pos"><UniSection :title="office.title" type="line" padding><uni-grid :column="4" :highlight="true" :showBorder="false"><uni-grid-item v-for="(item, index) in office.childrenList" :index="index" :key="index"><view class="grid-item-box" style="background-color: #fff;" @click="changeOffice(item)"><uni-icons :type="item.image" :size="30" color="#000" /><text style="font-size: 30rpx;">{{item.title}}</text></view></uni-grid-item></uni-grid></UniSection></view></view></template><script>import UniSection from '../../components/uni-section/uni-section'export default {components:{UniSection},data() {return {officeList:uni.getStorageSync("officeList")}},onLoad() {},methods: {/* 导航跳转 */changeOffice(item){uni.navigateTo({url:item.path})}}}</script><style lang="scss" scoped>/* 顶部高度 */.content-item image{height: 400rpx;width: 750rpx;}/* 头像、名称 */.user-info{position: absolute;top: 150rpx;left: 50rpx;display: flex;align-items: center;}/* 头像 */.user-info image{height: 120rpx;width: 120rpx;border-radius:60rpx;}/* 名称 */.user-info text{margin-left: 16rpx;}/* 动态导航样式 */.grid-item-box {flex: 1;display: flex;flex-direction: column;align-items: center;justify-content: center;padding: 10rpx 0;}</style>
本篇到此结束,赘述了如何从一名小白搭建uniapp脚手架,显而易见,vue转uniapp就是无缝衔接,没有成本可言!!!
灵魂拷问:
1、uniapp中路由和vue路由的区别?
2、webview组件的应用?
3、404页面配置方式?
4、路由分包策略的原理是什么?
文章评论