提交 12e55c5b 作者: 方治民

合并分支 '3.x' 到 'main'

3.x

查看合并请求 !72
...@@ -161,5 +161,6 @@ ...@@ -161,5 +161,6 @@
"engines": { "engines": {
"node": ">=18", "node": ">=18",
"pnpm": ">=9.0.0" "pnpm": ">=9.0.0"
} },
"appName": "基础工程"
} }
<script setup lang="ts"> <script setup lang="ts">
// import * as Push from '@/utils/push' import { onExit } from '@dcloudio/uni-app'
import { isDevMode } from '@/utils/env' import { isDevMode } from '@/utils/env'
import { usePermissions } from '@/hooks/app/usePermissions'
// import * as Push from '@/utils/push'
// 监听权限获取
const { listen: startPermissionListener, stop: stopPermissionListener } = usePermissions()
onLaunch(() => { onLaunch(() => {
console.log('App Launch') console.log('App Launch')
// 监听权限获取
startPermissionListener()
// 清除消息角标 // 清除消息角标
// Push.setBadge(0) // Push.setBadge(0)
...@@ -21,6 +30,11 @@ ...@@ -21,6 +30,11 @@
// #endif // #endif
}) })
onExit(() => {
// 停用监听权限
stopPermissionListener()
})
onShow(() => { onShow(() => {
console.log('App Show') console.log('App Show')
}) })
......
import { debounce } from 'lodash-es'
import { Storage } from '@/utils/storage'
export interface Permissions {
listen: () => void
stop: () => void
gotoAppPermissionSetting: () => void
}
export interface RequestPermissionListener {
onRequest: (e: any) => void
onConfirm: (e: any) => void
onComplete: (e: any) => void
stop: () => void
}
/**
* 权限申请说明(注:仅适用于 Android)
* @link https://uniapp.dcloud.net.cn/tutorial/app-nativeresource-android.html#permissions
*/
export function getRequestPermissionTipMessage(id: string) {
switch (id) {
case 'android.permission.INTERNET':
return { name: '网络权限', message: '允许访问网络,便于使用应用相关功能。' }
case 'android.permission.CAMERA':
return {
name: '相机权限',
message:
'允许访问摄像头,便于拍照上传照片/图片用于意见反馈、保存相册、缓存数据提升访问速度等场景中拍摄照片。',
}
case 'android.permission.READ_MEDIA_IMAGES':
case 'android.permission.READ_EXTERNAL_STORAGE':
case 'android.permission.WRITE_EXTERNAL_STORAGE':
return {
name: '存储权限',
message:
'允许访问存储,便于上传照片/图片用于意见反馈、保存相册、缓存数据提升访问速度等场景中读取和写入相册和文件内容。',
}
case 'android.permission.READ_PHONE_STATE':
case 'android.permission.ACCESS_NETWORK_STATE':
case 'android.permission.ACCESS_WIFI_STATE':
return {
name: '设备状态权限',
message: '允许访问设备状态,便于收集应用异常信息。',
}
case 'android.permission.ACCESS_COARSE_LOCATION':
case 'android.permission.ACCESS_FINE_LOCATION':
case 'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS':
case 'android.permission.ACCESS_MOCK_LOCATION':
return {
name: '位置权限',
message: '允许访问位置信息,便于确认上传随手拍问题时的所在位置。',
}
case 'android.permission.INSTALL_PACKAGES':
case 'android.permission.REQUEST_INSTALL_PACKAGES':
return {
name: '安装应用权限',
message: '允许访问安装应用,便于应用更新包的安装。',
}
}
}
export function usePermissions(): Permissions {
const system = uni.getSystemInfoSync()
const listener = ref<RequestPermissionListener>()
const requestPermissionId = ref<string>()
const show = ref(false)
const debounceShow = debounce((value) => {
show.value = value
}, 200)
watch(
() => show.value,
(value) => {
const id = requestPermissionId.value
if (value) {
const statusBarHeight = system.statusBarHeight
const navigationBarHeight = system.platform === 'android' ? 48 : 44
const totalHeight = statusBarHeight + navigationBarHeight
let view = new plus.nativeObj.View(id, {
top: '0px',
left: '0px',
width: '100%',
backgroundColor: 'rgba(0,0,0,0.3)',
})
view.drawRect(
{
color: '#fff',
radius: '5px',
},
{
top: `${totalHeight}px`,
left: '5%',
width: '90%',
height: '100px',
},
)
view.drawText(
'权限使用说明',
{
top: `${totalHeight + 5}px`,
left: '8%',
height: '30px',
},
{
align: 'left',
color: '#000',
size: '20px',
},
)
view.drawText(
getRequestPermissionTipMessage(id)?.message,
{
top: `${totalHeight + 35}px`,
height: '60px',
left: '8%',
width: '84%',
},
{
whiteSpace: 'normal',
size: '14px',
align: 'left',
color: '#656563',
},
)
view.show()
view = null
} else {
// @ts-expect-error
let view = plus.nativeObj.View.getViewById(id)
view && view.close()
view = null
requestPermissionId.value = null
}
},
)
function listen() {
// #ifdef APP-PLUS
// FIXED: 由于 iOS 下无问题,此处仅处理安卓平台的权限申请问题
if (system.platform === 'android') {
// @ts-expect-error
listener.value = uni.createRequestPermissionListener()
listener.value.onConfirm((e: string[]) => {
console.log('onConfirm', e)
// 创建弹窗视图
requestPermissionId.value = e[0]
debounceShow(true)
})
listener.value.onComplete((e: string[]) => {
console.log('onComplete', e)
// 关闭弹窗视图
const id = e[0]
// 判断是否触发了对应的权限显示
if (requestPermissionId.value !== id && Storage.get(id) !== 'granted') {
if (Storage.get(id) === 'denied') {
handlerPermissionModal(id)
} else {
plus.android.requestPermissions(e, (v) => {
console.log('requestPermissions', e, v)
if (v.granted.includes(id)) {
Storage.set(id, 'granted')
}
if (v.deniedAlways.includes(id)) {
Storage.set(id, 'denied')
handlerPermissionModal(id)
}
})
}
} else {
requestPermissionId.value = id
debounceShow(false)
}
})
console.log('[usePermissions] start listen...')
}
// #endif
}
function stop() {
// #ifdef APP-PLUS
listener.value?.stop()
// 清除权限标记,防止用户主动设置了权限允许重新打开仍然无法使用的问题
Storage.keys().forEach((key) => {
if (key.startsWith('android.permission.')) {
Storage.remove(key)
}
})
console.log('[usePermissions] stop listen')
// #endif
}
// 权限设置弹窗
const settingConfirmVisible = ref(false)
function handlerPermissionModal(id: string) {
if (settingConfirmVisible.value) {
return
}
settingConfirmVisible.value = true
Message.confirm(`缺少${getRequestPermissionTipMessage(id).name},立即去设置开启?`, '温馨提示', '去设置').then(
(confirm) => {
settingConfirmVisible.value = false
if (confirm) {
try {
Storage.remove(id)
gotoAppPermissionSetting()
} catch (e) {
console.error('[Permissions] gotoAppPermissionSetting fail!', e)
}
}
},
)
}
// 跳转到 APP 权限设置
function gotoAppPermissionSetting() {
// #ifdef APP-PLUS
if (system.platform === 'ios') {
const UIApplication = plus.ios.importClass('UIApplication')
const application = UIApplication.sharedApplication()
const NSURL = plus.ios.importClass('NSURL')
const setting = NSURL.URLWithString('app-settings:')
application.openURL(setting)
plus.ios.deleteObject(setting)
plus.ios.deleteObject(NSURL)
plus.ios.deleteObject(application)
} else {
// console.log(plus.device.vendor);
const Intent = plus.android.importClass('android.content.Intent')
const Settings = plus.android.importClass('android.provider.Settings')
const Uri = plus.android.importClass('android.net.Uri')
const mainActivity = plus.android.runtimeMainActivity()
// @ts-expect-error
const intent = new Intent()
// @ts-expect-error
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
// @ts-expect-error
const uri = Uri.fromParts('package', mainActivity.getPackageName(), null)
intent.setData(uri)
// @ts-expect-error
mainActivity.startActivity(intent)
}
// #endif
}
return {
listen,
stop,
gotoAppPermissionSetting,
}
}
{ {
"name" : "Beta App", "name": "Beta App",
"appid" : "__UNI__2E9441A", "appid": "__UNI__2E9441A",
"description" : "APP 基础工程", "description": "APP 基础工程",
"versionName" : "1.0.1", "versionName": "1.0.1",
"versionCode" : 10001, "versionCode": 10001,
"transformPx" : false, "transformPx": false,
"locale" : "zh-Hans", "locale": "zh-Hans",
"vueVersion" : "3", "vueVersion": "3",
/* 5+App特有相关 */// 配置文件详细说明 /* 5+App特有相关 */// 配置文件详细说明
// https://uniapp.dcloud.net.cn/collocation/manifest-app.html#full-manifest // https://uniapp.dcloud.net.cn/collocation/manifest-app.html#full-manifest
"app-plus" : { "app-plus": {
"usingComponents" : true, "usingComponents": true,
"nvueStyleCompiler" : "uni-app", "nvueCompiler": "uni-app",
"compilerVersion" : 3, "nvueStyleCompiler": "uni-app",
"splashscreen" : { "nvueLaunchMode": "fast",
"alwaysShowBeforeRender" : false, "compilerVersion": 3,
"autoclose" : false, "splashscreen": {
"waiting" : true "alwaysShowBeforeRender": false,
"autoclose": false,
"waiting": true
}, },
"screenOrientation" : [ "portrait-primary", "landscape-primary" ], "screenOrientation": ["portrait-primary", "landscape-primary"],
"compatible" : { "compatible": {
// 忽略版本提示 // 忽略版本提示
"ignoreVersion" : true "ignoreVersion": true
}, },
/* 模块配置 */ /* 模块配置 */
"modules" : { "modules": {
"Geolocation" : {}, "Geolocation": {},
"Camera" : {} "Camera": {}
}, },
/* 应用发布信息 */ /* 应用发布信息 */
"distribute" : { "distribute": {
/* android打包配置 */ /* android打包配置 */
"android" : { "android": {
"permissions" : [ "permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
...@@ -49,94 +51,97 @@ ...@@ -49,94 +51,97 @@
"<uses-feature android:name=\"android.hardware.camera\"/>", "<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
], ],
"permissionPhoneState" : { "permissionPhoneState": {
// app首次启动关闭权限申请 // app首次启动关闭权限申请
"request" : "none" "request": "none"
} },
"minSdkVersion": 23,
"targetSdkVersion": 30,
"abiFilters": ["armeabi-v7a", "arm64-v8a"]
}, },
/* ios打包配置 */ /* ios打包配置 */
"ios" : { "ios": {
"dSYMs" : false, "dSYMs": false,
"privacyDescription" : { "privacyDescription": {
"NSUserTrackingUsageDescription" : "请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备并保障服务安全与提示浏览体验", "NSUserTrackingUsageDescription": "请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备并保障服务安全与提示浏览体验",
"NSLocationAlwaysAndWhenInUseUsageDescription" : "该应用需要你的地理位置,以便检查你当前位置信息", "NSLocationAlwaysAndWhenInUseUsageDescription": "该应用需要你的地理位置,以便检查你当前位置信息",
"NSLocationAlwaysUsageDescription" : "该应用需要你的地理位置,以便检查你当前位置信息", "NSLocationAlwaysUsageDescription": "该应用需要你的地理位置,以便检查你当前位置信息",
"NSLocationWhenInUseUsageDescription" : "该应用需要你的地理位置,以便检查你当前位置信息", "NSLocationWhenInUseUsageDescription": "该应用需要你的地理位置,以便检查你当前位置信息",
"NSPhotoLibraryUsageDescription" : "请允许访问您的照片图库,以便能够上传应用异常问题的截图" "NSPhotoLibraryUsageDescription": "请允许访问您的照片图库,以便能够上传应用异常问题的截图"
} }
}, },
/* SDK配置 */ /* SDK配置 */
"sdkConfigs" : { "sdkConfigs": {
"ad" : {}, "ad": {},
"statics" : {}, "statics": {},
"geolocation" : { "geolocation": {
"system" : { "system": {
"__platform__" : [ "ios", "android" ] "__platform__": ["ios", "android"]
} }
} }
}, },
"icons" : { "icons": {
"android" : { "android": {
"hdpi" : "unpackage/res/icons/72x72.png", "hdpi": "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png", "xhdpi": "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png", "xxhdpi": "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png" "xxxhdpi": "unpackage/res/icons/192x192.png"
}, },
"ios" : { "ios": {
"appstore" : "unpackage/res/icons/1024x1024.png", "appstore": "unpackage/res/icons/1024x1024.png",
"ipad" : { "ipad": {
"app" : "unpackage/res/icons/76x76.png", "app": "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png", "app@2x": "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png", "notification": "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png", "notification@2x": "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png", "proapp@2x": "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png", "settings": "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png", "settings@2x": "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png", "spotlight": "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png" "spotlight@2x": "unpackage/res/icons/80x80.png"
}, },
"iphone" : { "iphone": {
"app@2x" : "unpackage/res/icons/120x120.png", "app@2x": "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png", "app@3x": "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png", "notification@2x": "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png", "notification@3x": "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png", "settings@2x": "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png", "settings@3x": "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png", "spotlight@2x": "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png" "spotlight@3x": "unpackage/res/icons/120x120.png"
} }
} }
}, },
"splashscreen" : { "splashscreen": {
"useOriginalMsgbox" : true "useOriginalMsgbox": true
} }
}, },
"uniStatistics" : { "uniStatistics": {
"enable" : true "enable": true
} }
}, },
"uniStatistics" : { "uniStatistics": {
"enable" : true, "enable": true,
"version" : "2", "version": "2",
"debug" : true "debug": true
}, },
/* 快应用特有相关 */ /* 快应用特有相关 */
"quickapp" : {}, "quickapp": {},
/* 小程序特有相关 */ /* 小程序特有相关 */
"mp-weixin" : { "mp-weixin": {
"appid" : "", "appid": "",
"setting" : { "setting": {
"urlCheck" : false "urlCheck": false
}, },
"usingComponents" : true "usingComponents": true
}, },
"mp-alipay" : { "mp-alipay": {
"usingComponents" : true "usingComponents": true
}, },
"mp-baidu" : { "mp-baidu": {
"usingComponents" : true "usingComponents": true
}, },
"mp-toutiao" : { "mp-toutiao": {
"usingComponents" : true "usingComponents": true
} }
} }
...@@ -64,7 +64,28 @@ ...@@ -64,7 +64,28 @@
"backgroundColorTop": "transparent", "backgroundColorTop": "transparent",
"popGesture": "none", "popGesture": "none",
"scrollIndicator": false, "scrollIndicator": false,
"titleNView": false "titleNView": false,
"bounce": "none"
},
"disableScroll": true
}
},
// === 应用全屏通知弹窗(场景:应用维护通知、用户通知公告等) ===
{
"path": "pages/common/notice/index",
"style": {
"navigationStyle": "custom",
"background": "transparent",
"app-plus": {
"animationDuration": 200,
"animationType": "fade-in",
"background": "transparent",
"backgroundColorTop": "transparent",
"popGesture": "none",
"scrollIndicator": false,
"webviewBGTransparent": true,
"titleNView": false,
"bounce": "none"
}, },
"disableScroll": true "disableScroll": true
} }
......
import dayjs from 'dayjs'
import { useRuntime } from '@/hooks/app/useRuntime'
import { Storage } from '@/utils/storage'
export interface Notice {
/**
* 通知 ID
*/
id: number | string
/**
* 标题
*/
title?: string
/**
* 内容
*/
content: string
/**
* 按钮集合,confirm: 确认,exit: 退出
*/
buttons?: string[]
/**
* 是否已确认
*/
confirmed?: boolean
}
export interface NoticeMessage extends Notice {
/**
* 版本号(code)规则 JSON, 例如 { '>': '10000', '<': '20000' }
*/
version?: Recordable
/**
* 平台,默认为 all,支持 'android' | 'ios' | 'harmonyos' | 'windows' | 'macos' | 'linux' | 'all'
*/
os?: ['android' | 'ios' | 'harmonyos' | 'windows' | 'macos' | 'linux' | 'all']
/**
* 验证参数,用于校验当前用户是否需要弹出通知,例如:用户信息,租户信息等
*/
verify: Recordable
}
/**
* 检查是否有应用通知,如果有自动跳转到通知页面,没有则返回 false
* @param verify 扩展校验参数,校验与通知中的 verify 字段一致
* @returns Promise<boolean> 是否有应用通知
*/
export async function inspect(verify: Recordable = {}): Promise<boolean> {
const { app } = useRuntime()
const system = uni.getSystemInfoSync()
const params = {
datetime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
app: {
id: system.appId,
name: system.appName,
version: system.appVersion,
versionCode: system.appVersionCode,
wgetVersion: app.value.version || system.appWgtVersion || system.appVersion,
language: system.appLanguage,
},
os: {
name: system.osName,
version: system.osVersion,
},
net: {
type: (await uni.getNetworkType()).networkType,
},
rom: {
name: system.romName,
version: system.romVersion,
},
device: {
type: system.deviceType,
brand: system.deviceBrand,
model: system.deviceModel,
},
browser: {
name: system.browserName,
version: system.browserVersion,
ua: system.ua,
},
runtime: {
platform: system.platform,
uniPlatform: system.uniPlatform,
version: system.uniRuntimeVersion,
compileVersion: system.uniCompileVersion,
mode: import.meta.env.MODE,
lastBuildTime: $app.lastBuildTime,
},
// 自定义校验参数(例如:用户信息/租户信息,可参与校验是否需要弹窗,实现精确到指定用户的提醒)
verify,
}
try {
// 调用云函数查询
const result = await uniCloud.callFunction({
name: 'app-notice-inspect',
data: {
params,
},
})
const body = result.result as { id: number | string; params: typeof params; data: NoticeMessage }
console.log('[Notice] Response', params, body)
// 检查是否有通知
const id = body.id
if (!id) {
return false
}
// 缓存通知数据
const data: Notice = {
id,
confirmed: getConfirm(id),
...body.data,
}
getApp().globalData.notice = data
if (!data.confirmed) {
uni.navigateTo({ url: '/pages/common/notice/index' })
return true
}
} catch (e) {
console.warn(e)
}
return false
}
export function setConfirm(id: number | string) {
if (getApp().globalData.notice) {
Storage.set(`notice-${id}`, '1')
getApp().globalData.notice.confirmed = true
}
}
export function getConfirm(id: number | string) {
return Storage.get(`notice-${id}`) === '1'
}
<script setup lang="ts">
import type { Notice } from '.'
import { setConfirm } from '.'
// 获取通知信息
const notice = ref<Notice>(getApp().globalData.notice || {})
// 内容初始化高度
const height = ref(100)
onLoad(() => {
// 没有通知消息或者已经确认过
if (!notice.value.id || notice.value.confirmed) {
Message.toast('暂无通知消息 ~')
uni.navigateBack()
return
}
// 禁止页面侧滑返回
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const currentWebview = currentPage.$getAppWebview()
currentWebview.setStyle({ popGesture: 'none' })
nextTick(() => {
setTimeout(() => {
uni.createSelectorQuery()
.select('.notice-content')
.boundingClientRect((data: UniNamespace.NodeInfo) => {
// 获取高度, 默认为 100-340
height.value = Math.max(100, Math.min(340, data.height))
})
.exec()
}, 300)
})
})
// 预览图片
function preview(e: { src: string; imageUrls: string[] }) {
uni.previewImage({
urls: [e.src],
current: 0,
})
}
// 点击链接
function atap(href: string) {
if (href.startsWith('http')) {
// 打开外部链接
uni.setClipboardData({
data: href,
success: () => {
Message.toast('已复制链接,请在浏览器中粘贴打开')
},
})
} else if (href.startsWith('/pages/')) {
// 打开内部链接
uni.navigateTo({
url: href,
})
}
}
// 退出应用
function quit() {
// 退出应用
// #ifdef APP-PLUS
if (uni.getSystemInfoSync().platform === 'ios') {
try {
// @ts-expect-error
plus.ios.import('UIApplication').sharedApplication().performSelector('abort')
} catch (e) {
console.error(e)
}
} else {
plus.runtime.quit()
}
// #endif
}
// 确认知道到了
function confirm() {
notice.value.id && setConfirm(notice.value.id)
uni.navigateBack()
}
</script>
<template>
<view class="notice-mask">
<view class="w-80% p-3 bg-white rd-2 shadow shadow-2xl overflow-hidden">
<view class="flex-center mt-2" v-if="notice.title">
<text class="font-bold text-36">{{ notice.title }}</text>
</view>
<FuiDivider text="·" :height="50" v-if="notice.title" />
<view class="h-auto p-1">
<scroll-view scroll-y :style="{ height: `${height}px` }">
<FuiParseGroup class="notice-content" @preview="preview" @atap="atap">
<FuiParse :nodes="notice.content" language="md" />
</FuiParseGroup>
</scroll-view>
</view>
<view class="flex gap-3 mt-3" v-if="notice.buttons.length">
<FuiButton type="gray" @click="confirm" v-if="notice.buttons.includes('confirm')"> 我知道了 </FuiButton>
<FuiButton
color="#fff"
background="#ff6363"
borderColor="#ff6363"
@click="quit"
v-if="notice.buttons.includes('exit')"
>
退出应用
</FuiButton>
</view>
</view>
</view>
</template>
<style lang="less">
// ...
page {
background: transparent;
user-select: none;
}
.notice-mask {
position: fixed;
inset: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
background-color: rgb(0 0 0 / 40%);
}
</style>
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import { checkUpgrade, closeSplashscreenAndChechUpgrade } from '@/utils/upgrade' import { checkUpgrade, closeSplashscreenAndChechUpgrade } from '@/utils/upgrade'
import { useRuntime } from '@/hooks/app/useRuntime' import { useRuntime } from '@/hooks/app/useRuntime'
import { useConcealedExit } from '@/hooks/app/useConcealedExit' import { useConcealedExit } from '@/hooks/app/useConcealedExit'
import * as Notice from '@/pages/common/notice'
const { exit } = useConcealedExit() const { exit } = useConcealedExit()
const { app } = useRuntime() const { app } = useRuntime()
...@@ -13,27 +14,11 @@ ...@@ -13,27 +14,11 @@
onLoad(() => { onLoad(() => {
// 关闭启动页并检查更新 // 关闭启动页并检查更新
closeSplashscreenAndChechUpgrade() closeSplashscreenAndChechUpgrade()
})
// test API onShow(() => {
API.example.hello // 检查是否有全局通知
.request() Notice.inspect()
.then((body) => {
title.value = body
console.log('[API]', body, $app.name, $app.version)
Message.toast(body)
})
.catch((err) => {
console.error('[API]', err)
})
// test WebSocket(STOMP)
Stomp.connect(() => {
Stomp.send('/app/ping', 'ping')
})
// test uni-stat
uni.report('onLoad', `[Test] onLoad: ${dayjs().format('YYYY-MM-DD HH:mm:ss')}`)
}) })
function surprise() { function surprise() {
......
...@@ -3,23 +3,22 @@ import checkVersion from '@/uni_modules/uni-upgrade-center-app/utils/check-updat ...@@ -3,23 +3,22 @@ import checkVersion from '@/uni_modules/uni-upgrade-center-app/utils/check-updat
// #endif // #endif
export function checkUpgrade(toast = false) { export async function checkUpgrade(toast = false): Promise<void> {
// #ifdef APP-PLUS // #ifdef APP-PLUS
// 版本更新检查 // 版本更新检查
if (toast) { if (toast) {
Message.loading('正在检查更新...') Message.loading('正在检查更新...')
} }
checkVersion() try {
.then((res) => { const res = await checkVersion()
if (toast && res?.code === 0) { if (toast && res?.code === 0) {
Message.toast('已是最新版本~') Message.toast('已是最新版本~')
} }
}) } finally {
.finally(() => { if (toast) {
if (toast) { Message.hideLoading()
Message.hideLoading() }
} }
})
// #endif // #endif
} }
...@@ -27,7 +26,7 @@ export function checkUpgrade(toast = false) { ...@@ -27,7 +26,7 @@ export function checkUpgrade(toast = false) {
* 关闭 splashscreen 并检查更新 * 关闭 splashscreen 并检查更新
*/ */
export function closeSplashscreenAndChechUpgrade() { export function closeSplashscreenAndChechUpgrade() {
return new Promise<void>((resolve) => { return new Promise<void>((resolve, reject) => {
// FIXED: pages 第一路由页面为登录页,加上 splashscreen 配置控制使得用户体验更好 // FIXED: pages 第一路由页面为登录页,加上 splashscreen 配置控制使得用户体验更好
// #ifdef APP-PLUS // #ifdef APP-PLUS
nextTick(() => { nextTick(() => {
...@@ -35,11 +34,15 @@ export function closeSplashscreenAndChechUpgrade() { ...@@ -35,11 +34,15 @@ export function closeSplashscreenAndChechUpgrade() {
const { platform } = uni.getSystemInfoSync() const { platform } = uni.getSystemInfoSync()
const isAndroid = platform === 'android' const isAndroid = platform === 'android'
setTimeout( setTimeout(
() => { async () => {
plus.navigator.closeSplashscreen() plus.navigator.closeSplashscreen()
// 检查更新 try {
checkUpgrade() // 检查更新
resolve() await checkUpgrade()
resolve()
} catch (e) {
reject(e)
}
}, },
isAndroid ? 1000 : 50, isAndroid ? 1000 : 50,
) )
......
...@@ -28,7 +28,9 @@ ...@@ -28,7 +28,9 @@
"@/*": ["src/*"], "@/*": ["src/*"],
"/@/*": ["src/*"], "/@/*": ["src/*"],
"/#/*": ["types/*"] "/#/*": ["types/*"]
} },
"rootDir": ".",
"outDir": "dist"
}, },
"include": [ "include": [
"src/**/*.ts", "src/**/*.ts",
......
/** /**
* App 信息 * App 信息
*/ */
type Application = { interface Application {
/**
* 应用名称
*/
appName: string
/** /**
* 项目名称 * 项目名称
*/ */
...@@ -31,7 +36,7 @@ type Application = { ...@@ -31,7 +36,7 @@ type Application = {
/** /**
* App 作者信息 * App 作者信息
*/ */
type Author = { interface Author {
/** /**
* 作者名称 * 作者名称
*/ */
......
'use strict'
exports.main = async (event, context) => {
// eslint-disable-next-line no-undef
const db = uniCloud.database()
const dbCmd = db.command
const { total } = await db.collection('opendb-app-list').where({ appid: context.appId }).count()
if (total < 1) {
return { errCode: 1, errMsg: '应用信息不存在' }
}
// 查询优先级最高且在生效中的 APP 通知
const { params } = event
const { data: notices } = await db
.collection('app-notice')
.where({
appid: context.appId,
enabled: true,
expired: dbCmd.gt(Date.now()),
})
.orderBy('priority', 'asc')
.limit(1)
.get()
if (notices.length < 1) {
return { errCode: 1, errMsg: '暂无生效中的通知' }
}
// 获取通知信息
const notice = notices[0]
console.log('notice', notice)
// 检查运行环境
if (notice.mode !== params.runtime.mode) {
return { errCode: 1, errMsg: '运行环境不匹配', message: `${params.runtime.mode} -> ${notice.mode}` }
}
// 检查平台
if (notice.os && notice.os.length > 0 && !notice.os.includes('all') && !notice.os.includes(params.os.name)) {
return { errCode: 1, errMsg: '平台不匹配', message: `${params.os.name} -> ${JSON.stringify(notice.os)}` }
}
// 检查版本号
if (notice.version) {
const keys = Object.keys(notice.version)
const code = Number(
context.appWgtVersion
.split('.')
.map((v) => String(v).padStart(2, '0'))
.join(''),
)
const mismatching = keys
.map((v) => {
switch (v) {
case '>':
if (code <= notice.version[v]) {
return false
}
break
case '<':
if (code >= notice.version[v]) {
return false
}
break
case '=':
if (code !== notice.version[v]) {
return false
}
break
case '>=':
if (code < notice.version[v]) {
return false
}
break
case '<=':
if (code > notice.version[v]) {
return false
}
break
default:
return false
}
return true
})
.some((v) => !v)
if (mismatching) {
return {
errCode: 1,
errMsg: '版本号不匹配',
message: `${context.appWgtVersion} -> ${code} -> ${JSON.stringify(notice.version)}`,
}
}
}
// 检查自定义参数
const verifyKeys = Object.keys(params.verify)
if (
verifyKeys.length < Object.keys(notice.verify).length ||
verifyKeys.some((v) => notice.verify[v] !== params.verify[v])
) {
return {
errCode: 1,
errMsg: '校验参数不匹配',
message: `${JSON.stringify(params.verify)} -> ${JSON.stringify(notice.verify)}`,
}
}
// 返回数据给客户端
return {
id: notice._id,
params: {
app: params.app,
verify: params.verify,
},
data: {
...notice,
},
}
}
{
"name": "app-notice-inspect",
"dependencies": {},
"extensions": {
"uni-cloud-jql": {}
}
}
// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
"bsonType": "object",
"required": [],
"permission": {
"read": true,
"create": false,
"update": false,
"delete": false
},
"properties": {
"_id": {
"description": "ID,系统自动生成"
},
"created_at": {
"description": "创建时间"
},
"update_at": {
"description": "更新时间"
},
"appid": {
"description": "appId"
},
"title": {
"description": "标题"
},
"content": {
"description": "内容"
},
"buttons": {
"description": "按钮列表"
},
"priority": {
"description": "优先级,数值越小,优先级越高"
},
"enabled": {
"description": "是否启用"
},
"expired": {
"description": "过期时间"
},
"platform": {
"description": "平台"
},
"mode": {
"description": "运行模式,development:开发模式,preview:预览模式,production:生产模式"
},
"version": {
"description": "版本号规则 JSON, 例如 { '>': '1.0.0', '<': '2.0.0' }"
},
"verify": {
"description": "自定义校验参数 JSON, 例如 { 'userId': '1', 'tenant': 'YR' }"
}
}
}
...@@ -45,6 +45,7 @@ export default ({ mode }: ConfigEnv): UserConfig => { ...@@ -45,6 +45,7 @@ export default ({ mode }: ConfigEnv): UserConfig => {
}, },
define: { define: {
__APP__: { __APP__: {
appName: pkg.appName || pkg.description,
name: pkg.name, name: pkg.name,
version: pkg.version, version: pkg.version,
description: pkg.description, description: pkg.description,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论