
关于 app跳转微信客服报错deeplink custmerservice no permission排查解决
1.准备工作,微信开放平企业认证微信开放平台app认证,企业微信企业认证共计600元
- 认证通过后在企业微信中>微信客服 绑定 微信开放平台认证的app
- 在manifest.json>app模块配置>share分享填入 开放平台认证的appid
- 重新打自定义包(标准基座会报错deeplink custmerservice no permission)
- 填写企业微信的id plus中的方法备注的是填写g_开头的不要管他
附上代码openWeixinService(e){ let sweixin = null plus.share.getServices(res=>{ sweixin = res.find(i => i.id === 'weixin') if(sweixin){ sweixin.openCustomerServiceChat({ corpid: '企业微信id', //获取链接https://work.weixin.qq.com/wework_admin/frame#/profile url: '客服链接', //获取链接https://work.weixin.qq.com/wework_admin/frame#/app/servicer },suc=>{ console.log("success",JSON.stringify(res)) },err=>{ console.log("error",JSON.stringify(err)) }) }else{ plus.nativeUI.alert('当前环境不支持微信操作!') } },function(){ uni.showToast({title: "获取服务失败,不支持该操作。"+JSON.stringify(e), icon: 'error'}) }) }
1.准备工作,微信开放平企业认证微信开放平台app认证,企业微信企业认证共计600元
- 认证通过后在企业微信中>微信客服 绑定 微信开放平台认证的app
- 在manifest.json>app模块配置>share分享填入 开放平台认证的appid
- 重新打自定义包(标准基座会报错deeplink custmerservice no permission)
- 填写企业微信的id plus中的方法备注的是填写g_开头的不要管他
附上代码openWeixinService(e){ let sweixin = null plus.share.getServices(res=>{ sweixin = res.find(i => i.id === 'weixin') if(sweixin){ sweixin.openCustomerServiceChat({ corpid: '企业微信id', //获取链接https://work.weixin.qq.com/wework_admin/frame#/profile url: '客服链接', //获取链接https://work.weixin.qq.com/wework_admin/frame#/app/servicer },suc=>{ console.log("success",JSON.stringify(res)) },err=>{ console.log("error",JSON.stringify(err)) }) }else{ plus.nativeUI.alert('当前环境不支持微信操作!') } },function(){ uni.showToast({title: "获取服务失败,不支持该操作。"+JSON.stringify(e), icon: 'error'}) }) }

解决 隐藏原生tabbar之后,手机屏幕下方会出现白色区域的问题
在源码视图中添加"safearea": {"bottom": { "offset": "none" }
在源码视图中添加"safearea": {"bottom": { "offset": "none" }

基于微信小程序的汽车电影院购票系统
概览:
随着科技的飞速发展,微信小程序作为一种轻量级、便捷的应用形式,已经深入到人们的日常生活中。在汽车电影院这一特色娱乐方式日益受欢迎的今天,开发一款基于微信小程序的汽车电影院购票系统,不仅能够提升观众的购票体验,还能优化影院的管理流程。本文将详细介绍该系统的设计思路、功能模块及实现方式。
系统设计思路
- 需求分析
用户端:提供便捷的购票、查看电影排期、在线支付等功能。

管理端:实现电影排片管理、订单处理、数据分析等功能。
功能模块
- 用户端功能模块
1.1 电影排期查询
用户可以在小程序首页查看当前及未来的电影排期信息,包括电影名称、放映时间、放映场地等。
1.2 在线支付
系统支持多种支付方式,确保用户能够快速完成支付流程。
1.3 我的订单
用户可以在“我的”页面查看历史订单,包括待支付、已支付、已取消等订单状态。
1.4 在线核销功能
用户到达影院后,只需打开小程序中的电子票券,出示二维码给工作人员扫描,即可快速完成票券的验证与核销,无需排队等待,极大提升了观影的便捷性。
1.5 租赁服务功能
用户可在小程序内轻松选择并租赁汽车电影院提供的设备与服务,如音响设备等,满足个性化观影需求,让观影体验更加舒适便捷。
- 管理端功能模块
2.1 电影排片管理
管理员可以根据影片资源和场地情况,灵活设置电影排片信息,包括放映时间、放映场地、票价等。
2.2 订单处理
管理员可以查看所有订单的详细信息,包括订单状态、支付情况等,并对订单进行人工干预,如取消订单、退款等。
联系我们
概览:
随着科技的飞速发展,微信小程序作为一种轻量级、便捷的应用形式,已经深入到人们的日常生活中。在汽车电影院这一特色娱乐方式日益受欢迎的今天,开发一款基于微信小程序的汽车电影院购票系统,不仅能够提升观众的购票体验,还能优化影院的管理流程。本文将详细介绍该系统的设计思路、功能模块及实现方式。
系统设计思路
- 需求分析
用户端:提供便捷的购票、查看电影排期、在线支付等功能。
管理端:实现电影排片管理、订单处理、数据分析等功能。
功能模块
- 用户端功能模块
1.1 电影排期查询
用户可以在小程序首页查看当前及未来的电影排期信息,包括电影名称、放映时间、放映场地等。
1.2 在线支付
系统支持多种支付方式,确保用户能够快速完成支付流程。
1.3 我的订单
用户可以在“我的”页面查看历史订单,包括待支付、已支付、已取消等订单状态。
1.4 在线核销功能
用户到达影院后,只需打开小程序中的电子票券,出示二维码给工作人员扫描,即可快速完成票券的验证与核销,无需排队等待,极大提升了观影的便捷性。
1.5 租赁服务功能
用户可在小程序内轻松选择并租赁汽车电影院提供的设备与服务,如音响设备等,满足个性化观影需求,让观影体验更加舒适便捷。
- 管理端功能模块
2.1 电影排片管理
管理员可以根据影片资源和场地情况,灵活设置电影排片信息,包括放映时间、放映场地、票价等。
2.2 订单处理
管理员可以查看所有订单的详细信息,包括订单状态、支付情况等,并对订单进行人工干预,如取消订单、退款等。
联系我们
收起阅读 »
鲜橙科技-智慧社区管理系统项目概述
1.1 时代背景
随着科技的飞速发展,智慧城市已成为全球城市发展的新趋势。智慧社区,作为智慧城市的重要基石,正以其独特的魅力,引领着未来居住环境的变革。
它依托物联网、云计算、大数据等前沿技术,将传统社区升级为安全、节能、便捷的现代生活空间,为居民带来前所未有的生活体验。
1.2 智慧社区的定义与内涵
智慧社区,简而言之,就是运用现代信息技术手段,实现对社区内各类资源的全面感知、智能整合与高效利用,从而提升居民生活质量,构建和谐宜居的社区环境。
它不仅涵盖了智慧物业管理、智能家居系统、社区电子商务等硬件设施的智能化升级,更涉及社区治理、信息服务、健康养老等软性服务的全面优化。
1.3 界面展示及系统功能概览
1.区驾驶舱:
作为智慧社区的中枢,区驾驶舱提供了对整个社区运营状况的实时监控和管理,帮助管理者全面掌握社区的各项数据和运行状态。
2.社区信息管理:
社区信息管理确保信息流通顺畅,人房关系管理优化住房资源配置,小区院落管理提升居住环境。同时,人员信息管理便于快速响应居民需求,组织机构管理促进协同合作,社区标签增强社区特色。
第三方平台链接丰富服务选择,居民信息审核保障信息真实性。网格化管理实现精细化服务,提升居民满意度。整体上,这些功能共同推动社区和谐发展。
3.辖区管理:
辖区管理确保社区秩序;选民登记投票促进民主参与;线上议事提高决策透明度;居务公开增强公信力;辖内企业管理促进经济发展;活动组织管理丰富居民生活;社区警务保障安全;社区通讯录方便联系;民意反馈倾听民声;问卷调查收集意见。
4.社区党建:
聚焦社区党建,包括党建指导、组织党员管理、活动中心运营及新闻动态发布。党建指导加强基层组织建设,提升党员素质。党员管理确保信息全面,增强归属感。
5.社区共建共治系统
包括社区共建共治系统、志愿服务、社区救助、随手拍、居民自治、社区纠纷处理和社区服务。共建共治系统促进居民参与,增强社区凝聚力;志愿服务培养社会责任感;社区救助及时帮助困难群体;随手拍便于居民反馈问题;居民自治提升自主管理;纠纷处理维护社区和谐;社区服务满足居民多样化需求。
6.社区物业管理系统:
社区物业管理系统集成了多种功能,如监控组织架构绑定、IOT组织架构管理、智慧灯杆与物联网设备控制、车辆出入与门禁管理、公共区域与设施维护、摄像头事件监控、烟感设备监测等。
7.社区安全管控系统:
社区安全管控系统集成了多元化功能,包括人员、车辆、房屋、消防及警情等专题分析与数据维护。
联系我们
1.1 时代背景
随着科技的飞速发展,智慧城市已成为全球城市发展的新趋势。智慧社区,作为智慧城市的重要基石,正以其独特的魅力,引领着未来居住环境的变革。
它依托物联网、云计算、大数据等前沿技术,将传统社区升级为安全、节能、便捷的现代生活空间,为居民带来前所未有的生活体验。
1.2 智慧社区的定义与内涵
智慧社区,简而言之,就是运用现代信息技术手段,实现对社区内各类资源的全面感知、智能整合与高效利用,从而提升居民生活质量,构建和谐宜居的社区环境。
它不仅涵盖了智慧物业管理、智能家居系统、社区电子商务等硬件设施的智能化升级,更涉及社区治理、信息服务、健康养老等软性服务的全面优化。
1.3 界面展示及系统功能概览
1.区驾驶舱:
作为智慧社区的中枢,区驾驶舱提供了对整个社区运营状况的实时监控和管理,帮助管理者全面掌握社区的各项数据和运行状态。
2.社区信息管理:
社区信息管理确保信息流通顺畅,人房关系管理优化住房资源配置,小区院落管理提升居住环境。同时,人员信息管理便于快速响应居民需求,组织机构管理促进协同合作,社区标签增强社区特色。
第三方平台链接丰富服务选择,居民信息审核保障信息真实性。网格化管理实现精细化服务,提升居民满意度。整体上,这些功能共同推动社区和谐发展。
3.辖区管理:
辖区管理确保社区秩序;选民登记投票促进民主参与;线上议事提高决策透明度;居务公开增强公信力;辖内企业管理促进经济发展;活动组织管理丰富居民生活;社区警务保障安全;社区通讯录方便联系;民意反馈倾听民声;问卷调查收集意见。
4.社区党建:
聚焦社区党建,包括党建指导、组织党员管理、活动中心运营及新闻动态发布。党建指导加强基层组织建设,提升党员素质。党员管理确保信息全面,增强归属感。
5.社区共建共治系统
包括社区共建共治系统、志愿服务、社区救助、随手拍、居民自治、社区纠纷处理和社区服务。共建共治系统促进居民参与,增强社区凝聚力;志愿服务培养社会责任感;社区救助及时帮助困难群体;随手拍便于居民反馈问题;居民自治提升自主管理;纠纷处理维护社区和谐;社区服务满足居民多样化需求。
6.社区物业管理系统:
社区物业管理系统集成了多种功能,如监控组织架构绑定、IOT组织架构管理、智慧灯杆与物联网设备控制、车辆出入与门禁管理、公共区域与设施维护、摄像头事件监控、烟感设备监测等。
7.社区安全管控系统:
社区安全管控系统集成了多元化功能,包括人员、车辆、房屋、消防及警情等专题分析与数据维护。
联系我们
收起阅读 »
鲜橙生鲜配送系统:引领生鲜配送行业的新篇章
鲜橙生鲜配送系统,作为一款专为生鲜配送行业量身定制的信息化管理软件,致力于全面提升生鲜配送企业的运营效率、管理水平和服务质量。在当今竞争激烈的生鲜市场中,鲜橙生鲜系统凭借其卓越的功能和先进的技术,正逐步成为行业内的佼佼者。
一、系统概述
鲜橙生鲜配送系统全面覆盖了从订单接收、采购、分拣、配送、库存管理、财务管理到客户关系管理等核心业务环节。通过高度集成的信息化手段,该系统实现了这些业务流程的自动化、数字化和智能化管理,为企业打造了一个高效、精准的运营体系。
二、核心功能
采购管理
基于实时的订单需求和精准的库存状况,鲜橙生鲜系统能够智能生成采购清单,减少人工计算错误,并通过对市场动态和供应商信息的深入分析,优化采购策略。系统还能智能对比供应商价格、品质及交货能力,帮助企业做出最优采购决策,降低采购成本。
录入订单
简单快捷的将订单录入,提高效率,保证正确度。
配送管理
为实现快速、准确的配送,鲜橙生鲜系统规划了最优配送路线,充分考虑交通、地址分布及车辆负载能力等因素。利用实时定位技术,系统对配送车辆进行实时监控,确保生鲜产品按时、完好地送达客户手中。
库存管理
鲜橙生鲜系统实现了对库存动态的实时监控,精确记录每一种生鲜产品的库存数量、入库时间、保质期等信息。当库存低于安全阈值时,系统自动发出预警,提醒补货。定期的库存盘点功能确保了库存数据的准确性和可靠性,提高了库存周转率。
财务管理
系统提供全面的财务管理功能,包括订单结算、账款管理、成本核算等。对每笔订单进行精确的收入支出计算,生成清晰财务报表。同时,系统跟踪客户付款情况,有效管理逾期账款,为企业的成本控制和利润优化提供数据支持。
三、系统优势
提高效率:自动化处理繁琐业务,减少人工操作,显著提升工作效率。
降低成本:优化采购、配送等环节,降低运营成本,提高企业利润。
保证质量:严格的质量控制和追溯体系,确保生鲜产品品质和安全。
提升服务:及时准确的配送和个性化服务,提升客户体验,增强市场竞争力。
数据决策:基于大数据分析,为企业决策提供科学依据,助力企业发展。
联系我们
鲜橙生鲜配送系统,作为一款专为生鲜配送行业量身定制的信息化管理软件,致力于全面提升生鲜配送企业的运营效率、管理水平和服务质量。在当今竞争激烈的生鲜市场中,鲜橙生鲜系统凭借其卓越的功能和先进的技术,正逐步成为行业内的佼佼者。
一、系统概述
鲜橙生鲜配送系统全面覆盖了从订单接收、采购、分拣、配送、库存管理、财务管理到客户关系管理等核心业务环节。通过高度集成的信息化手段,该系统实现了这些业务流程的自动化、数字化和智能化管理,为企业打造了一个高效、精准的运营体系。
二、核心功能
采购管理
基于实时的订单需求和精准的库存状况,鲜橙生鲜系统能够智能生成采购清单,减少人工计算错误,并通过对市场动态和供应商信息的深入分析,优化采购策略。系统还能智能对比供应商价格、品质及交货能力,帮助企业做出最优采购决策,降低采购成本。
录入订单
简单快捷的将订单录入,提高效率,保证正确度。
配送管理
为实现快速、准确的配送,鲜橙生鲜系统规划了最优配送路线,充分考虑交通、地址分布及车辆负载能力等因素。利用实时定位技术,系统对配送车辆进行实时监控,确保生鲜产品按时、完好地送达客户手中。
库存管理
鲜橙生鲜系统实现了对库存动态的实时监控,精确记录每一种生鲜产品的库存数量、入库时间、保质期等信息。当库存低于安全阈值时,系统自动发出预警,提醒补货。定期的库存盘点功能确保了库存数据的准确性和可靠性,提高了库存周转率。
财务管理
系统提供全面的财务管理功能,包括订单结算、账款管理、成本核算等。对每笔订单进行精确的收入支出计算,生成清晰财务报表。同时,系统跟踪客户付款情况,有效管理逾期账款,为企业的成本控制和利润优化提供数据支持。
三、系统优势
提高效率:自动化处理繁琐业务,减少人工操作,显著提升工作效率。
降低成本:优化采购、配送等环节,降低运营成本,提高企业利润。
保证质量:严格的质量控制和追溯体系,确保生鲜产品品质和安全。
提升服务:及时准确的配送和个性化服务,提升客户体验,增强市场竞争力。
数据决策:基于大数据分析,为企业决策提供科学依据,助力企业发展。
联系我们
收起阅读 »
横屏启动样式错乱强制竖屏
当我们横屏启动应用时,如果应用需要保持竖屏使用的,应用会先以横屏启动后再恢复竖屏,这时会导致样式错乱,查了很多方法没有解决我的问题。
想过操作webview,但是没实现
最后解决方案
const isLandScape = Math.abs(plus.navigator.getOrientation()) === 90
plus.screen.lockOrientation("portrait-primary")
if(isLandScape) plus.runtime.restart()
其他配置和官方说的一样
当我们横屏启动应用时,如果应用需要保持竖屏使用的,应用会先以横屏启动后再恢复竖屏,这时会导致样式错乱,查了很多方法没有解决我的问题。
想过操作webview,但是没实现
最后解决方案
const isLandScape = Math.abs(plus.navigator.getOrientation()) === 90
plus.screen.lockOrientation("portrait-primary")
if(isLandScape) plus.runtime.restart()
其他配置和官方说的一样

uniadmin上有个uni-id-co的用户函数代码错误或者逻辑异常
这个报错,个人复现出现的问题是,注册账号用的本地云,实际跑的时候用的云端,就会出错,兄弟们少走弯路,一路一律使用云端!这个破问题,我翻了接近一个多小时的文档。我把注册好的管理员账号删除了,然后用云端重新注册,再用云端跑就可以了,部署好野没问题了。这个项目过后,我也放弃uniapp了,从最开始apicloud那会儿,跑来使用uniapp来说,它确实是一款非常好的产品,免除了很多繁琐的工作,但问题也很明显,文档写的一塌糊涂,更新也不及时,内容也是过了几百年也不去补正,导致很多文档指向的链接根本就没有。写的有部分示例代码也让人看了之后产生分歧,还得挨个写测试。问答社区活跃度也堪忧,好多问题除了工作人员根本没人来解答,uniapp人手不足这个可以理解,但要做好一款产品只是靠大家理解,那可能远远不够。也希望以后的日子uniapp能更进一步。
这个报错,个人复现出现的问题是,注册账号用的本地云,实际跑的时候用的云端,就会出错,兄弟们少走弯路,一路一律使用云端!这个破问题,我翻了接近一个多小时的文档。我把注册好的管理员账号删除了,然后用云端重新注册,再用云端跑就可以了,部署好野没问题了。这个项目过后,我也放弃uniapp了,从最开始apicloud那会儿,跑来使用uniapp来说,它确实是一款非常好的产品,免除了很多繁琐的工作,但问题也很明显,文档写的一塌糊涂,更新也不及时,内容也是过了几百年也不去补正,导致很多文档指向的链接根本就没有。写的有部分示例代码也让人看了之后产生分歧,还得挨个写测试。问答社区活跃度也堪忧,好多问题除了工作人员根本没人来解答,uniapp人手不足这个可以理解,但要做好一款产品只是靠大家理解,那可能远远不够。也希望以后的日子uniapp能更进一步。
收起阅读 »
uniapp app自动监听权限
自动监听 onLaunch 引入 listener方法即可
监听权限
// @ts-nocheck
// #ifdef APP-PLUS
import permissions from "./permissions";
import {closeNotify, drawNotify} from "@/utils/notify";
const permissionListener = uni.createRequestPermissionListener()
// #endif
/**
* 监听权限
*/
export function listener() {
/* 是否已经弹出设置框 */
let hasConfirm = false
// 监听申请系统权限
permissionListener.onRequest((data: string[]) => {
})
// 监听弹出系统权限授权框
permissionListener.onConfirm((data: string[]) => {
const permissionsObj = getPermissionsObj(data)
drawNotify(permissionsObj.title, permissionsObj.desc)
})
// 监听权限申请完成
permissionListener.onComplete((data: string[]) => {
closeNotify()
const parts = data[data.length - 1].split('.');
let permissionName = parts[parts.length - 1];
const Manifest = plus.android.importClass('android.Manifest')
const MainActivity = plus.android.runtimeMainActivity()
const permissionStatus = MainActivity.checkSelfPermission(Manifest.permission[permissionName])
// 彻底拒绝
if (permissionStatus === -1 && !hasConfirm) {
hasConfirm = true
const permissionsObj = getPermissionsObj(data)
plus.nativeUI.confirm(permissionsObj.desc, (e) => {
if (e.index === 0) uni.openAppAuthorizeSetting()
hasConfirm = false
}, {
title: permissionsObj.title,
"buttons": ["去设置", "取消"],
});
}
})
}
function getPermissionsObj(data: string[]): { title: string, desc: string } {
let title: string = ""
let desc: string = ""
data.forEach((permission) => {
permissions.forEach(item => {
if (item.permissions.indexOf(permission) !== -1) {
title = item.title
desc = item.desc
}
})
})
return {title, desc}
}
权限设置
export interface Permissions {
title: string,
desc: string,
permissions: string[]
}
const permissions: Permissions[] = [
{
title: "读取短信权限",
desc: "读取短信权限达到个性化目的",
permissions: [
"android.permission.SEND_SMS",
"android.permission.RECEIVE_SMS",
"android.permission.READ_SMS",
"android.permission.RECEIVE_WAP_PUSH",
"android.permission.RECEIVE_MMS",
]
},
{
title: "读取存储卡,包括相册等权限",
desc: "读取存储卡,包括相册等权限达到个性化目的",
permissions: [
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE",
]
},
{
title: "读取联系人权限",
desc: "读取联系人权限达到个性化目的",
permissions: [
"android.permission.READ_CONTACTS",
"android.permission.WRITE_CONTACTS",
"android.permission.GET_ACCOUNTS",
]
},
{
title: "读取手机权限",
desc: "读取手机权限达到个性化目的",
permissions: [
"android.permission.READ_PHONE_STATE",
"android.permission.CALL_PHONE",
"android.permission.READ_CALL_LOG",
"android.permission.WRITE_CALL_LOG",
"android.permission.ADD_VOICEMAIL",
"android.permission.USE_SIP",
"android.permission.PROCESS_OUTGOING_CALLS",
]
},
{
title: "读取日历权限",
desc: "读取日历权限达到个性化目的",
permissions: [
"android.permission.READ_CALENDAR",
"android.permission.WRITE_CALENDAR",
]
},
{
title: "读取相机权限",
desc: "读取相机权限达到个性化目的",
permissions: [
"android.permission.CAMERA",
]
},
{
title: "读取位置权限",
desc: "读取位置权限达到个性化目的",
permissions: [
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.ACCESS_COARSE_LOCATION",
]
},
{
title: "读取传感器权限",
desc: "读取传感器权限达到个性化目的",
permissions: [
"android.permission.BODY_SENSORS",
]
},
{
title: "读取麦克风权限",
desc: "读取麦克风权限达到个性化目的",
permissions: [
"android.permission.RECORD_AUDIO",
]
},
]
export default permissions
顶部信息
let view: any
let idKey: string = 'permissionNotify'
/**
* 绘制消息
* @description 可根据描述字数动态变更高度
* @param {string} title 标题
* @param {string} desc 描述
*/
export function drawNotify(title: string, desc: string) {
// 关闭之前的 notify
// @ts-ignore
let idView = plus.nativeObj.View.getViewById(idKey)
if (idView) idView.close()
const {windowWidth, statusBarHeight} = uni.getSystemInfoSync()
const xDistance: number = 16 // 距离屏幕两侧的距离
const tDistance: number = 16 // 距离顶部的距离
const radius: number = 16 // 盒子圆角
const xPadding: number = 16 // 盒子paddingX
const YPadding: number = 16 // 盒子paddingY
const space: number = 12 // 描述距离标题的距离
const titleSize: number = 16 // 标题文字大小
const descSize: number = 14 // 描述文字大小
const width: number = windowWidth - xDistance * 2
// @ts-ignore
const top: number = statusBarHeight + tDistance
const lineNum = Math.ceil(desc.length / Math.floor((width - YPadding * 2) / descSize)) * 1.2
const height = YPadding * 2 + titleSize + space + descSize * lineNum
// @ts-ignore
let _view = new plus.nativeObj.View(
idKey,
{
width: `${width}`,
top: `${top}`,
left: `${xDistance}`,
height: `${height}`,
},
[
{
tag: 'rect',
id: 'rect',
rectStyles: {
color: '#FFFFFF',
radius: `${radius}`,
},
},
{
tag: 'font',
id: 'title',
text: title,
textStyles: {size: `${titleSize}`, align: 'left', weight: 'bold'},
position: {top: `${YPadding}`, left: `${xPadding}`, height: 'wrap_content'},
},
{
tag: 'font',
id: 'desc',
text: desc,
textStyles: {size: `${descSize}`, align: 'left', color: '#666666', whiteSpace: 'normal'},
position: {
top: `${YPadding + titleSize + space}`,
left: `${xPadding}`,
height: 'wrap_content',
width: `${width - YPadding * 2}`,
},
},
]
)
_view.show()
view = _view
}
/**
* 关闭消息
*/
export function closeNotify() {
if (view) view.close()
}
自动监听 onLaunch 引入 listener方法即可
监听权限
// @ts-nocheck
// #ifdef APP-PLUS
import permissions from "./permissions";
import {closeNotify, drawNotify} from "@/utils/notify";
const permissionListener = uni.createRequestPermissionListener()
// #endif
/**
* 监听权限
*/
export function listener() {
/* 是否已经弹出设置框 */
let hasConfirm = false
// 监听申请系统权限
permissionListener.onRequest((data: string[]) => {
})
// 监听弹出系统权限授权框
permissionListener.onConfirm((data: string[]) => {
const permissionsObj = getPermissionsObj(data)
drawNotify(permissionsObj.title, permissionsObj.desc)
})
// 监听权限申请完成
permissionListener.onComplete((data: string[]) => {
closeNotify()
const parts = data[data.length - 1].split('.');
let permissionName = parts[parts.length - 1];
const Manifest = plus.android.importClass('android.Manifest')
const MainActivity = plus.android.runtimeMainActivity()
const permissionStatus = MainActivity.checkSelfPermission(Manifest.permission[permissionName])
// 彻底拒绝
if (permissionStatus === -1 && !hasConfirm) {
hasConfirm = true
const permissionsObj = getPermissionsObj(data)
plus.nativeUI.confirm(permissionsObj.desc, (e) => {
if (e.index === 0) uni.openAppAuthorizeSetting()
hasConfirm = false
}, {
title: permissionsObj.title,
"buttons": ["去设置", "取消"],
});
}
})
}
function getPermissionsObj(data: string[]): { title: string, desc: string } {
let title: string = ""
let desc: string = ""
data.forEach((permission) => {
permissions.forEach(item => {
if (item.permissions.indexOf(permission) !== -1) {
title = item.title
desc = item.desc
}
})
})
return {title, desc}
}
权限设置
export interface Permissions {
title: string,
desc: string,
permissions: string[]
}
const permissions: Permissions[] = [
{
title: "读取短信权限",
desc: "读取短信权限达到个性化目的",
permissions: [
"android.permission.SEND_SMS",
"android.permission.RECEIVE_SMS",
"android.permission.READ_SMS",
"android.permission.RECEIVE_WAP_PUSH",
"android.permission.RECEIVE_MMS",
]
},
{
title: "读取存储卡,包括相册等权限",
desc: "读取存储卡,包括相册等权限达到个性化目的",
permissions: [
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE",
]
},
{
title: "读取联系人权限",
desc: "读取联系人权限达到个性化目的",
permissions: [
"android.permission.READ_CONTACTS",
"android.permission.WRITE_CONTACTS",
"android.permission.GET_ACCOUNTS",
]
},
{
title: "读取手机权限",
desc: "读取手机权限达到个性化目的",
permissions: [
"android.permission.READ_PHONE_STATE",
"android.permission.CALL_PHONE",
"android.permission.READ_CALL_LOG",
"android.permission.WRITE_CALL_LOG",
"android.permission.ADD_VOICEMAIL",
"android.permission.USE_SIP",
"android.permission.PROCESS_OUTGOING_CALLS",
]
},
{
title: "读取日历权限",
desc: "读取日历权限达到个性化目的",
permissions: [
"android.permission.READ_CALENDAR",
"android.permission.WRITE_CALENDAR",
]
},
{
title: "读取相机权限",
desc: "读取相机权限达到个性化目的",
permissions: [
"android.permission.CAMERA",
]
},
{
title: "读取位置权限",
desc: "读取位置权限达到个性化目的",
permissions: [
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.ACCESS_COARSE_LOCATION",
]
},
{
title: "读取传感器权限",
desc: "读取传感器权限达到个性化目的",
permissions: [
"android.permission.BODY_SENSORS",
]
},
{
title: "读取麦克风权限",
desc: "读取麦克风权限达到个性化目的",
permissions: [
"android.permission.RECORD_AUDIO",
]
},
]
export default permissions
顶部信息
let view: any
let idKey: string = 'permissionNotify'
/**
* 绘制消息
* @description 可根据描述字数动态变更高度
* @param {string} title 标题
* @param {string} desc 描述
*/
export function drawNotify(title: string, desc: string) {
// 关闭之前的 notify
// @ts-ignore
let idView = plus.nativeObj.View.getViewById(idKey)
if (idView) idView.close()
const {windowWidth, statusBarHeight} = uni.getSystemInfoSync()
const xDistance: number = 16 // 距离屏幕两侧的距离
const tDistance: number = 16 // 距离顶部的距离
const radius: number = 16 // 盒子圆角
const xPadding: number = 16 // 盒子paddingX
const YPadding: number = 16 // 盒子paddingY
const space: number = 12 // 描述距离标题的距离
const titleSize: number = 16 // 标题文字大小
const descSize: number = 14 // 描述文字大小
const width: number = windowWidth - xDistance * 2
// @ts-ignore
const top: number = statusBarHeight + tDistance
const lineNum = Math.ceil(desc.length / Math.floor((width - YPadding * 2) / descSize)) * 1.2
const height = YPadding * 2 + titleSize + space + descSize * lineNum
// @ts-ignore
let _view = new plus.nativeObj.View(
idKey,
{
width: `${width}`,
top: `${top}`,
left: `${xDistance}`,
height: `${height}`,
},
[
{
tag: 'rect',
id: 'rect',
rectStyles: {
color: '#FFFFFF',
radius: `${radius}`,
},
},
{
tag: 'font',
id: 'title',
text: title,
textStyles: {size: `${titleSize}`, align: 'left', weight: 'bold'},
position: {top: `${YPadding}`, left: `${xPadding}`, height: 'wrap_content'},
},
{
tag: 'font',
id: 'desc',
text: desc,
textStyles: {size: `${descSize}`, align: 'left', color: '#666666', whiteSpace: 'normal'},
position: {
top: `${YPadding + titleSize + space}`,
left: `${xPadding}`,
height: 'wrap_content',
width: `${width - YPadding * 2}`,
},
},
]
)
_view.show()
view = _view
}
/**
* 关闭消息
*/
export function closeNotify() {
if (view) view.close()
}
收起阅读 »

uniapp 在抖音(字节)小程序在安卓手机上,使用自己封装的input组件或者easy-input组件时,多次进入修改页面,不定时为空的问题。
在修改页面使用input组件时,多次反复进入修改页面,input的值有时为空,是因为 抖音小程序的input组件的maxlength会触发bindinput事件,并且执行顺序是不确定的。
目前抖音官方好像还没有修复,可使用如下代码临时解决:
/**
* 输入时触发
* @param {Object} event
*/
onInput(event) {
let value = event.detail.value
// 解决抖音小程序 反复返回,输入框值为空的问题(maxlength在安卓手机上会触发bindinput事件)
// #ifdef MP-TOUTIAO
if(!value && this.modelValue){
return
}
// #endif
this.val = value;
// TODO 兼容 vue2
this.$emit('input', value);
// TODO 兼容 vue3
this.$emit('update:modelValue', value);
},
在修改页面使用input组件时,多次反复进入修改页面,input的值有时为空,是因为 抖音小程序的input组件的maxlength会触发bindinput事件,并且执行顺序是不确定的。
目前抖音官方好像还没有修复,可使用如下代码临时解决:
/**
* 输入时触发
* @param {Object} event
*/
onInput(event) {
let value = event.detail.value
// 解决抖音小程序 反复返回,输入框值为空的问题(maxlength在安卓手机上会触发bindinput事件)
// #ifdef MP-TOUTIAO
if(!value && this.modelValue){
return
}
// #endif
this.val = value;
// TODO 兼容 vue2
this.$emit('input', value);
// TODO 兼容 vue3
this.$emit('update:modelValue', value);
},
收起阅读 »

支付宝小程序map高级定制渲染踩坑
使用支付宝小程序的map callout气泡功能时出现了customCallout不显示 / xml文件不被编译等等一系列的问题,网上的信息比较杂乱,分享一下自己的解决过程
支付宝小程序中的customCallout在真机中只显示第一个,改用iconLayout实现功能;
使用map高级定制渲染时,xml文件不会被编译,按照支付宝官方文档,在manifest.json中添加:
"mp-alipay": {
"include": ["**/*.xml"]
}
添加后没有效果,之后发现可以在项目根目录添加mini.project.json文件,在此文件中添加配置:
{
"enableAppxNg": true,
"include": ["**/*.xml"]
}
marker:
// #ifdef MP-ALIPAY
iconLayout: {
params: {
count: 1
},
src: '/static/map-xml/map-icon.xml'
}
// #endif
map-icon.xml:
<box layout="horizontal">
<text
id="test1"
clickable="true"
text="测试1"
padding-left="8"
padding-right="8"
font-size="16"
border-radius="6"
background-color="#FF0000"
/>
<text
id="test2"
clickable="true"
text="测试2"
padding-left="8"
padding-right="8"
font-size="16"
border-radius="6"
background-color="#FF0000"
/>
</box>
编译成功后在支付宝开发者工具mini.project.json文件中的配置
xml文件需要放入static文件夹中,放在根目录时不会编译
真机运行后气泡正常显示
使用支付宝小程序的map callout气泡功能时出现了customCallout不显示 / xml文件不被编译等等一系列的问题,网上的信息比较杂乱,分享一下自己的解决过程
支付宝小程序中的customCallout在真机中只显示第一个,改用iconLayout实现功能;
使用map高级定制渲染时,xml文件不会被编译,按照支付宝官方文档,在manifest.json中添加:
"mp-alipay": {
"include": ["**/*.xml"]
}
添加后没有效果,之后发现可以在项目根目录添加mini.project.json文件,在此文件中添加配置:
{
"enableAppxNg": true,
"include": ["**/*.xml"]
}
marker:
// #ifdef MP-ALIPAY
iconLayout: {
params: {
count: 1
},
src: '/static/map-xml/map-icon.xml'
}
// #endif
map-icon.xml:
<box layout="horizontal">
<text
id="test1"
clickable="true"
text="测试1"
padding-left="8"
padding-right="8"
font-size="16"
border-radius="6"
background-color="#FF0000"
/>
<text
id="test2"
clickable="true"
text="测试2"
padding-left="8"
padding-right="8"
font-size="16"
border-radius="6"
background-color="#FF0000"
/>
</box>
编译成功后在支付宝开发者工具mini.project.json文件中的配置
xml文件需要放入static文件夹中,放在根目录时不会编译
真机运行后气泡正常显示
收起阅读 »

热更新的静默更新会导致数据错误的优化方案
官方的热更新升级方案中是支持静默更新的,但是静默更新如果不重启常常会有页面样式错乱、数据加载错误等问题,看组件源码我们可以发现下载完成后是立即执行了wgt安装的操作,此时不重启app就会导致以上问题,这里我的思路是想在app在后台时执行安装并重启,或者在下次打开时执行安装并重启。
方案一、应用置于后台运行时安装热更新包并重启。
优点:更新会比较及时,只要应用置于后台了就会执行更新;
缺点:用户返回app时会执行安装重启,此时有明显的重启操作(安装完之后,我的wgt包20M需等3秒左右会重启),重启后会回到首页,这里可以记录重启前的页面,重启后重新定位该页面。
方案二、应用完全退出后重启时安装热更新包并再次重启。
优点:用户不会感觉到很明显的重启操作,不影响用户正常操作;
缺点:第一次启动时会有较长的时间停留在启动页(wgt包20M会停留在启动页7秒左右)安装完的重启几乎无感,很快,当然我们可以制作一个nvue的前端页面来代替启动页,并在这个页面用文字提示用户正在加载资源什么的(类似游戏打开先检查一番资源),获取用户信任,否则长时间的停留在启动页容易让用户误以为是app卡死。
主要的代码部分
uni-upgrade-center-app/utils/check-update.ts
// 静默更新,只有wgt有
if (uniUpgradeCenterResult.is_silently) {
uni.downloadFile({
url,
success: res => {
if (res.statusCode == 200) {
uni.setStorageSync('WGTFilePath', res.tempFilePath)
// 下载好直接安装,下次启动生效
// plus.runtime.install(res.tempFilePath, {
// force: false
// });
}
}
});
return;
}
保存wgt包的文件路径
App.vue
方案一:
onHide: function() {
let WGTFilePath = uni.getStorageSync('WGTFilePath')
if (WGTFilePath)
plus.runtime.install(WGTFilePath, {
force: false
}, () => {
uni.setStorageSync('WGTFilePath', '')
plus.runtime.restart()
return
})
},
方案二:
onLaunch: function() {
let WGTFilePath = uni.getStorageSync('WGTFilePath')
if (WGTFilePath)
plus.runtime.install(WGTFilePath, {
force: false
}, () => {
uni.setStorageSync('WGTFilePath', '')
plus.runtime.restart()
return
})
},
onShow: function() {
let WGTFilePath = uni.getStorageSync('WGTFilePath')
if (!WGTFilePath)//手动关闭启动页
plus.navigator.closeSplashscreen()
},
需要在配置文件关闭自动关闭启动页,并在app.vue手动处理
"splashscreen" : {
"alwaysShowBeforeRender" : false,
"waiting" : false,
"autoclose" : false,
"delay" : 0
},
官方的热更新升级方案中是支持静默更新的,但是静默更新如果不重启常常会有页面样式错乱、数据加载错误等问题,看组件源码我们可以发现下载完成后是立即执行了wgt安装的操作,此时不重启app就会导致以上问题,这里我的思路是想在app在后台时执行安装并重启,或者在下次打开时执行安装并重启。
方案一、应用置于后台运行时安装热更新包并重启。
优点:更新会比较及时,只要应用置于后台了就会执行更新;
缺点:用户返回app时会执行安装重启,此时有明显的重启操作(安装完之后,我的wgt包20M需等3秒左右会重启),重启后会回到首页,这里可以记录重启前的页面,重启后重新定位该页面。
方案二、应用完全退出后重启时安装热更新包并再次重启。
优点:用户不会感觉到很明显的重启操作,不影响用户正常操作;
缺点:第一次启动时会有较长的时间停留在启动页(wgt包20M会停留在启动页7秒左右)安装完的重启几乎无感,很快,当然我们可以制作一个nvue的前端页面来代替启动页,并在这个页面用文字提示用户正在加载资源什么的(类似游戏打开先检查一番资源),获取用户信任,否则长时间的停留在启动页容易让用户误以为是app卡死。
主要的代码部分
uni-upgrade-center-app/utils/check-update.ts
// 静默更新,只有wgt有
if (uniUpgradeCenterResult.is_silently) {
uni.downloadFile({
url,
success: res => {
if (res.statusCode == 200) {
uni.setStorageSync('WGTFilePath', res.tempFilePath)
// 下载好直接安装,下次启动生效
// plus.runtime.install(res.tempFilePath, {
// force: false
// });
}
}
});
return;
}
保存wgt包的文件路径
App.vue
方案一:
onHide: function() {
let WGTFilePath = uni.getStorageSync('WGTFilePath')
if (WGTFilePath)
plus.runtime.install(WGTFilePath, {
force: false
}, () => {
uni.setStorageSync('WGTFilePath', '')
plus.runtime.restart()
return
})
},
方案二:
onLaunch: function() {
let WGTFilePath = uni.getStorageSync('WGTFilePath')
if (WGTFilePath)
plus.runtime.install(WGTFilePath, {
force: false
}, () => {
uni.setStorageSync('WGTFilePath', '')
plus.runtime.restart()
return
})
},
onShow: function() {
let WGTFilePath = uni.getStorageSync('WGTFilePath')
if (!WGTFilePath)//手动关闭启动页
plus.navigator.closeSplashscreen()
},
需要在配置文件关闭自动关闭启动页,并在app.vue手动处理
"splashscreen" : {
"alwaysShowBeforeRender" : false,
"waiting" : false,
"autoclose" : false,
"delay" : 0
},
收起阅读 »