布局 · 样式
在 uni-app 或者 小程序前端布局过程中,我们经常会使用一个 view 包裹一个图片组件,您会发现图片底部会产生一小块空白,如下代码 :
<template>
<view style="background:#F97D7C;">
<image
style="width:100%;"
src="https://img0.baidu.com/it/u=1239913398,4122473254&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=312"
mode="widthFix"
></image>
</view>
</template>
运行效果 :
在 view 上添加样式 font-size:0; 即可。
<template>
<view style="background:#F97D7C; font-size:0;">
<image
style="width:100%;"
src="https://img0.baidu.com/it/u=1239913398,4122473254&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=312"
mode="widthFix"
></image>
</view>
</template>
nvue 可以通过 page.json 来设置页面背景颜色
{
"pages": [
{
"path" : "pages/test/test",
"style" :
{
"navigationBarTitleText": "页标题",
// 设置页面背景色
"app-plus" : {
"background":"#FF0000"
}
}
},
......
]
}
在页面 style 中修改 page 背景样式即可,注意不能使用 scoped,其他页面样式使用2个独立的 style 标签来实现。
如 : index.vue
<style>
/* #ifndef APP-NVUE */
page{background-color: #3688FF;}
/* #endif */
</style>
<style scoped>
.red{color: red;}
</style>
在开发过程中我们需要根据项目的实际需求来扩展一套图标库,uni-app 图标扩展步骤如下 :
以阿里图标库 ( https://iconfont.cn/ ) 为例 :
通过搜索找到图标 > 加入到购物车 > 添加到具体的项目,如 : test
点击 项目配置 按钮打开配置界面 :
除 FontClass 前缀,修改 Font Family 为自己的字体名称,如 test。
点击 下载至本地 按钮,下载图标文件。
将下载获得的字体文件 iconfont.ttf 文件 重命名为自己的字体文件,如 test.ttf ,然后复制此文件到 uni-app 项目的 static 目录。
在 app.vue 的 style 内或者 自己创建一个 css 文件,编写一下代码 :
/* 自定义图标 */
/* #ifndef APP-NVUE */
@font-face{
font-family : "my-icons";
font-weight : normal;
font-style : normal;
src : url('@/static/test.ttf') format('truetype');
}
.my-icons{font-family:"my-icons"; font-style:normal;}
/* #endif */
注意
1. 字体名称对应 .my-icons 内的 font-family 属性。
2. 利用条件编译解决多平台兼容问题( 此处定义非 nvue 平台 )。
通过上面的配置您就可以在项目中直接使用图标, 语法如下 :
<text class="my-icons">图标 unicode</text>
示例 :
<template>
<view class="gui-margin">
<text class="my-icons">图标 unicode 在阿里图标库对应项目<可以获取/text>
</view>
</template>
将下载获得的字体文件 iconfont.ttf 文件 重命名为自己的字体文件,如 test.ttf ,然后复制此文件到 uni-app 项目的 static 目录。
在 app.vue 内利用条件编译加载字体 :
<script>
export default {
onLaunch: function() {
// #ifdef APP-PLUS
plus.screen.lockOrientation('portrait-primary'); //锁定屏幕
const dom = weex.requireModule('dom');
dom.addRule('fontFace', {
'fontFamily': "myIcons",
'src': "url('/static/test.ttf')"
});
// #endif
},
onShow: function() {},
onHide: function() {}
}
</script>
<style>
/* #ifdef APP-NVUE */
.my-icons{font-family:myIcons;}
/* #endif */
</style>
语法 : <text class="my-icons">图标 unicode</text>
示例 :
<template>
<view class="gui-margin">
<text class="my-icons">图标 unicode 在阿里图标库对应项目<可以获取/text>
</view>
</template>
在 uni-app 中利用 rich-text 组件可以实现图标的动态化,
演示代码 :
<template>
<view class="gui-margin">
<view class="gui-margin-top">
<rich-text :nodes="icon" type="text" class="gui-icons"></rich-text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
icon:'<span>图标编码</span>'
}
},
onLoad:function(){
setTimeout(()=>{
this.icon = '<span>图标编码</span>'
}, 2000);
}
}
</script>
<style scoped>
.gui-card-item{margin-right:20rpx;}
</style>
通过上面的代码我们就可以实现图标的动态展示与切换,核心工作是对 rich-text 组件的动态赋值。
https://uniapp.dcloud.io/component/rich-text
当 switch 组件在 v-for 循环中, 某个具体 switch 发生变化时会触发 switch 组件的 @change 事件,我们可以利用 dataset 属性标注组件索引,在事件对应的逻辑中利用 dataset 属性获取到组件的索引,从而更精准的控制他们。
举一反三 : 其他组件也可以使用此处方法进行识别。
<组件 data-属性名称 ... >
e.currentTarget.dataset.属性名称
<template>
<view>
<view style="margin-top:80rpx;">
<switch
:data-idx="idx"
v-for="(item, idx) in [1,2,3,4,5]"
checked
@change="switchChange" />
</view>
</view>
</template>
<script>
var graceJS = require('@/GraceUI5/js/grace.js');
export default {
data() {
return {}
},
methods:{
switchChange:function(e){
console.log(e);
// 利用dataset 获取索引
var idx = e.currentTarget.dataset.idx;
console.log('当前 switch 索引为 : ' + idx);
// 获取到索引即可通过索引进行更多逻辑代码编写
}
}
}
</script>
<style>
</style>
官方手册 :
https://uniapp.dcloud.io/api/system/clipboard?id=setclipboarddata
<template>
<view style="padding:50rpx; text-align:center;">
<text data-content="被复制的文本内容" @tap="copy">点击这里复制文本</text>
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
copy : function(e){
uni.setClipboardData({
// 复制内容
data: e.currentTarget.dataset.content,
success: function () {
console.log('success');
uni.showToast({
title:"复制成功"
})
}
});
}
}
}
</script>
</script>
<style>
</style>
<template>
<view>
<text>{{textContent}}</text>
</view>
</template>
<script>
export default {
data() {
return {
textContent : 'hi'
}
},
onLoad:function(){
// 丢失 this
setTimeout(function(){
this.textContent = 'test....';
// this 代表当前 function
// 所以无法完成对 textContent 变量赋值的工作
}, 1000);
},
methods: {},
}
</script>
<style>
</style>
<template>
<view>
<text>{{textContent}}</text>
</view>
</template>
<script>
export default {
data() {
return {
textContent : 'hi'
}
},
onLoad:function(){
// 丢失 this
setTimeout(()=>{
this.textContent = 'test....';
// this 被保持
}, 1000);
},
methods: {},
}
</script>
<style>
</style>
<template>
<view>
<text>{{textContent}}</text>
</view>
</template>
<script>
var _this;
export default {
data() {
return {
textContent : 'hi'
}
},
onLoad:function(){
_this = this;
setTimeout(function(){
_this.textContent = 'test....';
// this 被保持
}, 1000);
},
methods: {},
}
</script>
<style>
</style>
JavaScript 将时间戳转换为刚刚、几分钟前、几小时前、几天前 ... 代码分享 :
将时间戳转换为刚刚、几分钟前、几小时前、几天前
将时间戳转换为日期时间
<template>
<view>
{{fromTime(1643170590)}}
</view>
</template>
<script>
export default {
methods:{
// 将时间戳转换为刚刚、几分钟前、几小时前、几天前
fromTime : function(time){
if(time < 90000000000 ){time *= 1000;}
var timer = new Date().getTime() - time;
timer = parseInt(timer / 1000);
if(timer < 180){
return '刚刚';
}else if(timer >= 180 && timer < 3600){
return parseInt(timer / 60) + '分钟前';
}else if(timer >= 3600 && timer < 86400){
return parseInt(timer / 3600) + '小时前';
}else if(timer >= 86400 && timer < 2592000){
return parseInt(timer / 86400) + '天前';
}else{
return this.toDate(time, 'str');
}
},
// 时间戳转换为日期时间
toDate : function(timeStamp, returnType){
timeStamp = parseInt(timeStamp);
var date = new Date();
if(timeStamp < 90000000000 ){
date.setTime(timeStamp * 1000);
}else{
date.setTime(timeStamp );
}
var y = date.getFullYear();
var m = date.getMonth() + 1;
m = m < 10 ? ('0' + m) : m;
var d = date.getDate();
d = d < 10 ? ('0' + d) : d;
var h = date.getHours();
h = h < 10 ? ('0' + h) : h;
var minute = date.getMinutes();
var second = date.getSeconds();
minute = minute < 10 ? ('0' + minute) : minute;
second = second < 10 ? ('0' + second) : second;
if(returnType == 'str'){return y + '-' + m + '-' + d + ' '+ h +':' + minute + ':' + second;}
return [y, m, d, h, minute, second];
}
}
}
</script>
<style>
</style>
分享几种 javascript 遍历对象的方法
<script>
const obj = {
id:1,
name:'zhangsan',
age:18
}
for(let key in obj){
console.log(key + '---' + obj[key])
}
</script>
<script>
const obj = {
id:1,
name:'zhangsan',
age:18
}
Object.keys(obj).forEach((key)=>{
console.log(key+' : '+obj[key]);
});
</script>
<script>
const obj = {
id:1,
name:'zhangsan',
age:18
}
Object.getOwnPropertyNames(obj).forEach(function(key){
console.log(key+ '---'+obj[key])
})
</script>
在 JavaScript 中,Set 是一个集合,它允许你仅存储唯一值。这意味着删除任何重复的值。 因此,要从数组中删除重复项,你可以将其转换为集合,然后再转换回数组。
const numbers = [1, 1, 20, 3, 3, 3, 9, 9];
const uniqueNumbers = [...new Set(numbers)]; // -> [1, 20, 3, 9]
1) new Set(numbers)从数字列表中创建一个集合。创建集合会自动删除所有重复值。
2) 展开运算符...将任何可迭代对象转换为数组。这意味着将集合转换回数组。[...new Set(numbers)]
在日常应用开发过程中,经常遇到后端 api 接口输出的数据格式与前端组件要求的数据格式不一样的情况,我们可以通过数据转换来解决这个问题,详解下面的代码 :
<template>
<view class="gui-padding">
<view class="gui-margin-top">
<picker
@change="bindPickerChange"
:value="pickerIndex"
:range="pickerArray">
<view>{{pickerArray[pickerIndex]}}</view>
</picker>
</view>
</view>
</template>
<script>
export default {
data() {
return {
pickerArray : [],
pickerIndex : 0,
apiData : []
}
},
onReady: function (options) {
//模拟api 请求
setTimeout(()=>{
// 假设 api 返回的数据格式如下:
var apiData = [{id:0, name:"全部"}, {id:1, name:"男"}, {id:2, name:"女"}];
this.apiData = apiData;
// 而 picker 组件需要的格式为 ['全部', '男', '女']
// 解决方案 : 遍历 api 数据并转换为 picker 要求的形式
var pickerData = [];
for(let i = 0; i < apiData.length; i++){
pickerData.push(apiData[i].name);
}
this.pickerArray = pickerData;
}, 300);
},
methods: {
bindPickerChange : function (e) {
console.log(e);
// 选择索引
this.pickerIndex = e.detail.value;
// 对应选择的 api 数据
console.log(this.apiData[this.pickerIndex]);
// api 数据名称, 获取id 则 .id 即可
console.log(this.apiData[this.pickerIndex].name);
// 对应选择的转换后的 picker 数据
console.log(this.pickerArray[this.pickerIndex]);
}
}
}
</script>
<style>
</style>
我们可以将一些常用的功能以对象形式封装为一个 js 文件,然后在 main.js 中利用 uni.工具对象的方式进行全局挂载。这样我们就可以在每个页面内放本地使用自己封装的工具。
如 : 项目根目录下创建 /tools/ 文件夹并在此文件夹下创建 myTools.js,
示例代码 :
module.exports = {
// 如 : 基于 uni-app uni.showToast api 封装一个 msg 方法
msg : function(msg){
uni.showToast({title:msg, icon:"none"});
},
// 如 : 基于 uni-app uni.showLoading api 封装一个 showLoading 方法
showLoading : function (title) {
uni.showLoading({ title:title , mask:true });
}
}
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
App.mpType = 'app'
import myTool from './tools/myTool.js';
uni.myTool = myTool;
const app = new Vue({
...App
})
app.$mount()
<template>
<view></view>
</template>
<script>
export default{
onLoad:function(){
uni.myTool.msg('hi...');
}
}
</script>
<style>
</style>
是不是非常简单并好用呢~ 注意全局挂载基于 uni 对象,不要过大封装或者封装一些影响 uni 性能的代码。
微信小程序 在 2023年9月15日之后涉及处理用户个人信息的小程序开发者,需通过弹窗等明显方式提示用户阅读隐私政策等收集使用规则。我们在使用小程序与用户隐私相关接口( 如 : 获取用户信息、相册使用等 )时需要用户授权才能正常调用。
官方说明
https://developers.weixin.qq.com/miniprogram/dev/framework/user-privacy/PrivacyAuthorize.html
开发者需在「小程序管理后台」配置《小程序用户隐私保护指引》,详细指引可见:用户隐私保护指引填写说明。
需要注意的是,仅有在指引中声明所处理的用户信息,才可以调用平台提供的对应接口或组件。若未声明,对应接口或组件将直接禁用。隐私接口与对应的处理的信息关系可见:小程序用户隐私保护指引内容介绍。
此步骤需要填写 《用户隐私保护指引设置》,填写完成后等待微信官方审核。
从基础库 2.32.3 开始支持
开发者可通过 wx.getPrivacySetting 接口,查询微信侧记录的用户是否有待同意的隐私政策信息。该信息可通过返回结果 res 中的 needAuthorization 字段获取。uni-app 示例代码 :
<script>
export default {
data() {
return {
showPrivacy : false,
resolvePrivacyAuthorization : null
}
},
onLoad:function(){
// #ifdef MP-WEIXIN
wx.getPrivacySetting({
success: res => {
console.log(res)
// 返回结果为:
// res = { needAuthorization: true/false, privacyContractName: '《xxx隐私保护指引》' }
},
fail: () => {},
complete: () => {}
})
// #endif
},
methods: {
}
}
</script>
同时,wx.getPrivacySetting 接口会返回开发者在小程序管理后台配置的《小程序用户隐私保护指引》名称信息,开发者可以调用 wx.openPrivacyContract 接口打开该页面。
如果存在有待用户同意的隐私政策信息,开发者需要主动提示用户阅读隐私政策等收集使用规则,对于提示方式,小程序开发者可自行设计,同时需要在相关界面中使用 <button open-type="agreePrivacyAuthorization"> 组件,当用户轻触该 <button> 组件后,表示用户已阅读并同意小程序的隐私政策等收集使用规则,微信会收到该同步信息,此时开发者可以在该组件的 bindagreeprivacyauthorization 事件回调后调用已声明的隐私接口。
<template>
<gui-page>
<template v-slot:gBody>
<view class="">页面内容</view>
<!-- #ifdef MP-WEIXIN -->
<!-- 隐私授权弹窗 -->
<gui-modal
ref="guimodal"
width="660rpx"
:isCloseBtn="false"
title="用户隐私保护提示">
<template v-slot:content>
<scroll-view
scroll-y
:style="{height:contentHeight+'px'}"
class="gui-bg-gray gui-dark-bg-level-2">
<view style="padding:50rpx 30rpx;">
<view class="">
<text class="gui-block gui-text">感谢您使用**小程序,请阅读并同意</text>
</view>
<view style="padding:35rpx 0;">
<text class="gui-color-blue gui-h5" @tap="openXY">《用户隐私保护指引》点击打开</text>
</view>
<view>
<text class="gui-block gui-text">当您点击同意并开始时用产品服务时,即表示你已理解并同息该条款内容,该条款将对您产生法律约束力。如您拒绝,将无法正常使用**小程序。</text>
</view>
</view>
</scroll-view>
</template>
<template v-slot:btns>
<!-- 利用 flex 布局 可以放置多个自定义按钮哦 -->
<view class="gui-flex gui-rows gui-space-between">
<text
class="modal-btns gui-block-text gui-color-gray"
@tap="disagree">不同意</text>
<button
type="default"
class="gui-button gui-noborder modal-btns"
id="agree-btn"
:plain="true"
open-type="agreePrivacyAuthorization"
@agreeprivacyauthorization="handleAgreePrivacyAuthorization">
<text
class="gui-color-green gui-button-text">同意</text>
</button>
</view>
</template>
</gui-modal>
<!-- #endif -->
</template>
</gui-page>
</template>
<script>
import graceJS from '@/Grace6/js/grace.js';
export default {
data() {
return {
showPrivacy : false,
resolvePrivacyAuthorization : null
}
},
onLoad:function(){
// #ifdef MP-WEIXIN
wx.getPrivacySetting({
success: res => {
console.log(res)
// 返回结果为:
// res = { needAuthorization: true/false, privacyContractName: '《xxx隐私保护指引》' }
// 需要授权
if (res.needAuthorization){
graceJS.getRefs('guimodal', this, 0, (ref)=>{
// 获取到 ref
ref.open();
});
}
},
fail: () => {},
complete: () => {}
})
// #endif
},
methods: {
// #ifdef MP-WEIXIN
openXY : function(){
wx.openPrivacyContract();
// 真机测试会打开协议页面
},
handleAgreePrivacyAuthorization:function(){
this.$refs.guimodal.close();
},
disagree:function(){
this.$refs.guimodal.close();
}
// #endif
}
}
</script>
<style scoped>
.modal-btns{line-height:88rpx; font-size:26rpx; text-align:center; width:200rpx;}
</style>
点击 微信开发者工具 清缓存 > 清除全部缓存 可以重置授权,进行测试。
防抖是指在频繁触发某一个事件时,一段时间内或者一定条件下不再触发该事件对应调用的函数。
<template>
<view>
<view style="margin-top:80rpx;">
<view style="width:200rpx;">
<button
type="default"
@tap="buttonTap">点击</button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 请求状态识别变量
requestStatus : false
}
},
methods:{
buttonTap:function(){
// 模拟按钮点击后会执行 api 请求,耗时 1 秒
// 请求完成前 按钮点击将不会继续执行此函数
if(this.requestStatus){
// 利用 return 终止函数继续运行
return false;
}
console.log('按钮点击函数执行');
// 执行函数
this.requestStatus = true;
setTimeout(()=>{
// 模拟执行完毕
// 改变 requestStatus
this.requestStatus = false;
}, 1000);
}
}
}
</script>
<style>
</style>
对按钮点击触发函数的防抖控制 : 按钮事件执行未完成时不多次重复执行对应按钮的业务逻辑。
在事件持续触发的前提下,保证一定时间段内只调用一次事件处理函数,就是函数节流;
利用可以延时器实现节流,原理是连续触发时只执行最后一次事件。
如下面的例子:
页面滚动(连续事件),我们利用 setTimeout() 函数只执行识别最后一次。
<template>
<view>
<view style="margin-top:80rpx;">
<view style="height:2000px; width:750rpx; background-color: #006065;">
滚动页面试试~~
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 延时器对象
scrollTimer : null
}
},
methods:{},
onPageScroll:function(e){
if(this.scrollTimer != null){
clearTimeout(this.scrollTimer);
}
this.scrollTimer = setTimeout(()=>{
console.log(e);
}, 100)
}
}
</script>
<style>
</style>
利用计时器可以实现在指定的间隔时间内只执行一次逻辑,从而控制逻辑执行次数,达到节流的目的。
示例代码 :
<template>
<view>
<view style="margin-top:80rpx;">
<view style="height:20000px; width:750rpx; background-color: #006065;">
滚动页面试试~~
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 计时器时间
countTime : 0,
// 计时器对象
countTimer : null
}
},
methods:{},
onPageScroll:function(e){
// 函数执行时先判断时间
if(this.countTime >= 10){
// 利用 return 终止函数继续运行
return false;
}
// 开始计时
this.countTimer = setInterval(()=>{
if(this.countTime >= 500){
this.countTime = 0;
clearInterval(this.countTimer);
return;
}
this.countTime += 10;
}, 10);
// 执行逻辑
console.log(e);
}
}
</script>
<style>
</style>