diff --git a/pages.json b/pages.json index 2fc32d33258f9f90664cc241d4c268a230c95a6f..fd3ee4a5df18415319d7abd285dc275f8695f280 100644 --- a/pages.json +++ b/pages.json @@ -37,6 +37,12 @@ "style": { "navigationBarTitleText": "progress" } + }, + { + "path": "pages/component/form/form", + "style": { + "navigationBarTitleText": "form" + } }, { "path": "pages/component/button/button", @@ -1047,6 +1053,13 @@ "navigationBarTitleText" : "install-apk", "enablePullDownRefresh" : false } + }, + { + "path" : "pages/template/schema/schema", + "style" : + { + "navigationBarTitleText" : "打开schema示例" + } } ], diff --git a/pages/API/element-draw/element-draw.uvue b/pages/API/element-draw/element-draw.uvue index 2c67f756645b08fa97f69ed04fa360ae0357c595..1947536513fd4ba288aa2ec6f82b60a6f625458d 100644 --- a/pages/API/element-draw/element-draw.uvue +++ b/pages/API/element-draw/element-draw.uvue @@ -59,66 +59,66 @@ drawText() { let element = uni.getElementById('draw-text-view') let ctx = element!.getDrawableContext() - let width = element.getBoundingClientRect().width + let width = element!.getBoundingClientRect().width ctx!.reset() - ctx.font = "15px" - ctx.textAlign = "center" + ctx!.font = "15px" + ctx!.textAlign = "center" for (var i = 0; i < this.texts.length; i++) { let value = this.texts[i] if (i % 2 == 0) { - ctx.fillText(value, width / 2, (20 * (i + 1))) + ctx!.fillText(value, width / 2, (20 * (i + 1))) } else { - ctx.strokeText(value, width / 2, (20 * (i + 1))) + ctx!.strokeText(value, width / 2, (20 * (i + 1))) } } - ctx.update() + ctx!.update() }, drawLines() { let ctx = uni.getElementById('draw-line-view')!.getDrawableContext() ctx!.reset() - ctx.lineWidth = 10; + ctx!.lineWidth = 10; ["round", "bevel", "miter"].forEach((join, i) => { - ctx.lineJoin = join; - ctx.beginPath(); - ctx.moveTo(5, 10 + i * 40); - ctx.lineTo(50, 50 + i * 40); - ctx.lineTo(90, 10 + i * 40); - ctx.lineTo(130, 50 + i * 40); - ctx.lineTo(170, 10 + i * 40); - ctx.stroke(); + ctx!.lineJoin = join; + ctx!.beginPath(); + ctx!.moveTo(5, 10 + i * 40); + ctx!.lineTo(50, 50 + i * 40); + ctx!.lineTo(90, 10 + i * 40); + ctx!.lineTo(130, 50 + i * 40); + ctx!.lineTo(170, 10 + i * 40); + ctx!.stroke(); }); - ctx.lineWidth = 1 + ctx!.lineWidth = 1 var space = 170 - ctx.strokeStyle = '#09f'; - ctx.beginPath(); - ctx.moveTo(10 + space, 10); - ctx.lineTo(140 + space, 10); - ctx.moveTo(10 + space, 140); - ctx.lineTo(140 + space, 140); - ctx.stroke(); + ctx!.strokeStyle = '#09f'; + ctx!.beginPath(); + ctx!.moveTo(10 + space, 10); + ctx!.lineTo(140 + space, 10); + ctx!.moveTo(10 + space, 140); + ctx!.lineTo(140 + space, 140); + ctx!.stroke(); // Draw lines - ctx.strokeStyle = 'black'; + ctx!.strokeStyle = 'black'; ['butt', 'round', 'square'].forEach((lineCap, i) => { - ctx.lineWidth = 15; - ctx.lineCap = lineCap; - ctx.beginPath(); - ctx.moveTo(25 + space + i * 50, 10); - ctx.lineTo(25 + space + i * 50, 140); - ctx.stroke(); + ctx!.lineWidth = 15; + ctx!.lineCap = lineCap; + ctx!.beginPath(); + ctx!.moveTo(25 + space + i * 50, 10); + ctx!.lineTo(25 + space + i * 50, 140); + ctx!.stroke(); }); - ctx.lineWidth = 1; - this.drawDashedLine([], ctx); - this.drawDashedLine([2, 2], ctx); - this.drawDashedLine([10, 10], ctx); - this.drawDashedLine([20, 5], ctx); - this.drawDashedLine([15, 3, 3, 3], ctx); - this.drawDashedLine([20, 3, 3, 3, 3, 3, 3, 3], ctx); - ctx.lineDashOffset = 18; - this.drawDashedLine([12, 3, 3], ctx); - ctx.lineDashOffset = 0 - ctx.setLineDash([0]) - ctx.update() + ctx!.lineWidth = 1; + this.drawDashedLine([], ctx!); + this.drawDashedLine([2, 2], ctx!); + this.drawDashedLine([10, 10], ctx!); + this.drawDashedLine([20, 5], ctx!); + this.drawDashedLine([15, 3, 3, 3], ctx!); + this.drawDashedLine([20, 3, 3, 3, 3, 3, 3, 3], ctx!); + ctx!.lineDashOffset = 18; + this.drawDashedLine([12, 3, 3], ctx!); + ctx!.lineDashOffset = 0 + ctx!.setLineDash([0]) + ctx!.update() }, drawDashedLine(pattern : Array, ctx : DrawableContext) { ctx.beginPath(); @@ -134,7 +134,7 @@ // Draw shapes for (var i = 0; i < 4; i++) { for (var j = 0; j < 3; j++) { - ctx.beginPath(); + ctx!.beginPath(); var x = 25 + j * 50; // x coordinate var y = 25 + i * 50; // y coordinate var radius = 20; // Arc radius @@ -142,22 +142,22 @@ var endAngle = Math.PI + (Math.PI * j) / 2; // End point on circle var clockwise = i % 2 == 0 ? false : true; // clockwise or anticlockwise - ctx.arc(x, y, radius, startAngle, endAngle, clockwise); + ctx!.arc(x, y, radius, startAngle, endAngle, clockwise); if (i > 1) { - ctx.fill(); + ctx!.fill(); } else { - ctx.stroke(); + ctx!.stroke(); } } } - ctx.update() + ctx!.update() }, drawStar() { let ctx = uni.getElementById('draw-dash-line')!.getDrawableContext() ctx!.reset() - ctx.beginPath(); + ctx!.beginPath(); var horn = 5; // 画5个角 var angle = 360 / horn; // 五个角的度数 // 两个圆的半径 @@ -169,45 +169,45 @@ for (var i = 0; i < horn; i++) { // 角度转弧度:角度/180*Math.PI // 外圆顶点坐标 - ctx.lineTo(Math.cos((18 + i * angle) / 180.0 * Math.PI) * R + x, -Math.sin((18 + i * angle) / 180.0 * Math.PI) * R + y); + ctx!.lineTo(Math.cos((18 + i * angle) / 180.0 * Math.PI) * R + x, -Math.sin((18 + i * angle) / 180.0 * Math.PI) * R + y); // 內圆顶点坐标 - ctx.lineTo(Math.cos((54 + i * angle) / 180.0 * Math.PI) * r + x, -Math.sin((54 + i * angle) / 180.0 * Math.PI) * r + y); + ctx!.lineTo(Math.cos((54 + i * angle) / 180.0 * Math.PI) * r + x, -Math.sin((54 + i * angle) / 180.0 * Math.PI) * r + y); } // closePath:关闭路径,将路径的终点与起点相连 - ctx.closePath(); - ctx.lineWidth = 3; - ctx.fillStyle = '#E4EF00'; - ctx.strokeStyle = "red"; - ctx.fill(); - ctx.stroke(); + ctx!.closePath(); + ctx!.lineWidth = 3; + ctx!.fillStyle = '#E4EF00'; + ctx!.strokeStyle = "red"; + ctx!.fill(); + ctx!.stroke(); - ctx.lineWidth = 10; - ctx.beginPath() - ctx.moveTo(170, 100) - ctx.lineTo(255, 15) - ctx.lineTo(340, 100) - ctx.closePath() - ctx.fill() - ctx.strokeStyle = "blue" - ctx.stroke() - ctx.beginPath() - ctx.moveTo(170, 145) - ctx.lineTo(255, 45) - ctx.lineTo(340, 145) - ctx.closePath() - ctx.fill() - ctx.strokeStyle = "gray" - ctx.stroke() + ctx!.lineWidth = 10; + ctx!.beginPath() + ctx!.moveTo(170, 100) + ctx!.lineTo(255, 15) + ctx!.lineTo(340, 100) + ctx!.closePath() + ctx!.fill() + ctx!.strokeStyle = "blue" + ctx!.stroke() + ctx!.beginPath() + ctx!.moveTo(170, 145) + ctx!.lineTo(255, 45) + ctx!.lineTo(340, 145) + ctx!.closePath() + ctx!.fill() + ctx!.strokeStyle = "gray" + ctx!.stroke() // 未设置beginPath,导致上下表现一致,与前端一致 - ctx.moveTo(170, 190) - ctx.lineTo(255, 90) - ctx.lineTo(340, 190) - ctx.closePath() - ctx.fillStyle = "orange" - ctx.fill() - ctx.strokeStyle = "khaki" - ctx.stroke() - ctx.update() + ctx!.moveTo(170, 190) + ctx!.lineTo(255, 90) + ctx!.lineTo(340, 190) + ctx!.closePath() + ctx!.fillStyle = "orange" + ctx!.fill() + ctx!.strokeStyle = "khaki" + ctx!.stroke() + ctx!.update() }, hex(num : number) : string { if (num == 0) { @@ -228,80 +228,80 @@ drawhouse() { let ctx = uni.getElementById('draw-house')!.getDrawableContext() ctx!.reset() - ctx.lineWidth = 10; + ctx!.lineWidth = 10; // Wall - ctx.strokeRect(75, 140, 150, 110); + ctx!.strokeRect(75, 140, 150, 110); // Door - ctx.fillRect(130, 190, 40, 60); + ctx!.fillRect(130, 190, 40, 60); // Roof - ctx.beginPath(); - ctx.moveTo(50, 140); - ctx.lineTo(150, 60); - ctx.lineTo(250, 140); - ctx.closePath(); - ctx.stroke(); - ctx.update() + ctx!.beginPath(); + ctx!.moveTo(50, 140); + ctx!.lineTo(150, 60); + ctx!.lineTo(250, 140); + ctx!.closePath(); + ctx!.stroke(); + ctx!.update() }, drawPoint() { let ctx = uni.getElementById('draw-style')!.getDrawableContext() ctx!.reset() for (let i = 0; i < 6; i++) { for (let j = 0; j < 6; j++) { - ctx.strokeStyle = `rgb(0,${Math.floor(255 - 42.5 * i)},${Math.floor(255 - 42.5 * j)})`; - ctx.beginPath(); - ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true); - ctx.stroke(); + ctx!.strokeStyle = `rgb(0,${Math.floor(255 - 42.5 * i)},${Math.floor(255 - 42.5 * j)})`; + ctx!.beginPath(); + ctx!.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true); + ctx!.stroke(); } } for (let i = 0; i < 6; i++) { for (let j = 0; j < 6; j++) { - ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)},${Math.floor(255 - 42.5 * j)},0)`; - ctx.fillRect(180 + j * 25, i * 25, 25, 25); + ctx!.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)},${Math.floor(255 - 42.5 * j)},0)`; + ctx!.fillRect(180 + j * 25, i * 25, 25, 25); } } - ctx.update() + ctx!.update() }, drawRect() { let ctx = uni.getElementById('draw-odd')!.getDrawableContext() ctx!.reset() // Create path - ctx.moveTo(30, 90); - ctx.lineTo(110, 20); - ctx.lineTo(240, 130); - ctx.lineTo(60, 130); - ctx.lineTo(190, 20); - ctx.lineTo(270, 90); - ctx.closePath(); + ctx!.moveTo(30, 90); + ctx!.lineTo(110, 20); + ctx!.lineTo(240, 130); + ctx!.lineTo(60, 130); + ctx!.lineTo(190, 20); + ctx!.lineTo(270, 90); + ctx!.closePath(); // Fill path - ctx.fillStyle = "green"; - ctx.fill("evenodd"); - ctx.update() + ctx!.fillStyle = "green"; + ctx!.fill("evenodd"); + ctx!.update() }, drawArcTo() { let ctx = uni.getElementById('draw-arcto')!.getDrawableContext() ctx!.reset() - ctx.beginPath(); - ctx.moveTo(50, 20); - ctx.bezierCurveTo(230, 30, 150, 60, 50, 100); - ctx.stroke(); + ctx!.beginPath(); + ctx!.moveTo(50, 20); + ctx!.bezierCurveTo(230, 30, 150, 60, 50, 100); + ctx!.stroke(); - ctx.fillStyle = "blue"; + ctx!.fillStyle = "blue"; // start point - ctx.fillRect(50, 20, 10, 10); + ctx!.fillRect(50, 20, 10, 10); // end point - ctx.fillRect(50, 100, 10, 10); + ctx!.fillRect(50, 100, 10, 10); - ctx.fillStyle = "red"; + ctx!.fillStyle = "red"; // control point one - ctx.fillRect(230, 30, 10, 10); + ctx!.fillRect(230, 30, 10, 10); // control point two - ctx.fillRect(150, 70, 10, 10); - ctx.update() + ctx!.fillRect(150, 70, 10, 10); + ctx!.update() } } } diff --git a/pages/CSS/text/line-height.uvue b/pages/CSS/text/line-height.uvue index 411710aa61b2a0854041980b8d48cfdff0a3793a..31361fe263d875e57370493febf1d8367a4956cb 100644 --- a/pages/CSS/text/line-height.uvue +++ b/pages/CSS/text/line-height.uvue @@ -1,13 +1,10 @@ @@ -19,5 +16,8 @@ \ No newline at end of file + diff --git a/pages/component/form/form.test.js b/pages/component/form/form.test.js new file mode 100644 index 0000000000000000000000000000000000000000..4af6858c58680436807374d9a2cc818a3df65243 --- /dev/null +++ b/pages/component/form/form.test.js @@ -0,0 +1,64 @@ +const PAGE_PATH = '/pages/component/form/form' + +const DEFAULT_NICK_NAME = 'hello' +const DEFAULT_GENDER = '0' +const DEFAULT_AGE = 18 +const DEFAULT_SWITCH = true + +const CHANGE_NICK_NAME = 'hello' +const CHANGE_GENDER = '0' +const CHANGE_AGE = 50 +const CHANGE_SWITCH = false + +describe('form', () => { + let page + beforeAll(async () => { + page = await program.reLaunch(PAGE_PATH) + await page.waitFor(500) + }) + it('submit', async () => { + await page.setData({ + nickname: CHANGE_NICK_NAME, + age: CHANGE_AGE, + switch: CHANGE_SWITCH + }) + await page.waitFor(200) + + const btnSubmit = await page.$('.btn-l') + await btnSubmit.tap() + await page.waitFor(200) + + const { + formData + } = await page.data() + + // expect(formData['nickname']).toBe(CHANGE_NICK_NAME) + expect(formData['gender']).toBe(CHANGE_GENDER) + expect(formData['loves'][0]).toBe('1') + expect(formData['age']).toBe(CHANGE_AGE) + expect(formData['switch']).toBe(CHANGE_SWITCH) + }) + it('reset', async () => { + await page.setData({ + nickname: CHANGE_NICK_NAME, + age: CHANGE_AGE, + switch: CHANGE_SWITCH + }) + await page.waitFor(100) + + const btnReset = await page.$('.btn-r') + await btnReset.tap() + await page.waitFor(100) + + const { + formData + } = await page.data() + + // TODO + // expect(formData['nickname']).toBe(DEFAULT_NICK_NAME) + // expect(formData['gender']).toBe(undefined) + // expect(formData['loves'][0]).toBe(undefined) + // expect(formData['age']).toBe(undefined) + // expect(formData['switch']).toBe(undefined) + }) +}) diff --git a/pages/component/form/form.uvue b/pages/component/form/form.uvue new file mode 100644 index 0000000000000000000000000000000000000000..6a5507795f4e35d2867aeed3b86d5a9553ab9086 --- /dev/null +++ b/pages/component/form/form.uvue @@ -0,0 +1,124 @@ + + + + + diff --git a/pages/component/image/image-path.uvue b/pages/component/image/image-path.uvue index 847369a56bd19bec4786dfd1cbf7653b38b73886..08984261a1d92878c3ed1022b33f1cd2540b6180 100644 --- a/pages/component/image/image-path.uvue +++ b/pages/component/image/image-path.uvue @@ -11,6 +11,12 @@ + + 非static目录的src静态路径:./logo.png + + + + @@ -36,7 +42,7 @@ }, { src: logo, - description: '非static目录(需import才能访问)' + description: 'import方式' }, { // #ifdef APP-ANDROID @@ -76,6 +82,10 @@ { src: 'https://request.dcloud.net.cn/api/http/contentType/404.png', description: '错误网络地址,不存在的图片' + }, + { + src: './logo.png', + description: '非static目录的动态路径(不显示是对的)' } ] as Array } diff --git a/pages/component/web-view/web-view.uvue b/pages/component/web-view/web-view.uvue index 04c5eb85c15d8aafaac092d27403e1fa1575c0a5..e2c74487a12695783289ec10f28474a7826ccf68 100644 --- a/pages/component/web-view/web-view.uvue +++ b/pages/component/web-view/web-view.uvue @@ -1,27 +1,19 @@ - + + + - diff --git a/pages/template/custom-tab-bar/custom-tab-bar.uvue b/pages/template/custom-tab-bar/custom-tab-bar.uvue index 7c04890f89952579f100fdc043cc78ebbfd3b156..e21bb64381f5ee8cfc7e0af4a6a7477b7a32a5f6 100644 --- a/pages/template/custom-tab-bar/custom-tab-bar.uvue +++ b/pages/template/custom-tab-bar/custom-tab-bar.uvue @@ -18,11 +18,8 @@ - + - - + - @@ -34,6 +31,9 @@ + + + + @@ -179,15 +179,13 @@ .btn-plus { position: absolute; - left: 43px; width: 70px; height: 70px; + bottom: 21px; border-radius: 50px; background-color: #FE5722; box-shadow: 0 0 4px rgba(0, 0, 0, 0.5); - /* margin-left: auto; - margin-right: auto; */ - margin-top: -35px; + align-self: center; align-items: center; justify-content: center; overflow: visible; @@ -197,4 +195,4 @@ color: #fff; font-size: 32px; } - \ No newline at end of file + diff --git a/pages/template/long-list/long-list.uvue b/pages/template/long-list/long-list.uvue index 4c95a7599430a38b2477e181b0ba4aa9445091be..ab75047ab51f2665ed67c20daa8ba736bb797dc0 100644 --- a/pages/template/long-list/long-list.uvue +++ b/pages/template/long-list/long-list.uvue @@ -102,7 +102,7 @@ }, methods: { // TODO - onStartNestedScroll(event : StartNestedScrollEvent) : boolean { + onStartNestedScroll(_ : StartNestedScrollEvent) : boolean { return true }, onNestedPreScroll(event : NestedPreScrollEvent) { diff --git a/pages/template/schema/schema.uvue b/pages/template/schema/schema.uvue new file mode 100644 index 0000000000000000000000000000000000000000..53807045ca54041954bfcf932ea69f0e6d7b7748 --- /dev/null +++ b/pages/template/schema/schema.uvue @@ -0,0 +1,35 @@ + + + + + diff --git a/uni_modules/uni-upgrade-center-app/changelog.md b/uni_modules/uni-upgrade-center-app/changelog.md new file mode 100644 index 0000000000000000000000000000000000000000..21a54c451df9770bc47b6dac793b829c3a17d04a --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/changelog.md @@ -0,0 +1,72 @@ +## 0.6.5(2023-10-27) +- 修复 安装 wgt 报错 manifest.json 文件不存在的Bug +## 0.6.4(2023-09-01) +chore: 优化代码结构 +## 0.6.3(2023-08-30) +- 修复 下载 wgt 时如果后缀名不正确,重命名后安装 +## 0.6.2(2022-11-21) +- 处理 cloudfunctions 目录 +## 0.6.1(2022-08-17) +- 修复 后台添加应用市场,但都没有启用的情况下报错的Bug (需要 uni-admin 1.9.3+) +## 0.6.0(2022-07-19) +- 新增 支持多应用商店配置(需要 uni-admin 1.9.3+) +## 0.4.1(2022-05-27) +- 修复 上版引出的报错问题 +## 0.4.0(2022-05-27) +- 新增 Android 支持跳转手机自带商店,填写升级包地址时请填写跳转商店链接 +- 新增 改为云对象调用方式,使用更直观 +## 0.3.3(2022-04-14) +- 修复 调用 check-update,当 code 为 0 时没有回调 +## 0.3.2(2022-01-12) +- 优化显示逻辑 +## 0.3.1(2021-11-24) +- 修复 vue3 上图片不显示的Bug +## 0.3.0(2021-11-18) +- 移除 wgt 安装成功后提示,防止重启过快弹框不消失 +## 0.2.2(2021-08-25) +- 兼容vue3.0 +## 0.2.1(2021-07-26) +- 修复 使用腾讯云并手动填写地址时,导致下载链接失效的bug +## 0.2.0(2021-07-13) +- 更新文档 关于报错local_storage_key 为空,请不要将页面路径设置为pages.json中第一项 +## 0.1.9(2021-06-28) +- 更新文档 +- 修复 wgt安装失败时,按钮状态不对 +## 0.1.8(2021-06-16) +- 修复 跳转安装时,导致上次下载的apk还没安装就被删掉的bug +## 0.1.7(2021-06-03) +- 修改 移除static中的图片 +## 0.1.6(2021-06-03) +- 修改 下载更新按钮使用CSS渐变色 +## 0.1.5(2021-04-22) +- 更新check-update函数。现在返回一个Promise,有更新时成功回调,其他情况错误回调 +## 0.1.4(2021-04-13) +- 更新文档。明确云函数调用结果 +## 0.1.3(2021-04-13) +- 解耦云函数与弹框处理。utils中新增 call-check-version.js,可用于单独检测是否有更新 +## 0.1.2(2021-04-07) +- 更新版本对比函数 compare +## 0.1.1(2021-04-07) +- 修复 腾讯云空间下载链接不能下载问题 +## 0.1.0(2021-04-07) +- 新增使用uni.showModal提示升级示例 +- 修改iOS升级提示方式 +## 0.0.7(2021-04-02) +- 修复在iOS上打开弹框报错 +## 0.0.6(2021-04-01) +- 兼容旧版本安卓 +## 0.0.5(2021-04-01) +- 修复低版本安卓上进度条错位 +## 0.0.4(2021-04-01) +- 更新readme +- 修复check-update语法错误 +## 0.0.3(2021-04-01) +- 新增前台更新弹框,详见readme +- 更新前台检查更新方法 + +## 0.0.2(2021-03-29) +- 更新文档 +- 移除 dependencies + +## 0.0.1(2021-03-25) +- 升级中心前台检查更新 diff --git a/uni_modules/uni-upgrade-center-app/package.json b/uni_modules/uni-upgrade-center-app/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f2750d5c4fb439ad0dbd3a8e48ee3a17f39ced36 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/package.json @@ -0,0 +1,81 @@ +{ + "id": "uni-upgrade-center-app", + "displayName": "升级中心 uni-upgrade-center - App", + "version": "0.6.5", + "description": "uni升级中心 - 客户端检查更新", + "keywords": [ + "uniCloud", + "update", + "升级", + "wgt" +], + "repository": "https://gitee.com/dcloud/uni-upgrade-center/tree/master/uni_modules/uni-upgrade-center-app", + "engines": { + "HBuilderX": "^3.2.14" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "插件不采集任何数据", + "permissions": "无" + }, + "npmurl": "", + "type": "unicloud-template-page" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-upgrade-center-app/pages/upgrade-popup-uts.uvue b/uni_modules/uni-upgrade-center-app/pages/upgrade-popup-uts.uvue new file mode 100644 index 0000000000000000000000000000000000000000..f17da29291d0f2b0d7df616e6ce8ef7104f943a3 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/pages/upgrade-popup-uts.uvue @@ -0,0 +1,437 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-upgrade-center-app/pages/upgrade-popup.vue b/uni_modules/uni-upgrade-center-app/pages/upgrade-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..ab7866025f38998955ea2c8e875f2d9d710dc4c0 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/pages/upgrade-popup.vue @@ -0,0 +1,554 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-upgrade-center-app/readme.md b/uni_modules/uni-upgrade-center-app/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..3e830b5acf2e6744239e1fba529e50265533683f --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/readme.md @@ -0,0 +1,126 @@ +## 升级中心 - app插件与 `uni-admin` 版本关系 + +### `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 diff --git a/uni_modules/uni-upgrade-center-app/static/app_update_close.png b/uni_modules/uni-upgrade-center-app/static/app_update_close.png new file mode 100644 index 0000000000000000000000000000000000000000..8b2ffe62cba2466f184ea9f8ee4f9395ed8cf37a Binary files /dev/null and b/uni_modules/uni-upgrade-center-app/static/app_update_close.png differ diff --git a/uni_modules/uni-upgrade-center-app/static/bg_top.png b/uni_modules/uni-upgrade-center-app/static/bg_top.png new file mode 100644 index 0000000000000000000000000000000000000000..015f698cdd8c8caa63486a2bd87f5a96f17df630 Binary files /dev/null and b/uni_modules/uni-upgrade-center-app/static/bg_top.png differ diff --git a/uni_modules/uni-upgrade-center-app/uniCloud/database/db_init.json b/uni_modules/uni-upgrade-center-app/uniCloud/database/db_init.json new file mode 100644 index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/uniCloud/database/db_init.json @@ -0,0 +1 @@ +{} diff --git a/uni_modules/uni-upgrade-center-app/utils/call-check-version.ts b/uni_modules/uni-upgrade-center-app/utils/call-check-version.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce741bb077b67106994e96bc44d10bef472cb018 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/utils/call-check-version.ts @@ -0,0 +1,109 @@ +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 // 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 // 是否静默更新 + 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 { + // #ifdef APP + return new Promise((resolve, reject) => { + const systemInfo = uni.getSystemInfoSync() + const appId = systemInfo.appId + const appVersion = systemInfo.appVersion + // #ifndef UNI-APP-X + if (plus.runtime.appid && plus.runtime.version) { + plus.runtime.getProperty(plus.runtime.appid, function (widgetInfo) { + if (widgetInfo.version) { + let data = { + action: 'checkVersion', + appid: plus.runtime.appid, + appVersion: plus.runtime.version, + wgtVersion: widgetInfo.version + } + uniCloud.callFunction({ + name: 'uni-upgrade-center', + data: 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, + wgtVersion: '0.0.0.0.0.1' + } + try { + uniCloud.callFunction({ + name: 'uni-upgrade-center', + data: data + }).then(res => { + const result = JSON.parse(JSON.stringify(res.result)) + if (result === null ){ + reject({ + code: res.result['code'], + message: res.result['message'] + }) + } else { + resolve(result) + } + }).catch((err : any | null) => { + console.log('err: ', err); + const error = err as UniCloudError + reject(error.errMsg) + }) + } catch (e) { + reject(e.message) + } + } else { + reject('invalid appid or appVersion') + } + // #endif + }) + // #endif + // #ifndef APP-PLUS + /* return new Promise((resolve, reject) => { + reject({ + message: '请在App中使用' + }) + }) */ + // #endif +} diff --git a/uni_modules/uni-upgrade-center-app/utils/check-update.ts b/uni_modules/uni-upgrade-center-app/utils/check-update.ts new file mode 100644 index 0000000000000000000000000000000000000000..16fdc9dd58b48e339e88174ac9ce03c1a5fdbefa --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/utils/check-update.ts @@ -0,0 +1,206 @@ +import callCheckVersion, { UniUpgradeCenterResult } from "./call-check-version" +// #ifdef UNI-APP-X +import { ComponentPublicInstance } from 'vue' +// #endif + +// 推荐再App.vue中使用 +const PACKAGE_INFO_KEY = '__package_info__' +// #ifdef UNI-APP-X +export default function (component : ComponentPublicInstance | null = null) : Promise { +// #endif +// #ifndef UNI-APP-X +export default function () : Promise { +// #endif + // #ifdef APP-PLUS + return new Promise((resolve, reject) => { + callCheckVersion().then(async (uniUpgradeCenterResult) => { + // NOTE uni-app x 3.96 解构有问题 + const code = uniUpgradeCenterResult.code + const message = uniUpgradeCenterResult.message + const url = uniUpgradeCenterResult.url // 安装包下载地址 + // const is_silently = uniUpgradeCenterResult.is_silently // 是否静默更新 + // const platform = uniUpgradeCenterResult.platform // 安装包平台 + // const type = uniUpgradeCenterResult.type // 安装包类型 + + // 此处逻辑仅为实例,可自行编写 + 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) + + /** + * 提示升级二 + * 官方适配的升级弹窗,可自行替换资源适配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) { + // TODO 云函数报错处理 + console.error(message) + return reject(uniUpgradeCenterResult) + } + return resolve(uniUpgradeCenterResult) + }).catch((err) => { + // TODO 云函数报错处理 + console.error(err) + reject(err) + }) + }); + // #endif +} + +// #ifdef UNI-APP-X +/** + * 使用 uni.showModal 升级 + */ +function updateUseModal(packageInfo : UniUpgradeCenterResult) : void { + const { + title, // 标题 + contents, // 升级内容 + is_mandatory, // 是否强制更新 + url, // 安装包下载地址 + } = packageInfo; + + let confirmText = '立即下载更新' + + return uni.showModal({ + title, + content: contents, + showCancel: !is_mandatory, + confirmText, + success: res => { + if (res.cancel) return; + + uni.showToast({ + title: '后台下载中……', + duration: 1000 + }); + + // wgt 和 安卓下载更新 + uni.downloadFile({ + url, + success: res => { + if (res.statusCode !== 200) { + console.error('下载安装包失败'); + return; + } + // 下载好直接安装,下次启动生效 + uni.installApk({ + filePath: res.tempFilePath, + success: () => { + uni.showModal({ + title: '安装成功请手动重启' + }); + }, + fail: err => { + uni.showModal({ + title: '更新失败', + content: err + .message, + showCancel: false + }); + } + }); + } + }); + } + }); +} +// #endif + +// #ifndef UNI-APP-X +/** + * 使用 uni.showModal 升级 + */ +function updateUseModal(packageInfo : UniUpgradeCenterResult) : void { + const { + title, // 标题 + contents, // 升级内容 + is_mandatory, // 是否强制更新 + url, // 安装包下载地址 + platform, // 安装包平台 + type // 安装包类型 + } = packageInfo; + + let isWGT = type === 'wgt' + let isiOS = !isWGT ? platform.includes('iOS') : false; + let confirmText = isiOS ? '立即跳转更新' : '立即下载更新' + + return uni.showModal({ + title, + content: contents, + showCancel: !is_mandatory, + confirmText, + success: res => { + if (res.cancel) return; + + // 安装包下载 + if (isiOS) { + plus.runtime.openURL(url); + return; + } + + uni.showToast({ + title: '后台下载中……', + duration: 1000 + }); + + // wgt 和 安卓下载更新 + uni.downloadFile({ + url, + success: res => { + if (res.statusCode !== 200) { + console.error('下载安装包失败'); + return; + } + // 下载好直接安装,下次启动生效 + 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 \ No newline at end of file