提交 a208e7c0 作者: 方治民

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

3.x

查看合并请求 !68
# API 接口地址 # API 接口地址
VITE_GLOB_API_URL=http://127.0.0.1:8081 VITE_GLOB_API_URL=http://192.168.0.110:8081
# API 接口地址前缀 # API 接口地址前缀
VITE_GLOB_API_URL_PREFIX=/api VITE_GLOB_API_URL_PREFIX=/api
{ {
"name": "basic-app", "name": "basic-app",
"version": "1.0.0", "version": "1.0.1",
"description": "APP 基础工程", "description": "APP 基础工程",
"keywords": [ "keywords": [
"app", "app",
...@@ -82,14 +82,14 @@ ...@@ -82,14 +82,14 @@
"@dcloudio/uni-ui": "^1.5.5", "@dcloudio/uni-ui": "^1.5.5",
"@faker-js/faker": "^8.4.1", "@faker-js/faker": "^8.4.1",
"@vue/runtime-core": "3.4.21", "@vue/runtime-core": "3.4.21",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.11.0",
"axios": "^1.6.8", "axios": "^1.7.2",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"nanoid": "^5.0.7", "nanoid": "^5.0.7",
"pinia": "^2.1.7", "pinia": "2.0.36",
"qs": "~6.12.1", "qs": "6.9.7",
"stompjs": "^2.3.3", "stompjs": "^2.3.3",
"urijs": "^1.19.11", "urijs": "^1.19.11",
"vue": "3.4.21", "vue": "3.4.21",
...@@ -106,43 +106,44 @@ ...@@ -106,43 +106,44 @@
"@dcloudio/uni-cli-shared": "3.0.0-4010520240507001", "@dcloudio/uni-cli-shared": "3.0.0-4010520240507001",
"@dcloudio/uni-helper-json": "^1.0.13", "@dcloudio/uni-helper-json": "^1.0.13",
"@dcloudio/uni-stacktracey": "3.0.0-4010520240507001", "@dcloudio/uni-stacktracey": "3.0.0-4010520240507001",
"@dcloudio/uni-uts-v1": "3.0.0-4010520240507001",
"@dcloudio/vite-plugin-uni": "3.0.0-4010520240507001", "@dcloudio/vite-plugin-uni": "3.0.0-4010520240507001",
"@iconify/json": "^2.2.207", "@iconify/json": "^2.2.220",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.10", "@types/node": "^20.14.6",
"@types/qs": "^6.9.15", "@types/qs": "^6.9.15",
"@types/stompjs": "^2.3.9", "@types/stompjs": "^2.3.9",
"@types/urijs": "^1.19.25", "@types/urijs": "^1.19.25",
"@typescript-eslint/eslint-plugin": "^7.8.0", "@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.8.0", "@typescript-eslint/parser": "^7.13.1",
"commitizen": "^4.3.0", "commitizen": "^4.3.0",
"conventional-changelog-cli": "^4.1.0", "conventional-changelog-cli": "^4.1.0",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"cz-customizable": "^7.0.0", "cz-customizable": "^7.0.0",
"cz-git": "^1.9.1", "cz-git": "^1.9.3",
"czg": "^1.9.1", "czg": "^1.9.3",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.25.0", "eslint-plugin-vue": "^9.26.0",
"husky": "^8.0.3", "husky": "^8.0.3",
"jest": "27.0.4", "jest": "27.0.4",
"jest-environment-node": "27.5.1", "jest-environment-node": "27.5.1",
"less": "^4.2.0", "less": "^4.2.0",
"lint-staged": "^15.2.2", "lint-staged": "^15.2.7",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"picocolors": "^1.0.0", "picocolors": "^1.0.1",
"pont-engine": "^1.6.3", "pont-engine": "^1.6.3",
"postcss": "^8.4.38", "postcss": "^8.4.38",
"postcss-html": "^1.6.0", "postcss-html": "^1.7.0",
"postcss-less": "^6.0.0", "postcss-less": "^6.0.0",
"prettier": "^3.2.5", "prettier": "^3.3.2",
"rimraf": "^5.0.5", "rimraf": "^5.0.7",
"sass": "^1.77.0", "sass": "^1.77.6",
"sort-package-json": "^2.10.0", "sort-package-json": "^2.10.0",
"stylelint": "^16.5.0", "stylelint": "^16.6.1",
"stylelint-config-html": "^1.1.0", "stylelint-config-html": "^1.1.0",
"stylelint-config-recommended": "^14.0.0", "stylelint-config-recommended": "^14.0.0",
"stylelint-config-standard": "^36.0.0", "stylelint-config-standard": "^36.0.0",
...@@ -150,10 +151,10 @@ ...@@ -150,10 +151,10 @@
"typescript": "~5.4.5", "typescript": "~5.4.5",
"unocss": "^0.58.9", "unocss": "^0.58.9",
"unocss-preset-weapp": "^0.58.8", "unocss-preset-weapp": "^0.58.8",
"unplugin-auto-import": "^0.17.5", "unplugin-auto-import": "^0.17.6",
"unplugin-vue-components": "^0.27.0", "unplugin-vue-components": "^0.27.0",
"vite": "^5.2.11", "vite": "^5.3.1",
"vue-eslint-parser": "^9.4.2" "vue-eslint-parser": "^9.4.3"
}, },
"engines": { "engines": {
"node": ">=16", "node": ">=16",
......
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"prompt": "template"
}
...@@ -3,23 +3,11 @@ ...@@ -3,23 +3,11 @@
"operationFailed": "操作失败", "operationFailed": "操作失败",
"errorTip": "错误提示", "errorTip": "错误提示",
"successTip": "成功提示", "successTip": "成功提示",
"errorMessage": "操作失败,系统异常!", "errorMessage": "操作失败,系统异常",
"timeoutMessage": "'登录信息过期,请重新登录!'", "unauthorizeMessage": "'登录信息过期,请重新登录",
"apiTimeoutMessage": "接口请求超时,请刷新页面重试!", "apiTimeoutMessage": "接口请求超时",
"apiRequestFailed": "请求出错,请稍候重试", "apiRequestFailed": "请求出错,请稍候重试",
"networkException": "网络异常", "networkException": "网络异常",
"networkExceptionMsg": "网络异常,请检查您的网络连接是否正常!", "networkExceptionMsg": "网络异常,请检查您的网络连接是否正常"
"errMsg401": "用户没有权限(令牌、用户名、密码错误)!",
"errMsg403": "用户得到授权,但是访问是被禁止的。!",
"errMsg404": "网络请求错误,未找到该资源!",
"errMsg405": "网络请求错误,请求方法未允许!",
"errMsg408": "网络请求超时!",
"errMsg500": "服务器错误,请联系管理员!",
"errMsg501": "网络未实现!",
"errMsg502": "网络错误!",
"errMsg503": "服务不可用,服务器暂时过载或维护!",
"errMsg504": "网络超时!",
"errMsg505": "http版本不支持该请求!"
} }
} }
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
"name" : "Beta App", "name" : "Beta App",
"appid" : "__UNI__2E9441A", "appid" : "__UNI__2E9441A",
"description" : "APP 基础工程", "description" : "APP 基础工程",
"versionName" : "1.0.0", "versionName" : "1.0.1",
"versionCode" : 10000, "versionCode" : 10001,
"transformPx" : false, "transformPx" : false,
"locale" : "zh-Hans", "locale" : "zh-Hans",
"vueVersion" : "3", "vueVersion" : "3",
...@@ -117,7 +117,8 @@ ...@@ -117,7 +117,8 @@
}, },
"uniStatistics" : { "uniStatistics" : {
"enable" : true, "enable" : true,
"version" : "2" "version" : "2",
"debug" : true
}, },
/* 快应用特有相关 */ /* 快应用特有相关 */
"quickapp" : {}, "quickapp" : {},
......
...@@ -31,6 +31,9 @@ ...@@ -31,6 +31,9 @@
Stomp.connect(() => { Stomp.connect(() => {
Stomp.send('/app/ping', 'ping') 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() {
......
{
"debug": false,
"redis": false,
"cachetime": 604800,
"sessionExpireTime": 1800,
"realtimeStat": true,
"cronMin": false,
"cron": [
{
"type": "stat",
"time": "* * * 0"
},
{
"type": "stat",
"time": "* * 1 10"
},
{
"type": "stat",
"time": "1 * 1 20"
},
{
"type": "stat",
"time": "* 1 3 30"
},
{
"type": "active-device",
"time": "* * 0 10"
},
{
"type": "active-user",
"time": "* * 0 20"
},
{
"type": "page",
"time": "* * 3 20"
},
{
"type": "event",
"time": "* * 4 20"
},
{
"type": "error",
"time": "* * 5 20"
},
{
"type": "loyalty",
"time": "* * 6 20"
},
{
"type": "clean",
"time": "* * 5 30"
},
{
"type": "retention-device",
"time": "* * 2 20"
},
{
"type": "retention-device",
"time": "* 1 4 30"
},
{
"type": "retention-device",
"time": "1 * 2 30"
},
{
"type": "retention-user",
"time": "* * 3 40"
},
{
"type": "retention-user",
"time": "* 1 5 40"
},
{
"type": "retention-user",
"time": "1 * 6 30"
},
{
"type": "pay-result",
"time": "* * * 10",
"dimension": "hour",
"description": "每小时执行统计(会自动统计小时、天、周、月、季度、年度)",
"timely": true
}
],
"batchInsertNum": 5000,
"errorCheck": {
"needCheck": true,
"checkTime": 5
},
"cleanLog": {
"open": true,
"reserveDays": {
"sessionLog": 31,
"userSessionLog": 31,
"pageLog": 7,
"eventLog": 7,
"shareLog": 7,
"errorLog": 7
}
}
}
## 1.0.17(2024-04-26)
- 兼容uni-app-x对客户端uniPlatform的调整(uni-app-x内uniPlatform区分app-android、app-ios)
## 1.0.16(2023-04-25) ## 1.0.16(2023-04-25)
- 新增maxTokenLength配置,用于限制数据库用户记录token数组的最大长度 - 新增maxTokenLength配置,用于限制数据库用户记录token数组的最大长度
## 1.0.15(2023-04-06) ## 1.0.15(2023-04-06)
......
{ {
"displayName": "uni-id-common", "displayName": "uni-id-common",
"version": "1.0.16", "version": "1.0.17",
"description": "包含uni-id token生成、校验、刷新功能的云函数公共模块", "description": "包含uni-id token生成、校验、刷新功能的云函数公共模块",
"keywords": [ "keywords": [
"uni-id-common", "uni-id-common",
...@@ -9,16 +9,14 @@ ...@@ -9,16 +9,14 @@
"权限" "权限"
], ],
"repository": "https://gitcode.net/dcloud/uni-id-common", "repository": "https://gitcode.net/dcloud/uni-id-common",
"engines": { "engines": {},
"HBuilderX": "^3.1.0"
},
"dcloudext": { "dcloudext": {
"sale": { "sale": {
"regular": { "regular": {
"price": "0.00" "price": 0
}, },
"sourcecode": { "sourcecode": {
"price": "0.00" "price": 0
} }
}, },
"contact": { "contact": {
...@@ -41,7 +39,8 @@ ...@@ -41,7 +39,8 @@
"platforms": { "platforms": {
"cloud": { "cloud": {
"tcb": "y", "tcb": "y",
"aliyun": "y" "aliyun": "y",
"alipay": "n"
}, },
"client": { "client": {
"Vue": { "Vue": {
......
{ {
"name": "uni-id-common", "name": "uni-id-common",
"version": "1.0.16", "version": "1.0.17",
"description": "uni-id token生成、校验、刷新", "description": "uni-id token生成、校验、刷新",
"homepage": "https://uniapp.dcloud.io/uniCloud/uni-id-common.html", "homepage": "https://uniapp.dcloud.io/uniCloud/uni-id-common.html",
"repository": { "repository": {
......
## 1.0.5(2024-04-28)
解决Android API 24版本以下设备,安装apk失败的问题。
## 1.0.4(2023-12-08)
兼容asset目录文件的处理
## 1.0.3(2023-10-27)
遵循UniError规范
## 1.0.2(2023-10-27)
修改文档
## 1.0.1(2023-10-27)
支持js层调用
## 1.0.0(2023-10-26)
安装apk的插件
{
"displayName": "uni-installApk",
"version": "1.0.5",
"description": "uni-installApk",
"keywords": [
"uni-installApk"
],
"repository": "",
"engines": {
"HBuilderX": "^3.94"
},
"dcloudext": {
"type": "uts",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "android.permission.REQUEST_INSTALL_PACKAGES"
},
"npmurl": ""
},
"id": "uni-installApk",
"uni_modules": {
"dependencies": [],
"uni-ext-api": {
"uni": {
"installApk": {
"name": "installApk",
"app": {
"js": true,
"kotlin": true,
"swift": false
}
}
}
},
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-android": "y",
"app-ios": "n"
},
"H5-mobile": {
"Safari": "n",
"Android Browser": "n",
"微信浏览器(Android)": "n",
"QQ浏览器(Android)": "n"
},
"H5-pc": {
"Chrome": "n",
"IE": "n",
"Edge": "n",
"Firefox": "n",
"Safari": "n"
},
"小程序": {
"微信": "n",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "n",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}
\ No newline at end of file
# uni-installApk
## 使用说明
Android平台安装Apk
**注意: 3.95以下需要自定义基座**
### uni.installApk(options : InstallApkOptions):void
安装apk
参数说明
```
type InstallApkOptions = {
/**
* apk文件地址
*/
filePath : string,
/**
* 接口调用成功的回调函数
* @defaultValue null
*/
success ?: (res : any) => void,
/**
* 接口调用失败的回调函数
* @defaultValue null
*/
fail ?: (err : any) => void,
/**
* 接口调用结束的回调函数(调用成功、失败都会执行)
* @defaultValue null
*/
complete ?: (res : any) => void,
}
```
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="io.dcloud.uni.installApk">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</manifest>
\ No newline at end of file
import { InstallApkOptions, InstallApkSuccess } from "../interface.uts"
import { InstallApkFailImpl } from "../unierror.uts"
import Intent from 'android.content.Intent';
import Build from 'android.os.Build';
import File from 'java.io.File';
import FileProvider from 'androidx.core.content.FileProvider';
import Context from 'android.content.Context';
import Uri from 'android.net.Uri';
import FileOutputStream from 'java.io.FileOutputStream';
import IOException from 'java.io.IOException';
export function installApk(options : InstallApkOptions) : void {
const context = UTSAndroid.getAppContext() as Context
var filePath = UTSAndroid.convert2AbsFullPath(options.filePath)
var apkFile : File | null = null;
if (filePath.startsWith("/android_asset/")) {
filePath = filePath.replace("/android_asset/", "")
apkFile = copyAssetFileToPrivateDir(context, filePath)
} else {
apkFile = new File(filePath)
}
if (apkFile != null && !apkFile.exists() && !apkFile.isFile()) {
let error = new InstallApkFailImpl(1300002);
options.fail?.(error)
options.complete?.(error)
return
}
const intent = new Intent()
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.setAction(Intent.ACTION_VIEW)
if (Build.VERSION.SDK_INT >= 24) {
const authority = context.getPackageName() + ".dc.fileprovider"
const apkUri = FileProvider.getUriForFile(context, authority, apkFile!!)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(apkFile!!), "application/vnd.android.package-archive");
}
context.startActivity(intent)
const success : InstallApkSuccess = {
errMsg: "success"
}
options.success?.(success)
options.complete?.(success)
}
function copyAssetFileToPrivateDir(context : Context, fileName : string) : File | null {
try {
const destPath = context.getCacheDir().getPath() + "/apks/" + fileName
const outFile = new File(destPath)
const parentFile = outFile.getParentFile()
if (parentFile != null) {
if (!parentFile.exists()) {
parentFile.mkdirs()
}
}
if (!outFile.exists()) {
outFile.createNewFile()
}
const inputStream = context.getAssets().open(fileName)
const outputStream = new FileOutputStream(outFile)
let buffer = new ByteArray(1024);
do {
let len = inputStream.read(buffer);
if (len == -1) {
break;
}
outputStream.write(buffer, 0, len)
} while (true)
inputStream.close()
outputStream.close()
if (Build.VERSION.SDK_INT < 24) {
changePermissionRecursive(new File(context.getCacheDir().getPath() + "/apks/"))
}
return outFile
} catch (e : Exception) {
e.printStackTrace()
}
return null
}
function changePermissionRecursive(file: File){
const cmd = "chmod -R 777 " + file.getAbsolutePath()
const runtime = Runtime.getRuntime()
try {
runtime.exec(cmd)
} catch (e: IOException) {
}
}
declare namespace UniNamespace {
interface InstallApkSuccess {
/**
* 安装成功消息
*/
errMsg : string
}
type InstallApkErrorCode = 1300002
interface InstallApkFail {
errCode : InstallApkErrorCode
}
type InstallApkComplete = any
type InstallApkSuccessCallback = (res : InstallApkSuccess) => void
type InstallApkFailCallback = (err : InstallApkFail) => void
type InstallApkCompleteCallback = (res : InstallApkComplete) => void
interface InstallApkOptions {
/**
* apk文件地址
*/
filePath : string,
/**
* 接口调用成功的回调函数
* @defaultValue null
*/
success ?: InstallApkSuccessCallback | null,
/**
* 接口调用失败的回调函数
* @defaultValue null
*/
fail ?: InstallApkFailCallback | null,
/**
* 接口调用结束的回调函数(调用成功、失败都会执行)
* @defaultValue null
*/
complete ?: InstallApkCompleteCallback | null
}
}
declare interface Uni {
/**
* installApk()
* @description
* 安装apk
* @param {InstallApkOptions}
* @return {void}
* @uniPlatform {
* "app": {
* "android": {
* "osVer": "5.0",
* "uniVer": "3.94+",
* "unixVer": "3.94+"
* },
* "ios": {
* "osVer": "x",
* "uniVer": "x",
* "unixVer": "x"
* }
* }
* }
* @example
```typescript
uni.installApk({
filePath: "/xx/xx/xx.apk",
complete: (res: any) => {
console.log("complete => " + JSON.stringify(res));
}
});
```
*/
installApk(options : UniNamespace.InstallApkOptions) : void
}
export interface Uni {
/**
* installApk()
* @description
* 安装apk
* @param {InstallApkOptions}
* @return {void}
* @uniPlatform {
* "app": {
* "android": {
* "osVer": "5.0",
* "uniVer": "3.94+",
* "unixVer": "3.94+"
* },
* "ios": {
* "osVer": "x",
* "uniVer": "x",
* "unixVer": "x"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "x"
* }
* }
* @example
```typescript
uni.installApk({
filePath: "/xx/xx/xx.apk",
complete: (res: any) => {
console.log("complete => " + JSON.stringify(res));
}
});
```
*/
installApk(options : InstallApkOptions) : void
}
export type InstallApkSuccess = {
/**
* 安装成功消息
*/
errMsg : string
}
export type InstallApkComplete = any
export type InstallApkSuccessCallback = (res : InstallApkSuccess) => void
/**
* 错误码
* - 1300002 找不到文件
*/
export type InstallApkErrorCode = 1300002
/**
* 网络请求失败的错误回调参数
*/
export interface InstallApkFail extends IUniError {
errCode : InstallApkErrorCode
};
export type InstallApkFailCallback = (err : InstallApkFail) => void
export type InstallApkCompleteCallback = (res : InstallApkComplete) => void
export type InstallApkOptions = {
/**
* apk文件地址
*/
filePath : string,
/**
* 接口调用成功的回调函数
* @defaultValue null
*/
success ?: InstallApkSuccessCallback | null,
/**
* 接口调用失败的回调函数
* @defaultValue null
*/
fail ?: InstallApkFailCallback | null,
/**
* 接口调用结束的回调函数(调用成功、失败都会执行)
* @defaultValue null
*/
complete ?: InstallApkCompleteCallback | null
}
\ No newline at end of file
import { InstallApkErrorCode, InstallApkFail } from "./interface.uts"
/**
* 错误主题
*/
export const UniErrorSubject = 'uni-installApk';
/**
* 错误码
* @UniError
*/
export const UniErrors : Map<InstallApkErrorCode, string> = new Map([
/**
* 找不到文件
*/
[1300002, 'No such file'],
]);
export class InstallApkFailImpl extends UniError implements InstallApkFail {
constructor(errCode : InstallApkErrorCode) {
super();
this.errSubject = UniErrorSubject;
this.errCode = errCode;
this.errMsg = UniErrors[errCode] ?? "";
}
}
\ No newline at end of file
## 0.8.1(2024-04-28)
- 修复 在 HX 4.0.3+ uni-app x 项目运行到 Android 调不起安装的Bug
## 0.8.0(2024-04-15)
- 修复 更新弹窗 data 中新增初始化字段
## 0.7.9(2024-03-15)
- 移除无用代码
- 调整 is_silently 类型为可为 null
## 0.7.8(2024-01-04)
- 新增 移除无用代码
## 0.7.7(2024-01-04)
- 新增 uni-app x 项目中新增 @show 回调
## 0.7.6(2023-12-21)
- 修复 iOS使用升级中心云打包时报错(使用新版的 [uts-progressNotification](https://ext.dcloud.net.cn/plugin?name=uts-progressNotification) 插件,如果之前下载过请删除 `uts-progressNotification\utssdk\app-ios` 文件夹)
## 0.7.5(2023-12-12)
- 新增 通知栏进度条使用 uts-progressNotification 插件
- 新增 依赖 uni-installApk、uts-progressNotification。使用前要安装插件三方依赖
## 0.7.4(2023-11-29)
- 修复 uni-app-x 项目中由上版引发的无法升级的Bug
## 0.7.3(2023-11-27)
- 修复 在 uni-app x 中无更新时报错的Bug
## 0.7.2(2023-11-20)
- 新增 插件根目录 utils 文件夹中新增 check-update-nvue.js 文件(vue2 的 nvue 页面请引用该文件)
## 0.7.1(2023-11-17)
- 修复 运行至浏览器 ts 语法报错
## 0.7.0(2023-11-10)
- 新增 兼容 uni-app x 项目 [详情](https://uniapp.dcloud.net.cn/uniCloud/upgrade-center.html)
## 0.6.5(2023-10-27)
- 修复 安装 wgt 报错 manifest.json 文件不存在的Bug
## 0.6.4(2023-09-01) ## 0.6.4(2023-09-01)
chore: 优化代码结构 chore: 优化代码结构
## 0.6.3(2023-08-30) ## 0.6.3(2023-08-30)
......
{ {
"displayName": "升级中心 uni-upgrade-center - App", "displayName": "升级中心 uni-upgrade-center - App",
"version": "0.6.4", "version": "0.8.1",
"description": "uni升级中心 - 客户端检查更新", "description": "uni升级中心 - 客户端检查更新",
"keywords": [ "keywords": [
"uniCloud", "uniCloud",
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
], ],
"repository": "https://gitee.com/dcloud/uni-upgrade-center/tree/master/uni_modules/uni-upgrade-center-app", "repository": "https://gitee.com/dcloud/uni-upgrade-center/tree/master/uni_modules/uni-upgrade-center-app",
"engines": { "engines": {
"HBuilderX": "^3.2.14" "HBuilderX": "^4.03"
}, },
"dcloudext": { "dcloudext": {
"sale": { "sale": {
...@@ -34,12 +34,17 @@ ...@@ -34,12 +34,17 @@
}, },
"id": "uni-upgrade-center-app", "id": "uni-upgrade-center-app",
"uni_modules": { "uni_modules": {
"dependencies": [], "dependencies": [
"uni-installApk",
"uts-progressNotification",
"uts-openSchema"
],
"encrypt": [], "encrypt": [],
"platforms": { "platforms": {
"cloud": { "cloud": {
"tcb": "y", "tcb": "y",
"aliyun": "y" "aliyun": "y",
"alipay": "y"
}, },
"client": { "client": {
"App": { "App": {
......
...@@ -3,14 +3,14 @@ ...@@ -3,14 +3,14 @@
<view class="content botton-radius"> <view class="content botton-radius">
<view class="content-top"> <view class="content-top">
<text class="content-top-text">{{title}}</text> <text class="content-top-text">{{title}}</text>
<image class="content-top" style="top: 0;" width="100%" height="100%" src="../images/bg_top.png"> <image class="content-top" style="top: 0;" width="100%" height="100%" src="/uni_modules/uni-upgrade-center-app/static/bg_top.png">
</image> </image>
</view> </view>
<view class="content-header"></view> <view class="content-header"></view>
<view class="content-body"> <view class="content-body">
<view class="title"> <view class="title">
<text>{{subTitle}}</text> <text>{{subTitle}}</text>
<!-- <text style="padding-left:20rpx;font-size: 0.5em;color: #666;">v.{{version}}</text> --> <text class="content-body-version">{{version}}</text>
</view> </view>
<view class="body"> <view class="body">
<scroll-view class="box-des-scroll" scroll-y="true"> <scroll-view class="box-des-scroll" scroll-y="true">
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
<template v-else> <template v-else>
<template v-if="!downloadSuccess"> <template v-if="!downloadSuccess">
<view class="progress-box flex-column" v-if="downloading"> <view class="progress-box flex-column" v-if="downloading">
<progress class="progress" border-radius="35" :percent="downLoadPercent" activeColor="#3DA7FF" show-info <progress class="progress" :percent="downLoadPercent" activeColor="#3DA7FF" show-info
stroke-width="10" /> stroke-width="10"/>
<view style="width:100%;font-size: 28rpx;display: flex;justify-content: space-around;"> <view style="width:100%;font-size: 28rpx;display: flex;justify-content: space-around;">
<text>{{downLoadingText}}</text> <text>{{downLoadingText}}</text>
<text>({{downloadedSize}}/{{packageFileSize}}M)</text> <text>({{downloadedSize}}/{{packageFileSize}}M)</text>
...@@ -44,8 +44,12 @@ ...@@ -44,8 +44,12 @@
plain :loading="installing" :disabled="installing" @click="installPackage"> plain :loading="installing" :disabled="installing" @click="installPackage">
{{installing ? '正在安装……' : '下载完成,立即安装'}} {{installing ? '正在安装……' : '下载完成,立即安装'}}
</button> </button>
<button v-else-if="installed && !isWGT" class="content-button" style="border: none;color: #fff;"
plain :loading="installing" :disabled="installing" @click="installPackage">
安装未完成,点击安装
</button>
<button v-if="installed && isWGT" class="content-button" style="border: none;color: #fff;" plain <button v-else-if="installed && isWGT" class="content-button" style="border: none;color: #fff;" plain
@click="restart"> @click="restart">
安装完毕,点击重启 安装完毕,点击重启
</button> </button>
...@@ -53,15 +57,19 @@ ...@@ -53,15 +57,19 @@
</view> </view>
</view> </view>
<image v-if="!is_mandatory" class="close-img" src="../images/app_update_close.png" @click.stop="closeUpdate"> <image v-if="!is_mandatory" class="close-img" src="/uni_modules/uni-upgrade-center-app/static/app_update_close.png" @click.stop="closeUpdate">
</image> </image>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
// #ifdef APP
import { createNotificationProgress, cancelNotificationProgress, finishNotificationProgress } from '@/uni_modules/uts-progressNotification'
// #endif
const localFilePathKey = 'UNI_ADMIN_UPGRADE_CENTER_LOCAL_FILE_PATH' const localFilePathKey = 'UNI_ADMIN_UPGRADE_CENTER_LOCAL_FILE_PATH'
const platform_iOS = 'iOS'; const platform_iOS = 'iOS';
const platform_Android = 'Android';
let downloadTask = null; let downloadTask = null;
let openSchemePromise let openSchemePromise
...@@ -131,7 +139,11 @@ ...@@ -131,7 +139,11 @@
// 默认安装包信息 // 默认安装包信息
title: '更新日志', title: '更新日志',
contents: '', contents: '',
version: '',
is_mandatory: false, is_mandatory: false,
url: '',
platform: [],
store_list: null,
// 可自定义属性 // 可自定义属性
subTitle: '发现新版本', subTitle: '发现新版本',
...@@ -170,11 +182,8 @@ ...@@ -170,11 +182,8 @@
}, },
onBackPress() { onBackPress() {
// 强制更新不允许返回 // 强制更新不允许返回
if (this.is_mandatory) { if (this.is_mandatory) return true
return true if (!this.needNotificationProgress) downloadTask && downloadTask.abort()
}
downloadTask && downloadTask.abort()
}, },
onHide() { onHide() {
openSchemePromise = null openSchemePromise = null
...@@ -184,10 +193,16 @@ ...@@ -184,10 +193,16 @@
return this.type === 'wgt' return this.type === 'wgt'
}, },
isiOS() { isiOS() {
return !this.isWGT ? this.platform.includes(platform_iOS) : false; return !this.isWGT ? this.platform.indexOf(platform_iOS) !== -1 : false;
},
isAndroid() {
return this.platform.indexOf(platform_Android) !== -1
}, },
isAppStore() { isAppStore() {
return this.isiOS || (!this.isiOS && !this.isWGT && this.url.indexOf('.apk') === -1) return this.isiOS || (!this.isiOS && !this.isWGT && this.url.indexOf('.apk') === -1)
},
needNotificationProgress() {
return this.platform.indexOf(platform_iOS) === -1 && !this.is_mandatory
} }
}, },
methods: { methods: {
...@@ -212,6 +227,20 @@ ...@@ -212,6 +227,20 @@
} }
} }
}, },
askAbortDownload() {
uni.showModal({
title: '是否取消下载?',
cancelText: '否',
confirmText: '是',
success: res => {
if (res.confirm) {
downloadTask && downloadTask.abort()
cancelNotificationProgress()
uni.navigateBack()
}
}
});
},
async closeUpdate() { async closeUpdate() {
if (this.downloading) { if (this.downloading) {
if (this.is_mandatory) { if (this.is_mandatory) {
...@@ -221,33 +250,27 @@ ...@@ -221,33 +250,27 @@
duration: 500 duration: 500
}) })
} }
uni.showModal({ if (!this.needNotificationProgress) {
title: '是否取消下载?', this.askAbortDownload()
cancelText: '否', return;
confirmText: '是', }
success: res => {
if (res.confirm) {
downloadTask && downloadTask.abort()
uni.navigateBack()
}
}
});
return;
} }
if (this.downloadSuccess && this.tempFilePath) { if (!this.needNotificationProgress && this.downloadSuccess && this.tempFilePath) {
// 包已经下载完毕,稍后安装,将包保存在本地 // 包已经下载完毕,稍后安装,将包保存在本地
await this.saveFile(this.tempFilePath, this.version) await this.saveFile(this.tempFilePath, this.version)
uni.navigateBack()
return;
} }
uni.navigateBack() uni.navigateBack()
}, },
updateApp() { updateApp() {
this.checkStoreScheme().catch(() => { this.checkStoreScheme()
this.downloadPackage() .catch(() => {
}) this.downloadPackage()
})
.finally(() => {
openSchemePromise = null
})
}, },
// 跳转应用商店 // 跳转应用商店
checkStoreScheme() { checkStoreScheme() {
...@@ -279,19 +302,17 @@ ...@@ -279,19 +302,17 @@
url: this.url, url: this.url,
success: res => { success: res => {
if (res.statusCode == 200) { if (res.statusCode == 200) {
this.downloadSuccess = true;
// fix: wgt 文件下载完成后后缀不是 wgt // fix: wgt 文件下载完成后后缀不是 wgt
if (this.isWGT && !res.tempFilePath.endsWith('.wgt')) { if (this.isWGT && res.tempFilePath.split('.').slice(-1)[0] !== 'wgt') {
const failCallback = (e) => { const failCallback = (e) => {
console.log('[FILE RENAME FAIL]:', JSON.stringify(e)); console.log('[FILE RENAME FAIL]:', JSON.stringify(e));
} }
plus.io.resolveLocalFileSystemURL(res.tempFilePath, (entry) => { plus.io.resolveLocalFileSystemURL(res.tempFilePath, (entry) => {
const originName = entry.name
entry.getParent((parent) => { entry.getParent((parent) => {
const newName = `new_wgt_${Date.now()}.wgt` const newName = `new_wgt_${Date.now()}.wgt`
entry.copyTo(parent, newName, (result) => { entry.copyTo(parent, newName, (res) => {
this.tempFilePath = res.tempFilePath.replace(originName, newName) this.tempFilePath = res.fullPath
this.downLoadComplete() this.downLoadComplete()
}, failCallback) }, failCallback)
}, failCallback) }, failCallback)
}, failCallback); }, failCallback);
...@@ -307,9 +328,24 @@ ...@@ -307,9 +328,24 @@
this.downLoadPercent = res.progress; this.downLoadPercent = res.progress;
this.downloadedSize = (res.totalBytesWritten / Math.pow(1024, 2)).toFixed(2); this.downloadedSize = (res.totalBytesWritten / Math.pow(1024, 2)).toFixed(2);
this.packageFileSize = (res.totalBytesExpectedToWrite / Math.pow(1024, 2)).toFixed(2); this.packageFileSize = (res.totalBytesExpectedToWrite / Math.pow(1024, 2)).toFixed(2);
if (this.needNotificationProgress && !this.downloadSuccess) {
createNotificationProgress({
title: "升级中心正在下载安装包……",
content: `${this.downLoadPercent}%`,
progress: this.downLoadPercent,
onClick: () => {
this.askAbortDownload()
}
})
}
}); });
if (this.needNotificationProgress) {
uni.navigateBack()
}
}, },
downLoadComplete() { downLoadComplete() {
this.downloadSuccess = true;
this.downloading = false; this.downloading = false;
this.downLoadPercent = 0 this.downLoadPercent = 0
...@@ -318,6 +354,16 @@ ...@@ -318,6 +354,16 @@
downloadTask = null; downloadTask = null;
if (this.needNotificationProgress) {
finishNotificationProgress({
title: "安装升级包",
content: "下载完成"
})
this.installPackage();
return
}
// 强制更新,直接安装 // 强制更新,直接安装
if (this.is_mandatory) { if (this.is_mandatory) {
this.installPackage(); this.installPackage();
...@@ -329,7 +375,6 @@ ...@@ -329,7 +375,6 @@
if (this.isWGT) { if (this.isWGT) {
this.installing = true; this.installing = true;
} }
plus.runtime.install(this.tempFilePath, { plus.runtime.install(this.tempFilePath, {
force: false force: false
}, async res => { }, async res => {
...@@ -495,6 +540,16 @@ ...@@ -495,6 +540,16 @@
line-height: 38px; line-height: 38px;
} }
.content-body-version {
padding-left: 10px;
color: #fff;
font-size: 10px;
margin-left: 5px;
padding: 2px 4px;
border-radius: 10px;
background: #50aefd;
}
.footer { .footer {
height: 150rpx; height: 150rpx;
display: flex; display: flex;
...@@ -522,7 +577,7 @@ ...@@ -522,7 +577,7 @@
.progress { .progress {
width: 90%; width: 90%;
height: 40rpx; height: 40rpx;
border-radius: 35px; /* border-radius: 35px; */
} }
.close-img { .close-img {
......
## 升级中心 - app插件与 `uni-admin` 版本关系 文档已移至 [uni-upgrade-center](https://uniapp.dcloud.net.cn/uniCloud/upgrade-center.html)
\ No newline at end of file
### `uni-admin >= 1.9.3`:云函数 `checkVersion` 废弃,使用 uni-admin 自带的 `uni-upgrade-center` 云函数。
# uni-upgrade-center - App
### 概述
> 统一管理App及App在`Android`、`iOS`平台上`App安装包`和`wgt资源包`的发布升级
> uni升级中心分为业务插件和后台管理插件。本插件为业务插件,包括uni升级中心客户端检查更新的前后端逻辑。后台管理系统另见 [uni-upgrade-center - Admin](https://ext.dcloud.net.cn/plugin?id=4470)
### uni升级中心 - 客户端检查更新插件
- 一键式检查更新,同时支持整包升级与wgt资源包更新
- 好看、实用、可自定义的客户端提示框
## 安装指引
1. 依赖数据库`opendb-app-versions`,如果没有此库,请在云服务空间中创建。
2. 使用`HBuilderX 3.1.0+`,因为要使用到`uni_modules`
3. 在插件市场打开本插件页面,在右侧点击`使用 HBuilderX 导入插件`,选择要导入的项目点击确定
4. 绑定一个服务空间。自 `0.6.0` 起,依赖 `uni-admin 1.9.3+``uni-upgrade-center 云函数`,请和 uni-admin 项目关联同一个服务空间
5. 找到`/uni_modules/uni-upgrade-center-app/uniCloud/cloudfunctions/check-version`,右键上传部署。自 `0.6.0` 起,依赖 `uni-admin 1.9.3+``uni-upgrade-center 云函数`,插件不再单独提供云函数,这样可以省下一个云函数名额。
6.`pages.json`中添加页面路径。**注:请不要设置为pages.json中第一项**
```json
"pages": [
// ……其他页面配置
{
"path": "uni_modules/uni-upgrade-center-app/pages/upgrade-popup",
"style": {
"disableScroll": true,
"app-plus": {
"backgroundColorTop": "transparent",
"background": "transparent",
"titleNView": false,
"scrollIndicator": false,
"popGesture": "none",
"animationType": "fade-in",
"animationDuration": 200
}
}
}
]
```
7.`@/uni_modules/uni-upgrade-center-app/utils/check-update`import到需要用到的地方,调用一下即可
1. 默认使用当前绑定的服务空间,如果要请求其他服务空间,可以使用其他服务空间的 `callFunction`[详情](https://uniapp.dcloud.io/uniCloud/cf-functions.html#call-by-function-cross-space)
8. 升级弹框可自行编写,也可以使用`uni.showModal`,或使用现有的升级弹框样式,如果不满足UI需求请自行替换资源文件。在`utils/check-update.js`中都有实例。
9. wgt更新时,打包前请务必将manifest.json中的版本修改为更高版本。
### 更新下载安装`check-update.js`
*该函数在utils目录下*
1. 如果是静默更新,则不会打开更新弹框,会在后台下载后安装,下次启动应用生效
2. 如果是 iOS,则会直接打开AppStore的链接
3. 其他情况,会将`check-version`返回的结果保存在localStorage中,并跳转进入`upgrade-popup.vue`打开更新弹框
### 检查更新函数`check-version`
*该函数在uniCloud/cloudfunctions目录下*
1. 使用检查更新需要传递三个参数 `appid``appVersion``wgtVersion`
2. `appid` 使用 plus.runtime.appid 获取,*注:真机运行时为固定值HBuilder,在调试的时候请使用本地调试云函数*
3. `appVersion` 使用 plus.runtime.version 获取
4. `wgtVersion` 使用 plus.runtime.getProperty(plus.runtime.appid,(wgtInfo) => { wgtInfo.version }) 获取
5. `check-version`云函数内部会自动获取 App 平台
**Tips**
1. `check-version`云函数内部有版本对比函数(compare)。
- 使用多段式版本格式(如:"3.0.0.0.0.1.0.1", "3.0.0.0.0.1")。如果不满足对比规则,请自行修改。
- 如果修改,请将*pages/upgrade-popup.vue**compare*函数一并修改
## 项目代码说明
### 更新弹框
- `upgrade-popup.vue` - 更新应用:
- 如果云函数`check-version`返回的参数表明需要更新,则将参数保存在localStorage中,带着键值跳转该页面
- 进入时会先从localStorage中尝试取出之前存的安装包路径(此包不会是强制安装类型的包)
- 如果有已经保存的包,则和传进来的 `version` 进行比较,如果相等则安装。大于和小于都不进行安装,因为admin端可能会调整包的版本。不符合更新会将此包删除
- 如果本地没有包或者包不符合安装条件,则进行下载安装包
- 点击下载会有进度条、已下载大小和下载包的大小
- 下载完成会提示安装:
- 如果是 wgt 包,安装时则会提示 正在安装…… 和 安装完成。安装完成会提示是否重启
- 如果是 原生安装包,则直接跳出去覆盖安装
- 下载过程中,如果退出会提示是否取消下载。如果是强制更新,则只会提示正在下载请稍后,此时不可退出
- 如果是下载完成了没有安装就退出,则会将下载完成的包保存在本地。将包的本地路径和包version保存在localStorage中
### 工具类 utils
- `call-check-version`
- 请求云函数`check-version`拿取版本检测结果
- `check-update`
- 调用`call-check-version`并根据结果判断是否显示更新弹框
### 云函数
- `check-version` - 检查应用更新:
- 根据传参,先检测传参是否完整,appid appVersion wgtVersion 必传
- 先从数据库取出所有该平台(会从上下文读取平台信息)的所有线上发行更新
- 再从所有线上发行更新中取出版本最大的一版。如果可以,尽量先检测wgt的线上发行版更新
- 使用上一步取出的版本包的版本号 和传参 appVersion、wgtVersion 来检测是否有更新。必须同时大于这两项,因为上一次可能是wgt热更新,否则返回暂无更新
- 如果库中 wgt包 版本大于传参 appVersion,但是不满足 min_uni_version < appVersion,则不会使用wgt更新,会接着判断库中 app包version 是否大于 appVersion
- 返回结果:
|code|message|
|:-:|:-:|
|0|当前版本已经是最新的,不需要更新|
|101|wgt更新|
|102|整包更新|
|-101|暂无更新或检查appid是否填写正确|
|-102|请检查传参是否填写正确|
\ No newline at end of file
export default function() {
// #ifdef APP-PLUS
return new Promise((resolve, reject) => {
plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
let data = {
action: 'checkVersion',
appid: plus.runtime.appid,
appVersion: plus.runtime.version,
wgtVersion: widgetInfo.version
}
uniCloud.callFunction({
name: 'uni-upgrade-center',
data,
success: (e) => {
console.log("e: ", e);
resolve(e)
},
fail: (error) => {
reject(error)
}
})
})
})
// #endif
// #ifndef APP-PLUS
return new Promise((resolve, reject) => {
reject({
message: '请在App中使用'
})
})
// #endif
}
export type StoreListItem = {
enable : boolean
id : string
name : string
scheme : string
priority : number // 优先级
}
export type UniUpgradeCenterResult = {
_id : string
appid : string
name : string
title : string
contents : string
url : string // 安装包下载地址
platform : Array<string> // Array<'Android' | 'iOS'>
version : string // 版本号 1.0.0
uni_platform : string // "android" | "ios" // 版本号 1.0.0
stable_publish : boolean // 是否是稳定版
is_mandatory : boolean // 是否强制更新
is_silently : boolean | null // 是否静默更新
create_env : string // "upgrade-center"
create_date : number
message : string
code : number
type : string // "native_app" | "wgt"
store_list : StoreListItem[] | null
min_uni_version : string | null // 升级 wgt 的最低 uni-app 版本
}
export default function () : Promise<UniUpgradeCenterResult> {
// #ifdef APP
return new Promise<UniUpgradeCenterResult>((resolve, reject) => {
const systemInfo = uni.getSystemInfoSync()
const appId = systemInfo.appId
const appVersion = systemInfo.appVersion //systemInfo.appVersion
// #ifndef UNI-APP-X
if (typeof appId === 'string' && typeof appVersion === 'string' && appId.length > 0 && appVersion.length > 0) {
plus.runtime.getProperty(appId, function (widgetInfo) {
if (widgetInfo.version) {
let data = {
action: 'checkVersion',
appid: appId,
appVersion: appVersion,
wgtVersion: widgetInfo.version
}
uniCloud.callFunction({
name: 'uni-upgrade-center',
data,
success: (e) => {
resolve(e.result as UniUpgradeCenterResult)
},
fail: (error) => {
reject(error)
}
})
} else {
reject('widgetInfo.version is EMPTY')
}
})
} else {
reject('plus.runtime.appid is EMPTY')
}
// #endif
// #ifdef UNI-APP-X
if (typeof appId === 'string' && typeof appVersion === 'string' && appId.length > 0 && appVersion.length > 0) {
let data = {
action: 'checkVersion',
appid: appId,
appVersion: appVersion,
is_uniapp_x: true,
wgtVersion: '0.0.0.0.0.1'
}
try {
uniCloud.callFunction({
name: 'uni-upgrade-center',
data: data
}).then(res => {
const code = res.result['code']
const codeIsNumber = ['Int', 'Long', 'number'].includes(typeof code)
if (codeIsNumber) {
if ((code as number) == 0) {
reject({
code: res.result['code'],
message: res.result['message']
})
} else if ((code as number) < 0) {
reject({
code: res.result['code'],
message: res.result['message']
})
} else {
const result = JSON.parse<UniUpgradeCenterResult>(JSON.stringify(res.result)) as UniUpgradeCenterResult
resolve(result)
}
}
}).catch<void>((err : any | null) => {
const error = err as UniCloudError
if (error.errMsg == '未匹配到云函数[uni-upgrade-center]')
error.errMsg = '【uni-upgrade-center-app】未配置uni-upgrade-center,无法升级。参考: https://uniapp.dcloud.net.cn/uniCloud/upgrade-center.html'
reject(error.errMsg)
})
} catch (e) {
reject(e.message)
}
} else {
reject('invalid appid or appVersion')
}
// #endif
})
// #endif
// #ifndef APP
return new Promise((resolve, reject) => {
reject({
message: '请在App中使用'
})
})
// #endif
}
import callCheckVersion from './call-check-version' function callCheckVersion() {
// #ifdef APP-PLUS
return new Promise((resolve, reject) => {
plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
let data = {
action: 'checkVersion',
appid: plus.runtime.appid,
appVersion: plus.runtime.version,
wgtVersion: widgetInfo.version
}
uniCloud.callFunction({
name: 'uni-upgrade-center',
data,
success: (e) => {
resolve(e)
},
fail: (error) => {
reject(error)
}
})
})
})
// #endif
// #ifndef APP-PLUS
return new Promise((resolve, reject) => {})
// #endif
}
// 推荐再App.vue中使用 // 推荐再App.vue中使用
const PACKAGE_INFO_KEY = '__package_info__' const PACKAGE_INFO_KEY = '__package_info__'
......
import callCheckVersion, { UniUpgradeCenterResult } from "./call-check-version"
// #ifdef UNI-APP-X
import { openSchema } from '@/uni_modules/uts-openSchema'
// #endif
// 推荐再App.vue中使用
const PACKAGE_INFO_KEY = '__package_info__'
// uni-app 项目无法从 vue 中导出 ComponentPublicInstance 类型,故使用条件编译
// #ifdef UNI-APP-X
export default function (component : ComponentPublicInstance | null = null) : Promise<UniUpgradeCenterResult> {
// #endif
// #ifndef UNI-APP-X
export default function () : Promise<UniUpgradeCenterResult> {
// #endif
return new Promise<UniUpgradeCenterResult>((resolve, reject) => {
callCheckVersion().then(async (uniUpgradeCenterResult) => {
// NOTE uni-app x 3.96 解构有问题
const code = uniUpgradeCenterResult.code
const message = uniUpgradeCenterResult.message
const url = uniUpgradeCenterResult.url // 安装包下载地址
// 此处逻辑仅为示例,可自行编写
if (code > 0) {
// 腾讯云和阿里云下载链接不同,需要处理一下,阿里云会原样返回
const tcbRes = await uniCloud.getTempFileURL({ fileList: [url] });
if (typeof tcbRes.fileList[0].tempFileURL !== 'undefined') uniUpgradeCenterResult.url = tcbRes.fileList[0].tempFileURL;
/**
* 提示升级一
* 使用 uni.showModal
*/
// return updateUseModal(uniUpgradeCenterResult)
// #ifndef UNI-APP-X
// 静默更新,只有wgt有
if (uniUpgradeCenterResult.is_silently) {
uni.downloadFile({
url,
success: res => {
if (res.statusCode == 200) {
// 下载好直接安装,下次启动生效
plus.runtime.install(res.tempFilePath, {
force: false
});
}
}
});
return;
}
// #endif
/**
* 提示升级二
* 官方适配的升级弹窗,可自行替换资源适配UI风格
*/
// #ifndef UNI-APP-X
uni.setStorageSync(PACKAGE_INFO_KEY, uniUpgradeCenterResult)
uni.navigateTo({
url: `/uni_modules/uni-upgrade-center-app/pages/upgrade-popup?local_storage_key=${PACKAGE_INFO_KEY}`,
fail: (err) => {
console.error('更新弹框跳转失败', err)
uni.removeStorageSync(PACKAGE_INFO_KEY)
}
})
// #endif
// #ifdef UNI-APP-X
component?.$callMethod('show', true, uniUpgradeCenterResult)
// #endif
return resolve(uniUpgradeCenterResult)
} else if (code < 0) {
console.error(message)
return reject(uniUpgradeCenterResult)
}
return resolve(uniUpgradeCenterResult)
}).catch((err) => {
reject(err)
})
});
}
/**
* 使用 uni.showModal 升级
*/
function updateUseModal(packageInfo : UniUpgradeCenterResult) : void {
// #ifdef APP
const {
title, // 标题
contents, // 升级内容
is_mandatory, // 是否强制更新
url, // 安装包下载地址
type,
platform
} = packageInfo;
let isWGT = type === 'wgt'
let isiOS = !isWGT ? platform.includes('iOS') : false;
// #ifndef UNI-APP-X
let confirmText = isiOS ? '立即跳转更新' : '立即下载更新'
// #endif
// #ifdef UNI-APP-X
let confirmText = '立即下载更新'
// #endif
return uni.showModal({
title,
content: contents,
showCancel: !is_mandatory,
confirmText,
success: res => {
if (res.cancel) return;
if (isiOS) {
// iOS 平台跳转 AppStore
// #ifndef UNI-APP-X
plus.runtime.openURL(url);
// #endif
// #ifdef UNI-APP-X
openSchema(url)
// #endif
return;
}
uni.showToast({
title: '后台下载中……',
duration: 1000
});
// wgt 和 安卓下载更新
uni.downloadFile({
url,
success: res => {
if (res.statusCode !== 200) {
console.error('下载安装包失败');
return;
}
// 下载好直接安装,下次启动生效
// uni-app x 项目没有 plus5+ 故使用条件编译
// #ifndef UNI-APP-X
plus.runtime.install(res.tempFilePath, {
force: false
}, () => {
if (is_mandatory) {
//更新完重启app
plus.runtime.restart();
return;
}
uni.showModal({
title: '安装成功是否重启?',
success: res => {
if (res.confirm) {
//更新完重启app
plus.runtime.restart();
}
}
});
}, err => {
uni.showModal({
title: '更新失败',
content: err
.message,
showCancel: false
});
});
// #endif
// #ifdef UNI-APP-X
uni.installApk({
filePath: res.tempFilePath,
success: () => {
uni.showModal({
title: '安装成功请手动重启'
});
},
fail: err => {
uni.showModal({
title: '更新失败',
content: err.message,
showCancel: false
});
}
});
// #endif
}
});
}
});
// #endif
}
## 1.0.0(2024-04-25)
- 更新 在 Android 和 iOS 上打开链接的 UTS API
{
"displayName": "uts-openSchema",
"version": "1.0.0",
"description": "在 Android 和 iOS 上打开链接的 UTS API",
"keywords": [
"uts-openSchema"
],
"repository": "",
"engines": {
"HBuilderX": "^4.0"
},
"dcloudext": {
"type": "uts",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"id": "uts-openSchema",
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "y"
},
"client": {
"Vue": {
"vue2": "n",
"vue3": "y"
},
"App": {
"app-android": "y",
"app-ios": "y"
},
"H5-mobile": {
"Safari": "n",
"Android Browser": "n",
"微信浏览器(Android)": "n",
"QQ浏览器(Android)": "n"
},
"H5-pc": {
"Chrome": "n",
"IE": "n",
"Edge": "n",
"Firefox": "n",
"Safari": "n"
},
"小程序": {
"微信": "n",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "n",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}
\ No newline at end of file
# uts-openSchema
### 开发文档
[UTS 语法](https://uniapp.dcloud.net.cn/tutorial/syntax-uts.html)
[UTS API插件](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html)
[UTS 组件插件](https://uniapp.dcloud.net.cn/plugin/uts-component.html)
[Hello UTS](https://gitcode.net/dcloud/hello-uts)
\ No newline at end of file
{
"minSdkVersion": "21"
}
\ No newline at end of file
import Intent from 'android.content.Intent'
import Uri from 'android.net.Uri'
import { OpenSchema } from '../interface.uts'
export const openSchema: OpenSchema = function (url: string) {
if (typeof url === 'string' && url.length > 0) {
const context = UTSAndroid.getUniActivity()!
const uri = Uri.parse(url)
const intent = new Intent(Intent.ACTION_VIEW, uri)
intent.setData(uri)
context.startActivity(intent)
} else {
console.error('url param ERROR:', JSON.stringify(url))
}
}
{
"deploymentTarget": "12.0"
}
\ No newline at end of file
import { OpenSchema } from '../interface.uts'
export const openSchema: OpenSchema = function(url: string): void {
if (typeof url == 'string' && url.length > 0) {
let uri = new URL(string = url)
if (uri != null && UIApplication.shared.canOpenURL(uri!)) {
UIApplication.shared.open(uri!, options = new Map<UIApplication.OpenExternalURLOptionsKey, any>(), completionHandler = null)
}else {
console.error('url param Error: ', url)
}
}else {
console.error('url param Error: ', url)
}
}
\ No newline at end of file
export type OpenSchema = (url: string) => void
## 1.1.0(2024-03-08)
修复uniapp打包报错问题
## 1.0.9(2024-02-29)
去除代码过时警告
## 1.0.8(2023-12-21)
去除app-ios目录
## 1.0.7(2023-12-11)
去除无用代码
## 1.0.6(2023-12-11)
修改文档
## 1.0.5(2023-12-11)
1.修改插件名称
2.修改插件引入方式为import导入
## 1.0.4(2023-11-30)
1. createNotificationProgress增加`onClick`回调
2.修复在小米部分系统上,通知消息会归类于不重要通知的bug
## 1.0.3(2023-11-28)
更新截图
## 1.0.2(2023-11-28)
修改资源的包名
## 1.0.1(2023-11-28)
更新文档
## 1.0.0(2023-11-28)
Android通知栏显示进度插件
{
"displayName": "uts-progressNotification",
"version": "1.1.0",
"description": "uts-progressNotification",
"keywords": [
"uts-progressNotification"
],
"repository": "",
"engines": {
"HBuilderX": "^3.91"
},
"dcloudext": {
"type": "uts",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "TargetSDKVersion33以上时需配置\n`android.permission.POST_NOTIFICATIONS`"
},
"npmurl": ""
},
"id": "uts-progressNotification",
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-android": {
"minVersion": "19"
},
"app-ios": "n"
},
"H5-mobile": {
"Safari": "n",
"Android Browser": "n",
"微信浏览器(Android)": "n",
"QQ浏览器(Android)": "n"
},
"H5-pc": {
"Chrome": "n",
"IE": "n",
"Edge": "n",
"Firefox": "n",
"Safari": "n"
},
"小程序": {
"微信": "n",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "n",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}
# uts-progressNotification
## 使用说明
Android平台创建显示进度的通知栏消息
**注意: 需要自定义基座,否则点击通知栏消息不会拉起应用**
### 导入
需要import导入插件
### createNotificationProgress(options : CreateNotificationProgressOptions) : void,
创建显示进度的通知栏消息
参数说明
```
export type CreateNotificationProgressOptions = {
/**
* 通知标题
* @defaultValue 应用名称
*/
title ?: string | null
/**
* 通知内容
*/
content : string,
/**
* 进度
*/
progress : number,
/**
* 点击通知消息回调
* @defaultValue null
*/
onClick? : (() => void) | null
}
```
### finishNotificationProgress(options: FinishNotificationProgressOptions) : void
完成时调用的API,比如下载完成后需要显示下载完成并隐藏进度时调用。
参数说明
```
export type FinishNotificationProgressOptions = {
/**
* 通知标题
* @defaultValue 应用名称
*/
title ?: string | null
/**
* 通知内容
*/
content : string,
/**
* 点击通知消息回调
*/
onClick : () => void
}
```
### cancelNotificationProgress() : void
取消通知消息显示
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="uts.sdk.modules.utsProgressNotification">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application>
<activity android:name="uts.sdk.modules.utsProgressNotification.TransparentActivity"
android:theme="@style/DCNotificationProgressTranslucentTheme" android:hardwareAccelerated="true"
android:screenOrientation="user" android:exported="true">
</activity>
</application>
</manifest>
import Activity from "android.app.Activity";
import Bundle from 'android.os.Bundle';
import Build from 'android.os.Build';
import View from 'android.view.View';
import Color from 'android.graphics.Color';
import WindowManager from 'android.view.WindowManager';
import { globalNotificationProgressFinishCallBack, globalNotificationProgressCallBack } from './callbacks.uts';
import { ACTION_DOWNLOAD_FINISH, ACTION_DOWNLOAD_PROGRESS } from "./constant.uts"
export class TransparentActivity extends Activity {
constructor() {
super()
}
@Suppress("DEPRECATION")
override onCreate(savedInstanceState : Bundle | null) {
super.onCreate(savedInstanceState)
this.fullScreen(this)
const action = this.getIntent().getAction()
if (action == ACTION_DOWNLOAD_FINISH) {
setTimeout(() => {
globalNotificationProgressFinishCallBack()
globalNotificationProgressFinishCallBack = () => { }
}, 100)
this.overridePendingTransition(0, 0)
}
if (action == ACTION_DOWNLOAD_PROGRESS) {
setTimeout(() => {
globalNotificationProgressCallBack?.()
globalNotificationProgressCallBack = () => { }
}, 100)
this.overridePendingTransition(0, 0)
}
setTimeout(() => {
this.finish()
}, 20)
}
@Suppress("DEPRECATION")
private fullScreen(activity : Activity) {
if (Build.VERSION.SDK_INT >= 19) {
if (Build.VERSION.SDK_INT >= 21) {
const window = activity.getWindow();
const decorView = window.getDecorView();
const option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
} else {
const window = activity.getWindow();
const attributes = window.getAttributes();
const flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
attributes.flags |= flagTranslucentStatus;
window.setAttributes(attributes);
}
}
}
}
export let globalNotificationProgressCallBack : (() => void) | null = () => { }
export let globalNotificationProgressFinishCallBack = () => { }
{
"minSdkVersion": "19"
}
\ No newline at end of file
export const ACTION_DOWNLOAD_FINISH = "ACTION_DOWNLOAD_FINISH"
export const ACTION_DOWNLOAD_PROGRESS = "ACTION_DOWNLOAD_PROGRESS"
\ No newline at end of file
import Build from 'android.os.Build';
import Context from 'android.content.Context';
import NotificationManager from 'android.app.NotificationManager';
import NotificationChannel from 'android.app.NotificationChannel';
import Notification from 'android.app.Notification';
import Intent from 'android.content.Intent';
import ComponentName from 'android.content.ComponentName';
import PendingIntent from 'android.app.PendingIntent';
import { CreateNotificationProgressOptions, FinishNotificationProgressOptions } from '../interface.uts';
import { ACTION_DOWNLOAD_FINISH, ACTION_DOWNLOAD_PROGRESS } from "./constant.uts"
import { globalNotificationProgressFinishCallBack, globalNotificationProgressCallBack } from './callbacks.uts';
export { TransparentActivity } from './TransparentActivity.uts';
const DOWNLOAD_PROGRESS_NOTIFICATION_ID : Int = 7890
const DC_DOWNLOAD_CHANNEL_ID = "下载文件"
const DC_DOWNLOAD_CHANNEL_NAME = "用于显示现在进度的渠道"
let notificationBuilder : Notification.Builder | null = null
let timeId = -1
let histroyProgress = 0
let isProgress = false
export function createNotificationProgress(options : CreateNotificationProgressOptions) : void {
const { content, progress, onClick } = options
if (progress == 100) {
clearTimeout(timeId)
const context = UTSAndroid.getAppContext() as Context
realCreateNotificationProgress(options.title ?? getAppName(context), content, progress, onClick)
reset()
return
}
histroyProgress = progress
if (timeId != -1) {
return
}
const context = UTSAndroid.getAppContext() as Context
if (!isProgress) {
realCreateNotificationProgress(options.title ?? getAppName(context), content, histroyProgress, onClick)
isProgress = true
} else {
timeId = setTimeout(() => {
realCreateNotificationProgress(options.title ?? getAppName(context), content, histroyProgress, onClick)
timeId = -1
}, 1000)
}
}
export function cancelNotificationProgress() : void {
const context = UTSAndroid.getAppContext() as Context
const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(DOWNLOAD_PROGRESS_NOTIFICATION_ID)
reset()
}
function realCreateNotificationProgress(title : string, content : string, progress : number, cb : (() => void) | null) : void {
globalNotificationProgressCallBack = cb
const context = UTSAndroid.getAppContext() as Context
const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
createDownloadChannel(notificationManager)
const builder = createNotificationBuilder(context)
builder.setProgress(100, progress.toInt(), false)
builder.setContentTitle(title)
builder.setContentText(content)
builder.setContentIntent(createPendingIntent(context, ACTION_DOWNLOAD_PROGRESS));
notificationManager.notify(DOWNLOAD_PROGRESS_NOTIFICATION_ID, builder.build())
}
export function finishNotificationProgress(options : FinishNotificationProgressOptions) {
globalNotificationProgressFinishCallBack = options.onClick
const context = UTSAndroid.getAppContext() as Context
const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
createDownloadChannel(notificationManager)
const builder = createNotificationBuilder(context)
builder.setProgress(0, 0, false)
builder.setContentTitle(options.title ?? getAppName(context))
builder.setContentText(options.content)
//小米rom setOngoing未false的时候,会被通知管理器归为不重要通知
// builder.setOngoing(false)
builder.setAutoCancel(true);
builder.setContentIntent(createPendingIntent(context, ACTION_DOWNLOAD_FINISH));
notificationManager.notify(DOWNLOAD_PROGRESS_NOTIFICATION_ID, builder.build())
reset()
}
function reset() {
isProgress = false
notificationBuilder = null
histroyProgress = 0
if (timeId != -1) {
clearTimeout(timeId)
timeId = -1
}
}
function createPendingIntent(context : Context, action : string) : PendingIntent {
const i = new Intent(action);
i.setComponent(new ComponentName(context.getPackageName(), "uts.sdk.modules.utsProgressNotification.TransparentActivity"));
let flags = PendingIntent.FLAG_ONE_SHOT;
if (Build.VERSION.SDK_INT >= 23) {
flags = PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE;
}
return PendingIntent.getActivity(context, DOWNLOAD_PROGRESS_NOTIFICATION_ID, i, flags);
}
function createDownloadChannel(notificationManager : NotificationManager) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
const channel = new NotificationChannel(
DC_DOWNLOAD_CHANNEL_ID,
DC_DOWNLOAD_CHANNEL_NAME,
NotificationManager.IMPORTANCE_LOW
)
notificationManager.createNotificationChannel(channel)
}
}
@Suppress("DEPRECATION")
function createNotificationBuilder(context : Context) : Notification.Builder {
if (notificationBuilder == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationBuilder = new Notification.Builder(context, DC_DOWNLOAD_CHANNEL_ID)
} else {
notificationBuilder = new Notification.Builder(context)
}
notificationBuilder!.setSmallIcon(context.getApplicationInfo().icon)
notificationBuilder!.setOngoing(true)
notificationBuilder!.setSound(null)
}
return notificationBuilder!
}
@Suppress("DEPRECATION")
function getAppName(context : Context) : string {
let appName = ""
try {
const packageManager = context.getPackageManager()
const applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0)
appName = packageManager.getApplicationLabel(applicationInfo) as string
} catch (e : Exception) {
e.printStackTrace()
}
return appName
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DCNotificationProgressTranslucentTheme">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
</resources>
\ No newline at end of file
export type CreateNotificationProgressOptions = {
/**
* 通知标题
* @defaultValue 应用名称
*/
title ?: string | null
/**
* 通知内容
*/
content : string,
/**
* 进度
*/
progress : number,
/**
* 点击通知消息回调
* @defaultValue null
*/
onClick? : (() => void) | null
}
export type FinishNotificationProgressOptions = {
/**
* 通知标题
* @defaultValue 应用名称
*/
title ?: string | null
/**
* 通知内容
*/
content : string,
/**
* 点击通知消息回调
*/
onClick : () => void
}
export type CreateNotificationProgress = (options : CreateNotificationProgressOptions) => void;
export type CancelNotificationProgress = () => void;
export type FinishNotificationProgress = (options: FinishNotificationProgressOptions) => void
...@@ -64,7 +64,7 @@ const transform: AxiosTransform = { ...@@ -64,7 +64,7 @@ const transform: AxiosTransform = {
let timeoutMsg = '' let timeoutMsg = ''
switch (status) { switch (status) {
case HTTP.Status.UNAUTHORIZED: { case HTTP.Status.UNAUTHORIZED: {
timeoutMsg = t('sys.api.timeoutMessage') timeoutMsg = t('sys.api.unauthorizeMessage')
// 清空 token // 清空 token
const userStore = useUserStoreWithOut() const userStore = useUserStoreWithOut()
...@@ -183,7 +183,7 @@ const transform: AxiosTransform = { ...@@ -183,7 +183,7 @@ const transform: AxiosTransform = {
let errMessage = '' let errMessage = ''
try { try {
if (code === 'ECONNABORTED' && message.includes('timeout')) { if (code === 'ECONNABORTED' || message.includes('timeout')) {
errMessage = t('sys.api.apiTimeoutMessage') errMessage = t('sys.api.apiTimeoutMessage')
} }
if (err?.includes('Network Error')) { if (err?.includes('Network Error')) {
......
...@@ -11,7 +11,7 @@ export function checkUpgrade(toast = false) { ...@@ -11,7 +11,7 @@ export function checkUpgrade(toast = false) {
} }
checkVersion() checkVersion()
.then((res) => { .then((res) => {
if (toast && res?.result?.code === 0) { if (toast && res?.code === 0) {
Message.toast('已是最新版本~') Message.toast('已是最新版本~')
} }
}) })
......
...@@ -135,8 +135,6 @@ declare module 'vue' { ...@@ -135,8 +135,6 @@ declare module 'vue' {
FuiWhiteSpace: typeof import('./../src/components/FirstUI/fui-white-space/fui-white-space.vue')['default'] FuiWhiteSpace: typeof import('./../src/components/FirstUI/fui-white-space/fui-white-space.vue')['default']
FuiWingBlank: typeof import('./../src/components/FirstUI/fui-wing-blank/fui-wing-blank.vue')['default'] FuiWingBlank: typeof import('./../src/components/FirstUI/fui-wing-blank/fui-wing-blank.vue')['default']
Icon: typeof import('./../src/components/Icon/index.vue')['default'] Icon: typeof import('./../src/components/Icon/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
ThumbnailPreview: typeof import('./../src/components/ThumbnailPreview/index.vue')['default'] ThumbnailPreview: typeof import('./../src/components/ThumbnailPreview/index.vue')['default']
} }
} }
// 本文件用于,使用JQL语法操作项目关联的uniCloud空间的数据库,方便开发调试和远程数据库管理
// 本文件用于,使用JQL语法操作项目关联的uniCloud空间的数据库,方便开发调试和远程数据库管理
// 编写clientDB的js API(也支持常规js语法,比如var),可以对云数据库进行增删改查操作。不支持uniCloud-db组件写法
// 可以全部运行,也可以选中部分代码运行。点击工具栏上的运行按钮或者按下【F5】键运行代码
// 如果文档中存在多条JQL语句,只有最后一条语句生效
// 如果混写了普通js,最后一条语句需是数据库操作语句
// 此处代码运行不受DB Schema的权限控制,移植代码到实际业务中注意在schema中配好permission
// 不支持clientDB的action
// 数据库查询有最大返回条数限制,详见:https://uniapp.dcloud.net.cn/uniCloud/cf-database.html#limit
// 详细JQL语法,请参考:https://uniapp.dcloud.net.cn/uniCloud/jql.html
// 下面示例查询uni-id-users表的所有数据
db.collection('uni-id-users').get();
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论