相关文档
简介
https://uniapp.dcloud.net.cn/
组件
https://uniapp.dcloud.net.cn/component/
API
https://uniapp.dcloud.net.cn/api/
演示
https://hellouniapp.dcloud.net.cn/pages/component/view/view
判断环境
https://uniapp.dcloud.net.cn/api/other/getAccountInfoSync.html#getaccountinfosync
示例
1 2 3
| const accountInfo = uni.getAccountInfoSync(); console.log("appId",accountInfo.miniProgram.appId); console.log("envVersion",accountInfo.miniProgram.envVersion);
|
其中miniProgram.envVersion
develop开发版:开发工具中运行或真机调试时
trial体验版:提交后设置为体验版
release正式版:提交并审核上架后
gray灰度版(仅支付宝小程序支持)
状态栏/导航条
状态栏
官方默认的高度--status-bar-height的值是固定的25px。
这就导致不同型号的手机的状态栏的高度是不一样的,导致实际效果并不好。
导航栏在不同的型号上是不受影响的都是44px。
1 2 3 4
| .status_bar { height: var(--status-bar-height); width: 100%; }
|
JS中
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script> export default { data() { return { windowInfo: {}, } }, beforeMount() { this.windowInfo = uni.getWindowInfo() }, onLoad() {}, methods: {} } </script>
|
状态栏设置
1 2
| <view class="status_bar" :style="{height:windowInfo.statusBarHeight+'px'}"></view>
|
导航条
pages.json
1 2 3 4 5 6 7 8
| "globalStyle": { "navigationBarTextStyle": "black", "navigationBarBackgroundColor": "#ffffff", "navigationBarTitleText": "", "backgroundColor": "#f8f8f8" },
|
导航条颜色一般是通过navigationBarTextStyle配置
导航栏标题颜色及状态栏前景颜色,仅支持 black/white
但是支付宝小程序不支持
支付宝是通过navigationBarBackgroundColor影响,只有值是#ffffff的时候文字就是黑的,其它值文字都是白的。
样式中使用
有这种情况,假如我们有一个组件使用的定位是粘性定位,这就要设置top的值,并且top应该是状态栏和导航的和。
这种情况是不能用sass的变量的,sass的变量不是运行时的变量,而状态栏的高度我们只能在运行时获取,所以我们可以用CSS变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <view :style="{ '--statusBarHeight': statusBarHeight, '--statusAndNaviBarHeight': statusAndNaviBarHeight }"> </view> </template>
<script> export default { data() { return { statusBarHeight: '25px', statusAndNaviBarHeight: '69px' }; }, beforeMount() { let windowInfo = uni.getWindowInfo(); this.statusBarHeight = windowInfo.statusBarHeight + 'px'; this.statusAndNaviBarHeight = windowInfo.statusBarHeight + 44 + 'px'; } }; </script>
|
样式中直接这样用就行了
1 2 3
| position: sticky; top: var(--statusAndNaviBarHeight); z-index: 2;
|
注意
不要在App.vue中设置变量,页面中无法获取。
TabBar
pages.json 中 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| { "tabBar": { "color": "#555555", "selectedColor": "#FA9C31", "borderStyle": "black", "backgroundColor": "#ffffff", "height": "50px", "fontSize": "12px", "iconWidth": "24px", "spacing": "6px", "list": [{ "pagePath": "pages/index/index", "iconPath": "/static/imgs/tab/tab_home1.png", "selectedIconPath": "/static/imgs/tab/tab_home2.png", "text": "首页" }, { "pagePath": "pages/category/index", "iconPath": "/static/imgs/tab/tab_category1.png", "selectedIconPath": "/static/imgs/tab/tab_category2.png", "text": "分类" }, { "pagePath": "pages/usercenter/index", "iconPath": "/static/imgs/tab/tab_user1.png", "selectedIconPath": "/static/imgs/tab/tab_user2.png", "text": "我的" }] } }
|
如果要用图标字体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| { "tabBar": { "color": "#7A7E83", "selectedColor": "#3cc51f", "borderStyle": "black", "backgroundColor": "#ffffff", "height": "50px", "fontSize": "10px", "iconWidth": "24px", "spacing": "3px", "iconfontSrc": "/static/iconfont.ttf", "list": [{ "pagePath": "pages/index/index", "iconPath": "/static/imgs/tab/tab_home1.png", "selectedIconPath": "/static/imgs/tab/tab_home2.png", "text": "首页", "iconfont": { "text": "\ue102", "selectedText": "\ue103", "fontSize": "17px", "color": "#000000", "selectedColor": "#0000ff" } }] } }
|
交互反馈
消息
1 2 3 4 5 6
| uni.showToast({ title: "领取成功", duration: 3000 });
uni.hideToast();
|
Loading
1 2 3 4 5
| uni.showLoading({ title: '请求中...', });
uni.hideLoading();
|
如果没设置title,默认为加载中
提示带确定
1 2 3 4 5 6 7 8 9 10
| uni.showModal({ title: '提示', content: '这是一个警告框示例', showCancel: false, success: function (res) { if (res.confirm) { console.log('用户点击确定'); } } });
|
和支付宝小程序的下面的方法类似
1 2 3
| my.alert({ content: '支付失败' });
|
确认提示框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| uni.showModal({ title: '取消订单', content: '你确定要取消订单吗?', showCancel: true, cancelText: '取消', confirmText: '确定', success: (res) => { if (res.confirm) { console.log('用户点击确定'); } else if (res.cancel) { console.log('用户点击取消'); } } });
|
底部菜单
1 2 3 4 5 6 7 8 9
| uni.showActionSheet({ itemList: ['A', 'B', 'C'], success: function (res) { console.log('选中了第' + (res.tapIndex + 1) + '个按钮'); }, fail: function (res) { console.log(res.errMsg); } });
|
展示类组件
Image
1
| <image src="/static/imgs/home/search.png" mode="scaleToFill"></image>
|
如果我们想只设置宽度,让高度自适应
1
| <image src="/static/imgs/01.jpg" mode="widthFix"></image>
|
mode 有效值
默认为scaleToFill
mode 有 14 种模式,其中 5 种是缩放模式,9 种是裁剪模式。
| 模式 |
值 |
说明 |
| 缩放 |
scaleToFill |
不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素 |
| 缩放 |
aspectFit |
保持纵横比缩放图片,使图片的长边能完全显示出来。 也就是说,可以完整地将图片显示出来。 |
| 缩放 |
aspectFill |
保持纵横比缩放图片,只保证图片的短边能完全显示出来。 也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。 |
| 缩放 |
widthFix |
宽度不变,高度自动变化,保持原图宽高比不变 |
| 缩放 |
heightFix |
高度不变,宽度自动变化,保持原图宽高比不变 App 和 H5 平台 HBuilderX 2.9.3+ 支持、微信小程序需要基础库 2.10.3 |
| 裁剪 |
top |
不缩放图片,只显示图片的顶部区域 |
| 裁剪 |
bottom |
不缩放图片,只显示图片的底部区域 |
| 裁剪 |
center |
不缩放图片,只显示图片的中间区域 |
| 裁剪 |
left |
不缩放图片,只显示图片的左边区域 |
| 裁剪 |
right |
不缩放图片,只显示图片的右边区域 |
| 裁剪 |
top left |
不缩放图片,只显示图片的左上边区域 |
| 裁剪 |
top right |
不缩放图片,只显示图片的右上边区域 |
| 裁剪 |
bottom left |
不缩放图片,只显示图片的左下边区域 |
| 裁剪 |
bottom right |
不缩放图片,只显示图片的右下边区域 |
文本展示
轮播图
1 2 3 4 5 6 7 8 9 10 11
| <swiper class="swiper" circular :indicator-dots="true" :autoplay="true" interval="5000" duration="500"> <swiper-item> <view class="swiper-item uni-bg-red"></view> </swiper-item> <swiper-item> <view class="swiper-item uni-bg-green"></view> </swiper-item> <swiper-item> <view class="swiper-item uni-bg-blue"></view> </swiper-item> </swiper>
|
官方文档
https://uniapp.dcloud.net.cn/component/swiper.html
其中
interval 是多长时间切换下一张 单位毫秒
duration 一张切换的时长 单位毫秒
样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| .swiper { height: 386rpx; background-color: white;
.swiper-item { height: 100%; width: 100%; }
.uni-bg-red { background-color: red; }
.uni-bg-green { background-color: green; }
.uni-bg-blue { background-color: blue; } }
|
输入类组件
输入框
1 2 3 4 5 6
| <input class="z_input_border" focus v-model="inRealName" placeholder="请输入姓名" />
|
多行输入
1
| <textarea class="info_detail" disabled auto-height :value="info_text"></textarea>
|
输入
1 2 3 4 5 6 7 8
| <textarea v-if="seletList[seletIndex].value == 110" class="info_detail z_margin_top_l" style="height: 200rpx" :maxlength="100" v-model="info_text" > </textarea>
|
选择类组件
单选
单选
1 2
| <radio value="r1" :checked="false" /> <radio value="r1" :checked="true" />
|
注意
radio上没法绑定事件,绑定事件需要外面套一个radio-group。
大小调整
1
| <radio value="r1" checked="false" style="transform: scale(0.7)" />
|
单选和文字
1
| <label class="radio"><radio value="r1" checked="true" />选中</label>
|
样式调整
1
| <radio value="r1" :checked="true" color="#fa7e31" style="transform: scale(0.7)" />
|
单选按钮组
1 2 3 4 5 6
| <radio-group @change="radioChange"> <label class="radio" v-for="(item, index) in genderArr" :key="index"> <radio :value="item" color="#fa7e31" :checked="gender === item" /> {{ item }} </label> </radio-group>
|
方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| export default { data() { return { genderArr: ['男', '女'], gender: "女" } }, methods: { radioChange: function(evt) { this.gender = evt.detail.value; } } }
|
注意
radio-group上不支持绑定值,还得在radio上控制选中与未选中。
对象列表
1 2 3 4 5 6 7 8 9 10 11 12
| <radio-group @change="radioChange"> <view v-for="(item, index) in seletList" :key="index"> <label class="radio z_flex_h_l_r"> {{ item.name }} <radio :value="index" color="#fa7e31" :checked="seletIndex === index" /> </label> </view> </radio-group>
|
JS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| export default { data() { return { orderCode: '', info_text: '', seletList: [ { name: '押金问题', value: 10 }, { name: '租金问题', value: 20 }, { name: '多拍/错拍', value: 30 } ], seletIndex: 0 }; }, methods: { radioChange: function (evt) { this.seletIndex = evt.detail.value; }, } };
|
弹出菜单
在支付宝小程序中是中间弹出,有的是底部弹出
https://uniapp.dcloud.net.cn/component/uniui/uni-combox.html
1 2 3 4 5 6 7 8
| <picker @change="bindPickerChange" :value="seletIndex" range-key="name" :range="seletList" > <view class="uni-input">{{ seletList[seletIndex].name }}</view> </picker>
|
JS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { reactive, ref } from 'vue'; let seletList = reactive([ { name: '顺丰快递', value: 1 }, { name: '德邦快递', value: 2 } ]);
const seletIndex = ref(0); function bindPickerChange(e) { seletIndex.value = e.detail.value; }
|
选项式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| export default { data() { return { seletList: [ { name: '顺丰快递', value: 1 }, { name: '德邦快递', value: 2 } ], seletIndex: 0 }; }, methods: { bindPickerChange(e) { this.seletIndex = e.detail.value; } } };
|
注意
e.detail.value不是对象中的value,而是选中项的索引。
弹出层
https://ext.dcloud.net.cn/plugin?name=uni-popup
https://uniapp.dcloud.net.cn/component/uniui/uni-popup.html
使用
1
| <uni-popup ref="popup" type="center" :animation="false" :safe-area="false">中间弹出 Popup</uni-popup>
|
打开关闭
1 2 3 4 5 6
| openPop() { this.$refs.popup.open() }, close() { this.$refs.popup.close() }
|
或者代码中指定位置
1 2
| this.$refs.popup.open('bottom');
|
蒙版点击不关闭
1 2 3 4 5 6 7 8
| <uni-popup ref="popup" type="center" :is-mask-click="false" :animation="false" :safe-area="false" > </uni-popup>
|
弹出方位
模板中type支持的类型如下:
但是不建议用后面三种,这三种本质还是上面的5种。
| 属性名 |
说明 |
| top |
顶部弹出 |
| center |
居中弹出 |
| bottom |
底部弹出 |
| left |
左侧弹出 |
| right |
右侧弹出 |
| message |
预置样式 :消息提示 |
| dialog |
预置样式 :对话框 |
| share |
预置样式 :底部弹出分享示例 |
问题
下弹窗不显示
当从下面弹出的时候默认的:safe-area="true"会导致弹窗不显示,改成false就可以了。
1 2 3
| <uni-popup ref="popup" type="bottom" :animation="true" :safe-area="false"> <comp-goods-stats-pop></comp-goods-stats-pop> </uni-popu
|
禁用页面滚动
使用组件时,会发现内容部分滚动到底时,继续划动会导致底层页面的滚动,这就是滚动穿透。
但由于平台自身原因,除了h5平台外 ,其他平台都不能在在组件内禁止滚动穿透,所以页面内需要用户特殊处理一下。
弹出层显示的时候禁止页面滚动
1 2 3 4 5
| <view class="goods_detail_outer" :style="{ overflow: popIsShow ? 'hidden' : 'visible' }"></view>
<uni-popup ref="popup" type="bottom" :animation="true" :safe-area="false" @change="popStateChange"> <comp-goods-stats-pop></comp-goods-stats-pop> </uni-popup>
|
JS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export default { data() { return { popIsShow: false }; }, methods: { openZulinPop() { this.$refs.popup.open(); }, close() { this.$refs.popup.close(); }, popStateChange(e) { this.popIsShow = e.show; }, } };
|
日期选择
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <picker mode="date" :value="date" start="2020-01-01" end="2030-01-01" @change="bindDateChange"> <view class="z_text_base">{{ date }}</view> </picker>
<script>
import moment from 'moment'; moment.locale('zh-cn');
export default { data() { return { date: '2024-07-29' }; }, methods: { bindDateChange(e) { this.date = moment(e.detail.value).format('YYYY-MM-DD'); } } }; </script>
|
安装
引用
1 2 3
| import moment from 'moment'; moment.locale('zh-cn');
|
使用
1
| moment().format('YYYY-MM-DD');
|
图片选择
https://uniapp.dcloud.net.cn/component/uniui/uni-file-picker.html
模板
1
| <uni-file-picker limit="3" title="最多选择3张图片" @select="selectImg" @delete="delIMG"></uni-file-picker>
|
图片上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| export default { data() { return { imgPathArr: [], imageUrlArr: [] }; }, methods: { async selectImg(e) { const tempFilePaths = e.tempFilePaths; for (let i = 0; i < tempFilePaths.length; i++) { let imgPath = tempFilePaths[i]; this.imgPathArr.push(imgPath); try { let imgUrl = await this.uploadFile(imgPath); this.imageUrlArr.push(imgUrl); } catch (e) {} } console.info(this.imgPathArr); console.info(this.imageUrlArr); }, delIMG(e) { let delIndex = this.imgPathArr.indexOf(e.tempFilePath); console.info(e.tempFilePath); console.info('删除的索引为:' + delIndex); this.imgPathArr.splice(delIndex, 1); this.imageUrlArr.splice(delIndex, 1); console.info(this.imgPathArr); console.info(this.imageUrlArr); }, uploadFile(imgPath) { return new Promise((resolve, reject) => { uni.uploadFile({ url: 'https://xxx.xxx.com/api/uploadpic2/', filePath: imgPath, name: 'file', header: { 'Content-Type': 'multipart/form-data' }, success: (response) => { let obj = JSON.parse(response.data); resolve(obj.imgUrl); } }); }); } } };
|
选择文件的回调或者删除回调地址都是类似于下面的形式
1
| https://resource/apmlce3576a795169631d401bbc08d272fba.png
|
收货地址选择
JS
1 2 3 4 5 6 7 8
| uni.chooseAddress({ success: (res) => { console.info(JSON.stringify(res)); }, fail: (err) => { console.log('选择地址失败:', err); } });
|
用户选择后返回的格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { "userName": "张XX", "telNumber": "15225178123", "countyName": "中国", "provinceName": "河南省", "cityName": "郑州市", "detailInfo": "河南省-郑州市-荥阳市-京城路街道索河路广武路交叉口XX家属院X楼", "result": { "address": "河南省-郑州市-荥阳市-京城路街道索河路广武路交叉口XX家属院X楼", "area": "荥阳市", "city": "郑州市", "country": "中国", "fullname": "张XX", "mobilePhone": "15225178123", "prov": "河南省", "street": "" }, "resultStatus": "9000" }
|
可以看出
外层和result中的数据基本一致,内层的会更详细一点。
建议在这样写
支付宝的详细地址中新添加的是没有省市区的,但是之前的地址详情中却有,这里判断了一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| chooseAddress() { uni.chooseAddress({ success: (res) => { this.username = res.userName; this.userphone = res.telNumber; let ssqStr = `${res.result.prov}-${res.result.city}-${res.result.area}`; let addressStr = res.result.address; if (addressStr.indexOf(ssqStr) != -1) { this.userAddress = addressStr; } else { let addressAllStr = `${ssqStr}-${res.result.address}`; this.userAddress = addressAllStr; } }, fail: (err) => { console.log('选择地址失败:', err); } }); }
|
web-view
web-view 是一个 web 浏览器组件,可以用来承载网页的容器,会自动铺满整个页面。
小程序仅支持加载网络网页,不支持本地html。
小程序端 web-view 组件一定有原生导航栏,下面一定是全屏的 web-view 组件,navigationStyle: custom 对 web-view 组件无效,外层嵌套view也不起作用。
定义样式(小程序无效)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <view> <web-view :webview-styles="webviewStyles" src="https://www.psvmc.cn/zjtools/z/browserinfo/index.html"></web-view> </view> </template>
<script> export default { data() { return { webviewStyles: { progress: { color: '#fa7e31' } } } } } </script>
|
富文本
1 2
| <rich-text :nodes="getHtmlFitImg(goodsDetail.comm)"> </rich-text>
|
图片处理,让详情中的图片的最大宽度为100%
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| getHtmlFitImg(htmlStr) { if (htmlStr) { return htmlStr.replace( /(<img\s+.*?style=")(.*?)(".*?>)/g, function (match, p1, p2, p3) { var newStyle = 'max-width:100%;height:auto;display:block;margin:0 auto'; return p1 + newStyle + p3; } ); } else { return ''; } },
|
复制到剪贴板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function copyTextToClipboard(text) { uni.setClipboardData({ data: text, success: function () { uni.showToast({ title: '复制成功', icon: 'success', duration: 2000 }); } }); }
copyTextToClipboard('要复制的文本内容');
|
自定义组件
传统vue组件,需要安装、引用、注册,三个步骤后才能使用组件。
easycom将其精简为一步。
只要组件安装在项目的components目录下或uni_modules目录下,并符合components/组件名称/组件名称.(vue|uvue)目录结构,就可以不用引用、注册,直接在页面中使用。
状态栏占位组件
状态栏我们可能每个页面都用,为了方便,我们可以定义为组件。
文件目录/components/z-statusbar/z-statusbar.vue
页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <template> <view class="status_bar" :style="{ height: windowInfo.statusBarHeight + 'px' }"></view> </template>
<script> export default { name: 'z-statusbar', data() { return { windowInfo: {} }; }, beforeMount() { this.windowInfo = uni.getWindowInfo(); } }; </script>
<style> .status_bar { height: var(--status-bar-height); width: 100%; flex: none; } </style>
|
这样我们就可以直接使用了
1
| <z-statusbar></z-statusbar>
|
导航条占位组件
文件目录/components/z-navigationbar/z-navigationbar.vue
页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <view class="navigation_bar"></view> </template>
<script> export default { name: 'z-navigationbar', data() { return {}; } }; </script>
<style> .navigation_bar { height: 44px; width: 100%; flex: none; } </style>
|
这样我们就可以直接使用了
1
| <z-navigationbar></z-navigationbar>
|
拨打电话
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| callPhone(phone) { if (!phone) { return; } uni.makePhoneCall({ phoneNumber: phone, success: function () { console.log('拨打电话成功'); }, fail: function () { console.log('拨打电话失败'); } }); }
|