uniapp
官方文档:uni-app官网
uni-app
是一个使用 Vue.js 开发所有前端应用的框架,开发一套代码,可以在h5端、app端、小程序端同时使用。
开发一套代码,在微信小程序运行、h5端口、app端(手机模拟器 夜神模拟器)。
uniapp全局配置
index.html
App.vue
main.js
pages.json
globalStyle Object 否 设置默认页面的窗口表现
pages Object Array 是 设置页面路径及窗口表现
easycom Object 否 组件自动引入规则 2.5.5+
tabBar Object 否 设置底部 tab 的表现
组件
1、内置组件
视图容器组件:view scroll-view swiper swiper-item
基础内容组件: icon text rich-text
表单组件:form input radio buttom checkbox switch textarea
媒体组件:image radio audio
路由跳转组件:navigator openType="navigate|redirect|switchTab|navigateBack"
map地图
2、扩展 组件 uni-ui
3、第三方组件库 uview-ui 秋云-ucharts (插件市场 )
uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架
scss语法(了解)
<template><view class="outer"><view class="c1"><view class="c11">c1-->c11</view></view><view class="c2"><view class="c11">c2-->c11</view></view></view>
</template>
<script>export default {data() {return {}},methods: {}}
</script>
<style lang="scss">/* css选择器id #标签 标签名类选择 .属性选择器 [name='admin']子元素 >子孙元素 空格通配 **/// .c2{// background-color: gray;// height: 150px;// }// .c2>.c11{// background-color: gold;// height: 100px;// }.c1{.c11{background-color: red;height: 100px;}}.c2{background-color: gray;height: 150px;.c11{background-color: gold;height: 100px;}}</style>
vue2语法(熟悉)
<template><view class="container">{{id}}<button @click="m1()">++</button><button @tap="m1()">++</button></view>
</template>
<script>export default {data() {return {id:1,href: 'https://uniapp.dcloud.io/component/README?id=uniui'}},methods: {m1(){console.log("-----m1-----");this.id = ++this.id;},m2(){console.log("=====m2====");}},mounted() {this.m1();}}
</script>
<style>.container {padding: 20px;font-size: 14px;line-height: 24px;}
</style>
一、常见的事件类型
@click
用于处理用户点击操作。 可以绑定在按钮、列表项等元素上,当用户点击时触发相应的逻辑处理。 是标准的HTML DOM事件,在浏览器中通用。
@tap
也是用于处理用户点击事件。 是小程序和移动端框架(如微信、uni-app)封装的事件,专为触摸交互优化。 在移动端,由于浏览器的click延迟可能会导致用户体验卡顿,而@tap通过原生触摸事件(touchstart/touchend)实现即时响应。 在H5端,@tap会被编译为click(但通过触摸事件模拟,无延迟);在小程序/App端,@tap直接调用原生触摸事件;在PC端可能无效,需用@click。
@longpress 和 @longtap
用于处理长按操作。 可以绑定在需要长按触发逻辑的元素上,例如长按显示气泡框、长按删除等操作。 这两个事件在功能上相似,但具体实现和触发条件可能略有不同,具体使用哪个事件取决于你的应用需求和uniapp的版本。
其他手势事件: @touchstart:手指触摸动作开始。 @touchmove:手指触摸后移动。 @touchcancel:手指触摸被打断,如来电提醒、弹窗。 @touchend:手指触摸动作结束,如松开按钮。 这些事件为开发者提供了丰富的用户交互手段,可以根据实际需求选择合适的事件来处理用户交互和页面逻辑。
二、api
1、发起网络请求
uni.request
2、页面跳转的
uni.redirectTo
uni.naviateTo
uni.switchTab
uni.navigateBack
uni.relaunch
3、界面交互api
弹出框
uni.showToast uni.HideToast
uni.showLoading uni.HideLoading
4、定时任务
setTimeout
三、完整模块的api
列表查询
<template><view><view class="u-page"><u-list><u-list-item v-for="(item, index) in indexList" :key="index"><u-cell :title="item.channelName"><u-avatarslot="icon"shape="square"size="35":src="item.userImg"customStyle="margin: -3px 5px -3px 0"></u-avatar></u-cell></u-list-item></u-list></view></view>
</template>
<script>export default {data() {return {indexList:[]}},methods: {loadChannel(){//发起网络请求,查询数据uni.request({url:"http://localhost:8080/system/channels/list",method:"GET",header: {'authorization': 'Bearer '+uni.getStorageSync('token')},data:{pageNum:1,pageSize:10,},success: (res) => {console.log(res.data);this.indexList = res.data.rows;}})}},onLoad() {this.loadChannel();}}
</script>
<style>
</style>
封装request.js
1. 创建环境配置文件
在项目根目录创建 config/env.js
,管理不同环境的 URL:
// config/env.js
const ENV = process.env.NODE_ENV || 'development'; // 获取当前环境
const envConfig = {development: { // 开发环境BASE_API: 'http://dev.api.example.com',STATIC_URL: 'http://dev.static.example.com'},production: { // 生产环境BASE_API: 'https://api.example.com',STATIC_URL: 'https://static.example.com'},// 其他环境(如测试环境)test: {BASE_API: 'http://test.api.example.com',STATIC_URL: 'http://test.static.example.com'}
};
// 导出当前环境配置
export default envConfig[ENV];
2. 封装全局请求工具
创建 utils/request.js
,实现以下功能:
-
自动拼接基础 URL
-
拦截请求/响应
-
统一错误处理
-
自动携带 Token
// utils/request.js
import env from '@/config/env'; // 引入环境配置
/*** 封装 uni.request* @param {Object} options 请求配置*/
const request = (options) => {// 1. 处理请求 URL(拼接环境变量中的 BASE_API)if (!options.url.startsWith('http')) {options.url = env.BASE_API + options.url;}
// 2. 默认请求配置const defaultOptions = {method: 'POST',header: {'Content-Type': 'application/json','Authorization': uni.getStorageSync('token') || '' // 自动携带 Token}};
// 3. 合并配置options = { ...defaultOptions, ...options };
// 4. 返回 Promisereturn new Promise((resolve, reject) => {uni.request({...options,success: (res) => {// 5. 统一处理 HTTP 状态码if (res.statusCode >= 200 && res.statusCode < 300) {resolve(res.data); // 请求成功} else if (res.statusCode === 401) {// Token 过期处理uni.navigateTo({ url: '/pages/login/login' });reject(new Error('未登录或登录已过期'));} else {reject(res.data); // 其他错误}},fail: (err) => {// 6. 网络错误处理uni.showToast({ title: '网络错误', icon: 'none' });reject(err);}});});
};
// 7. 快捷方法封装(GET/POST/PUT/DELETE)
export const get = (url, data, options = {}) => {return request({ url, data, method: 'GET', ...options });
};
export const post = (url, data, options = {}) => {return request({ url, data, method: 'POST', ...options });
};
export default request;
3. 实际使用示例
import { get, post } from '@/utils/request';
export default {methods: {// GET 请求async fetchData() {try {const res = await get('/user/info', { id: 123 });console.log(res);} catch (err) {console.error(err);}},
// POST 请求async submitForm() {try {const res = await post('/user/login', {username: 'admin',password: '123456'});console.log(res);} catch (err) {console.error(err);}}}
};
4、普通表单新增
<template><view><uni-forms ref="form" :modelValue="formData" :rules="rules"><uni-forms-item label="渠道名称" name="channelName"><uni-easyinput type="text" v-model="formData.channelName" placeholder="请输入渠道名称" /></uni-forms-item><uni-forms-item label="渠道编码" name="channelCode"><uni-easyinput type="text" v-model="formData.channelCode" placeholder="请输入渠道名称" /></uni-forms-item>
</uni-forms><button @click="submit">Submit</button></view>
</template>
<script>import {get,post} from '@/utils/request';export default {data() {return {// 表单数据formData: {channelName: '平安好卫士',channelCode: 'pyhws'},rules: {// 对name字段进行必填验证channelName: {rules: [{required: true,errorMessage: '请输入渠道名',},{minLength: 3,maxLength: 5,errorMessage: '长度在 {minLength} 到 {maxLength} 个字符',}]},// 对code字段进行必填验证channelCode: {rules: [{required: true,errorMessage: '请输入渠道编码',}]}}}},methods: {async loadChannel() {const resp = await get('/system/channels/list', {});console.log(resp);},async submit() {const validate = await this.$refs.form.validate();console.log('表单数据信息:', validate);//接收数据console.log(this.formData);//发起网路请求const resp = await post('/system/channels', this.formData);console.log(resp, "新增结果");if (resp.code == 200) { //成功//跳转到列表查询uni.navigateTo({url: "/pages/channel/channel"})} else {console.log("新增失败");}}},onLoad() {this.loadChannel();}}
</script>
<style>
</style>
5、文件上传+form表单
<template><view><uni-forms ref="form" :modelValue="formData" :rules="rules"><uni-forms-item label="渠道名称" name="channelName"><uni-easyinput type="text" v-model="formData.channelName" placeholder="请输入渠道名称" /></uni-forms-item><uni-forms-item label="渠道编码" name="channelCode"><uni-easyinput type="text" v-model="formData.channelCode" placeholder="请输入渠道名称" /></uni-forms-item><uni-forms-item label="选择图片" name="userImg"><u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple:maxCount="10"></u-upload></uni-forms-item></uni-forms><button @click="submit">Submit</button></view>
</template>
<script>import {get,post} from '@/utils/request';export default {data() {return {fileList1: [],// 表单数据formData: {channelName: '平安好卫士',channelCode: 'pyhws'},rules: {// 对name字段进行必填验证channelName: {rules: [{required: true,errorMessage: '请输入渠道名',},{minLength: 3,maxLength: 5,errorMessage: '长度在 {minLength} 到 {maxLength} 个字符',}]},// 对code字段进行必填验证channelCode: {rules: [{required: true,errorMessage: '请输入渠道编码',}]}}}},methods: {async loadChannel() {//const resp = await get('/system/channels/list', {});//console.log(resp);},async submit() {const validate = await this.$refs.form.validate();console.log('表单数据信息:', validate);//接收数据console.log(this.formData);//发起网路请求const resp = await post('/system/channels', this.formData);console.log(resp, "新增结果");if (resp.code == 200) { //成功//跳转到列表查询uni.navigateTo({url: "/pages/channel/channel"})} else {console.log("新增失败");}
},// 删除图片deletePic(event) {this[`fileList${event.name}`].splice(event.index, 1);},// 新增图片async afterRead(event) {// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式let lists = [].concat(event.file);let fileListLen = this[`fileList${event.name}`].length;lists.map((item) => {this[`fileList${event.name}`].push({...item,status: "uploading",message: "上传中",});});for (let i = 0; i < lists.length; i++) {const result = await this.uploadFilePromise(lists[i].url);// console.log(JSON.parse(result).url,"--------");// this.formData.userImg = JSON.parse(result).url;let item = this[`fileList${event.name}`][fileListLen];this[`fileList${event.name}`].splice(fileListLen,1,Object.assign(item, {status: "success",message: "",url: JSON.parse(result).url,}));fileListLen++;}//打印输出所有的文件列表console.log(this.fileList1);//选择第一张照片,存储到数据库表this.formData.userImg = this.fileList1[0].url;},uploadFilePromise(url) {return new Promise((resolve, reject) => {let a = uni.uploadFile({url: "http://localhost:8080/common/upload", // 仅为示例,非真实的接口地址filePath: url,name: "file",formData: {user: "test",},header: {'authorization': 'Bearer ' + uni.getStorageSync('token')},success: (res) => {setTimeout(() => {console.log(res.data, "文件上传后返回响应对象");resolve(res.data);}, 1000);},});});}
},onLoad() {this.loadChannel();}}
</script>
<style>
</style>
6、删除
request.js
export const put = (url, data, options = {}) => {return request({ url, data, method: 'PUT', ...options });
};
export const del = (url, data, options = {}) => {return request({ url, data, method: 'DELETE', ...options });
};
<u-list-item v-for="(item, index) in indexList" :key="index"><u-cell :title="item.channelName"><u-avatar slot="icon" shape="square" size="35" :src="item.userImg"customStyle="margin: -3px 5px -3px 0"></u-avatar><text slot="value">修改|</text><text slot="value" @click="deleteChannel(item, index)">删除</text></u-cell></u-list-item>
</u-list>
async deleteChannel(item, index){//获取主键console.log(item.channelId);const res = await del("/system/channels/"+item.channelId);console.log(res);//判断失败,提醒//成功,刷新this.loadChannel();},
7、修改
1、点击修改按钮,传递id到修改页面
2、修改页面 onload函数中获取主键id
3、判断id是否存在,存在根据id查询详情,回显数据
4、更新数据到数据库表
5、联调新增和修改。
toEdit(item){//跳转到新增|修改页面 传递主键uni.navigateTo({url:"/pages/channel/channelAdd?id="+item.channelId})},
onLoad(options) {//判断是否是修改if(options != null && options.id!=null && options.id!=''){uni.setNavigationBarTitle({title: '修改渠道'});//调用方法,查询详情this.getChannelDtail(options.id);}}
async submit() {const validate = await this.$refs.form.validate();//接收数据console.log(this.formData);var resp = null ;//发起网路请求if(this.formData.channelId==null || this.formData.channelId==''){//新增resp = await post('/system/channels', this.formData);}else{//修改resp = await put('/system/channels', this.formData);}if (resp.code == 200) { //成功//跳转到列表查询uni.navigateTo({url: "/pages/channel/channel"})} else {console.log("操作失败");}},
8、下拉刷新
"globalStyle": {"navigationBarTextStyle": "black","navigationBarTitleText": "uni-app","navigationBarBackgroundColor": "#dfd7ce","backgroundColor": "#F8F8F8","app-plus": {"background": "#efeff4"},"enablePullDownRefresh": true}
onPullDownRefresh() {let pageSize = Math.ceil(this.total/3);if(this.currentPage >= pageSize){console.log("已经最后一页");}else{this.loadChannel(++this.currentPage);}setTimeout(()=>{uni.stopPullDownRefresh();},1000);}
9、搜索+分页
<uni-forms ref="form" :modelValue="formData" ><uni-forms-item label="渠道名称" name="channelName"><uni-easyinput type="text" v-model="formData.channelName" placeholder="请输入渠道名称" /></uni-forms-item><uni-forms-item label="渠道编码" name="channelCode"><uni-easyinput type="text" v-model="formData.channelCode" placeholder="请输入渠道编码" /></uni-forms-item></uni-forms><button @click="search">搜索</button>
async search(){this.indexList = [];this.loadChannel(1) ;},