提交 9d291587 编写于 作者: 辛宝Otto's avatar 辛宝Otto 🥊

Merge branch 'dev' into alpha

<script lang="uts">
import { state, setLifeCycleNum } from '@/store/index.uts'
// #ifdef APP-ANDROID
let firstBackTime = 0
// #endif
export default {
globalData: {
str: 'default globalData str',
num: 0,
bool: false,
obj: {
str: 'default globalData obj str',
num: 0,
bool: false,
},
null: null as string | null,
arr: [] as number[],
mySet: new Set<string>(),
myMap: new Map<string, any>(),
func: () : string => {
return 'globalData func'
},
launchOptions: {
path: '',
} as OnLaunchOptions,
onShowOption: {
path: ''
} as OnShowOptions
},
onLaunch: function (res : OnLaunchOptions) {
this.globalData.launchOptions = res
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1000)
console.log('App Launch')
// 页面性能分析
// const performance = uni.getPerformance()
// const observer1: PerformanceObserver = performance.createObserver(
// (entryList: PerformanceObserverEntryList) => {
// console.log('observer1:entryList.getEntries()' +JSON.stringify(entryList.getEntries()))
// }
// )
// observer1.observe({
// entryTypes: ['render', 'navigation'],
// } as PerformanceObserverOptions)
},
onShow: function (res : OnShowOptions) {
this.globalData.onShowOption = res
<script lang="uts">
import { state, setLifeCycleNum } from '@/store/index.uts'
// 处理scheme或通用链接直达
let url = this.getRedirectUrl(res.appScheme, res.appLink);
if (null != url) {
uni.navigateTo({
url: url
})
}
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 100)
console.log('App Show')
},
onHide: function () {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 100)
console.log('App Hide')
},
// #ifdef APP-ANDROID
onLastPageBackPress: function () {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1000)
console.log('App LastPageBackPress')
if (firstBackTime == 0) {
uni.showToast({
title: '再按一次退出应用',
position: 'bottom',
})
firstBackTime = Date.now()
setTimeout(() => {
firstBackTime = 0
}, 2000)
} else if (Date.now() - firstBackTime < 2000) {
firstBackTime = Date.now()
uni.exit()
}
},
onExit() {
console.log('App Exit')
},
// #endif
methods: {
increasetLifeCycleNum() {
setLifeCycleNum(state.lifeCycleNum + 100)
console.log('App increasetLifeCycleNum')
},
getRedirectUrl(scheme : string | null, ulink : string | null) : string | null {
//解析scheme或universal link启动直达页面:
//scheme格式:uniappx://redirect/pages/component/view/view?key=value //其中redirect后为页面路径
//universal link格式:https://uniappx.dcloud.net.cn/ulink/redirect.html?url=%2Fpages%2Fcomponent%2Fview%2Fview%3Fkey%3Dvalue //通用链接路径需固定,?后面的url参数为直达页面路径,注意url字段值需做url编码(可使用encodeURIComponent方法)
let url : string | null = null;
if (null != scheme && scheme.length > 0) {
const PATHPRE = 'redirect';
let parts : string | null = null;
let pos = scheme.search('//');
if (pos > 0) {
parts = scheme.substring(pos + 2);
}
if (null != parts && parts.startsWith(PATHPRE)) {
url = parts.substring(PATHPRE.length);
}
} else if (null != ulink && ulink.length > 0) {
const PATH = 'ulink/redirect.html';
let parts = ulink.split('?');
if (parts.length > 1 && parts[0].endsWith(PATH) && parts[1].length > 0) {
parts[1].split('&').forEach((e) => {
let params = e.split('=');
if (params.length > 1 && params[0].length > 0 && params[1].length > 0) {
if ('url' == params[0]) {
if (null == url) {
url = decodeURIComponent(params[1]);
}
}
}
});
}
}
return url;
}
}
}
</script>
<style>
/*每个页面公共css */
@import "./common/uni.css";
/* #ifdef WEB */
.uni-top-window uni-tabbar .uni-tabbar {
background-color: #fff !important;
}
.uni-app--showleftwindow .uni-page-head-btn {
display: none !important;
}
/* #endif */
// #ifdef APP-ANDROID
let firstBackTime = 0
// #endif
export default {
globalData: {
str: 'default globalData str',
num: 0,
bool: false,
obj: {
str: 'default globalData obj str',
num: 0,
bool: false,
},
null: null as string | null,
arr: [] as number[],
mySet: new Set<string>(),
myMap: new Map<string, any>(),
func: () : string => {
return 'globalData func'
},
launchOptions: {
path: '',
} as OnLaunchOptions,
onShowOption: {
path: ''
} as OnShowOptions
},
onLaunch: function (res : OnLaunchOptions) {
this.globalData.launchOptions = res
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1000)
console.log('App Launch')
// 页面性能分析
// const performance = uni.getPerformance()
// const observer1: PerformanceObserver = performance.createObserver(
// (entryList: PerformanceObserverEntryList) => {
// console.log('observer1:entryList.getEntries()' +JSON.stringify(entryList.getEntries()))
// }
// )
// observer1.observe({
// entryTypes: ['render', 'navigation'],
// } as PerformanceObserverOptions)
},
onShow: function (res : OnShowOptions) {
this.globalData.onShowOption = res
// 处理scheme或通用链接直达
let url = this.getRedirectUrl(res.appScheme, res.appLink);
if (null != url) {
uni.navigateTo({
url: url
})
}
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 100)
console.log('App Show')
},
onHide: function () {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 100)
console.log('App Hide')
},
// #ifdef APP-ANDROID
onLastPageBackPress: function () {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1000)
console.log('App LastPageBackPress')
if (firstBackTime == 0) {
uni.showToast({
title: '再按一次退出应用',
position: 'bottom',
})
firstBackTime = Date.now()
setTimeout(() => {
firstBackTime = 0
}, 2000)
} else if (Date.now() - firstBackTime < 2000) {
firstBackTime = Date.now()
uni.exit()
}
},
onExit() {
console.log('App Exit')
},
// #endif
methods: {
increasetLifeCycleNum() {
setLifeCycleNum(state.lifeCycleNum + 100)
console.log('App increasetLifeCycleNum')
},
getRedirectUrl(scheme : string | null, ulink : string | null) : string | null {
//解析scheme或universal link启动直达页面:
//scheme格式:uniappx://redirect/pages/component/view/view?key=value //其中redirect后为页面路径
//universal link格式:https://uniappx.dcloud.net.cn/ulink/redirect.html?url=%2Fpages%2Fcomponent%2Fview%2Fview%3Fkey%3Dvalue //通用链接路径需固定,?后面的url参数为直达页面路径,注意url字段值需做url编码(可使用encodeURIComponent方法)
let url : string | null = null;
if (null != scheme && scheme.length > 0) {
const PATHPRE = 'redirect';
let parts : string | null = null;
let pos = scheme.search('//');
if (pos > 0) {
parts = scheme.substring(pos + 2);
}
if (null != parts && parts.startsWith(PATHPRE)) {
url = parts.substring(PATHPRE.length);
}
} else if (null != ulink && ulink.length > 0) {
const PATH = 'ulink/redirect.html';
let parts = ulink.split('?');
if (parts.length > 1 && parts[0].endsWith(PATH) && parts[1].length > 0) {
parts[1].split('&').forEach((e) => {
let params = e.split('=');
if (params.length > 1 && params[0].length > 0 && params[1].length > 0) {
if ('url' == params[0]) {
if (null == url) {
url = decodeURIComponent(params[1]);
}
}
}
});
}
}
return url;
}
}
}
</script>
<style>
/*每个页面公共css */
@import "./common/uni.css";
/* #ifdef WEB */
.uni-top-window uni-tabbar .uni-tabbar {
background-color: #fff !important;
}
.uni-app--showleftwindow .uni-page-head-btn {
display: none !important;
}
/* #endif */
</style>
......@@ -543,6 +543,25 @@
"navigationBarTitleText": "getLaunchOptionsSync | 获取启动参数"
}
},
{
"path": "pages/API/get-enter-options-sync/get-enter-options-sync",
"group": "1,1,4,1",
"style": {
"navigationBarTitleText": "getEnterOptionsSync | 获取本次启动时的参数"
}
},
{
"path": "pages/API/requestAnimationFrame/requestAnimationFrame",
"style": {
"navigationBarTitleText": "requestAnimationFrame | 请求帧动画队列"
}
},
{
"path": "pages/API/cancelAnimationFrame/cancelAnimationFrame",
"style": {
"navigationBarTitleText": "cancelAnimationFrame | 取消已加入的帧动画队列"
}
},
{
"path": "pages/API/navigator/navigator",
"group": "1,2,0",
......@@ -1787,7 +1806,7 @@
"pagePath": "pages/tabBar/component",
"iconPath": "@tabBarComponentIconPath",
"selectedIconPath": "@tabBarComponentSelectedIconPath",
"text": "内置组件"
"text": "组件"
},
{
"pagePath": "pages/tabBar/API",
......@@ -2067,6 +2086,10 @@
{
"id": "api.base.launch.getLaunchOptionsSync",
"name": "getLaunchOptionsSync"
},
{
"id": "api.base.launch.getEnterOptionsSync",
"name": "getEnterOptionsSync"
}
]
},
......@@ -2736,4 +2759,4 @@
]
}
]
}
\ No newline at end of file
}
const PAGE_PATH = '/pages/API/cancelAnimationFrame/cancelAnimationFrame'
describe('API-cancelAnimationFrame', () => {
let page
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor(600);
});
it('cancelAnimationFrame', async () => {
await page.callMethod('startRequestAnimationFrame')
await page.waitFor(100)
const data1 = await page.data()
expect(data1.testFrameCount > 0).toBe(true)
await page.callMethod('stopRequestAnimationFrame')
await page.waitFor(100)
const data2 = await page.data()
const testFrameCount = data2.testFrameCount
await page.waitFor(100)
const data3 = await page.data()
expect(data3.testFrameCount).toBe(testFrameCount)
});
});
<template>
<view class="page">
<page-head :title="title"></page-head>
<button @click="startRequestAnimationFrame">requestAnimationFrame</button>
<button @click="stopRequestAnimationFrame">cancelAnimationFrame</button>
<text class="frame-count">{{testFrameCount}}</text>
</view>
</template>
<script>
export default {
data() {
return {
title: 'cancelAnimationFrame',
taskId: 0,
testFrameCount: 0
}
},
onUnload() {
if (this.taskId > 0) {
this.stopRequestAnimationFrame()
}
},
methods: {
startRequestAnimationFrame() {
this.taskId = uni.requestAnimationFrame((_ : number) => {
this.testFrameCount++
this.startRequestAnimationFrame()
})
},
stopRequestAnimationFrame() {
uni.cancelAnimationFrame(this.taskId)
}
}
}
</script>
<style>
.page {
padding: 15px;
}
.frame-count {
margin-top: 15px;
}
</style>
<template>
<view class="page">
<page-head :title="title"></page-head>
<canvas class="canvas-element" canvas-id="canvas" id="canvas"></canvas>
<scroll-view class="scroll-view">
<view class="grid-view">
<view class="grid-item" v-for="(name, index) in names" :key="index">
<button class="canvas-drawing-button" @click="handleCanvasButton(name)">{{name}}</button>
</view>
</view>
<button class="btn-to-image" @click="toTempFilePath" type="primary">toTempFilePath</button>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
title: 'createContext',
names: ["rotate", "scale", "reset", "translate", "save", "restore", "drawImage", "fillText", "fill",
"stroke", "clearRect", "beginPath", "closePath", "moveTo", "lineTo", "rect", "arc",
"quadraticCurveTo", "bezierCurveTo", "setFillStyle", "setStrokeStyle", "setGlobalAlpha",
"setShadow", "setFontSize", "setLineCap", "setLineJoin", "setLineWidth", "setMiterLimit"
],
// TODO 缺失 CanvasContext
canvasContext: null as any | null
}
},
onReady() {
// @ts-ignore
this.canvasContext = uni.createCanvasContext('canvas', this)
},
methods: {
toTempFilePath() {
// TODO 缺失
// uni.canvasToTempFilePath({
// canvasId: 'canvas',
// success: (res) => {
// console.log(res.tempFilePath)
// },
// fail: (err) => {
// console.error(JSON.stringify(err))
// }
// })
},
handleCanvasButton(name : string) {
switch (name) {
case "rotate":
this.rotate();
break;
case "scale":
this.scale();
break;
case "reset":
this.reset();
break;
case "translate":
this.translate();
break;
case "save":
this.save();
break;
case "restore":
this.restore();
break;
case "drawImage":
this.drawImage();
break;
case "fillText":
this.fillText();
break;
case "fill":
this.fill();
break;
case "stroke":
this.stroke();
break;
case "clearRect":
this.clearRect();
break;
case "beginPath":
this.beginPath();
break;
case "closePath":
this.closePath();
break;
case "moveTo":
this.moveTo();
break;
case "lineTo":
this.lineTo();
break;
case "rect":
this.rect();
break;
case "arc":
this.arc();
break;
case "quadraticCurveTo":
this.quadraticCurveTo();
break;
case "bezierCurveTo":
this.bezierCurveTo();
break;
case "setFillStyle":
this.setFillStyle();
break;
case "setStrokeStyle":
this.setStrokeStyle();
break;
case "setGlobalAlpha":
this.setGlobalAlpha();
break;
case "setShadow":
this.setShadow();
break;
case "setFontSize":
this.setFontSize();
break;
case "setLineCap":
this.setLineCap();
break;
case "setLineJoin":
this.setLineJoin();
break;
case "setLineWidth":
this.setLineWidth();
break;
case "setMiterLimit":
this.setMiterLimit();
break;
default:
break;
}
},
rotate() {
this.canvasContext!.beginPath()
this.canvasContext!.rotate(10 * Math.PI / 180)
this.canvasContext!.rect(225, 75, 20, 10)
this.canvasContext!.fill()
this.canvasContext!.draw()
},
scale() {
this.canvasContext!.beginPath()
this.canvasContext!.rect(25, 25, 50, 50)
this.canvasContext!.stroke()
this.canvasContext!.scale(2, 2)
this.canvasContext!.beginPath()
this.canvasContext!.rect(25, 25, 50, 50)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
reset() {
this.canvasContext!.beginPath()
this.canvasContext!.setFillStyle('#000000')
this.canvasContext!.setStrokeStyle('#000000')
this.canvasContext!.setFontSize(10)
this.canvasContext!.setGlobalAlpha(1)
this.canvasContext!.setShadow(0, 0, 0, 'rgba(0, 0, 0, 0)')
this.canvasContext!.setLineCap('butt')
this.canvasContext!.setLineJoin('miter')
this.canvasContext!.setLineWidth(1)
this.canvasContext!.setMiterLimit(10)
this.canvasContext!.draw()
},
translate() {
this.canvasContext!.beginPath()
this.canvasContext!.rect(10, 10, 100, 50)
this.canvasContext!.fill()
this.canvasContext!.translate(70, 70)
this.canvasContext!.beginPath()
this.canvasContext!.fill()
this.canvasContext!.draw()
},
save() {
this.canvasContext!.beginPath()
this.canvasContext!.setStrokeStyle('#00ff00')
this.canvasContext!.save()
this.canvasContext!.scale(2, 2)
this.canvasContext!.setStrokeStyle('#ff0000')
this.canvasContext!.rect(0, 0, 100, 100)
this.canvasContext!.stroke()
this.canvasContext!.restore()
this.canvasContext!.rect(0, 0, 50, 50)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
restore() {
[3, 2, 1].forEach((item) => {
this.canvasContext!.beginPath()
this.canvasContext!.save()
this.canvasContext!.scale(item, item)
this.canvasContext!.rect(10, 10, 100, 100)
this.canvasContext!.stroke()
this.canvasContext!.restore()
});
this.canvasContext!.draw()
},
drawImage() {
// #ifdef APP-PLUS
this.canvasContext!.drawImage('../../../static/app-plus/uni@2x.png', 0, 0)
// #endif
// #ifndef APP-PLUS
this.canvasContext!.drawImage('../../../static/uni.png', 0, 0)
// #endif
this.canvasContext!.draw()
},
fillText() {
this.canvasContext!.setStrokeStyle('#ff0000')
this.canvasContext!.beginPath()
this.canvasContext!.moveTo(0, 10)
this.canvasContext!.lineTo(300, 10)
this.canvasContext!.stroke()
// this.canvasContext!.save()
// this.canvasContext!.scale(1.5, 1.5)
// this.canvasContext!.translate(20, 20)
this.canvasContext!.setFontSize(10)
this.canvasContext!.fillText('Hello World', 0, 30)
this.canvasContext!.setFontSize(20)
this.canvasContext!.fillText('Hello World', 100, 30)
// this.canvasContext!.restore()
this.canvasContext!.beginPath()
this.canvasContext!.moveTo(0, 30)
this.canvasContext!.lineTo(300, 30)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
fill() {
this.canvasContext!.beginPath()
this.canvasContext!.rect(20, 20, 150, 100)
this.canvasContext!.setStrokeStyle('#00ff00')
this.canvasContext!.fill()
this.canvasContext!.draw()
},
stroke() {
this.canvasContext!.beginPath()
this.canvasContext!.moveTo(20, 20)
this.canvasContext!.lineTo(20, 100)
this.canvasContext!.lineTo(70, 100)
this.canvasContext!.setStrokeStyle('#00ff00')
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
clearRect() {
this.canvasContext!.setFillStyle('#ff0000')
this.canvasContext!.beginPath()
this.canvasContext!.rect(0, 0, 300, 150)
this.canvasContext!.fill()
this.canvasContext!.clearRect(20, 20, 100, 50)
this.canvasContext!.draw()
},
beginPath() {
this.canvasContext!.beginPath()
this.canvasContext!.setLineWidth(5)
this.canvasContext!.setStrokeStyle('#ff0000')
this.canvasContext!.moveTo(0, 75)
this.canvasContext!.lineTo(250, 75)
this.canvasContext!.stroke()
this.canvasContext!.beginPath()
this.canvasContext!.setStrokeStyle('#0000ff')
this.canvasContext!.moveTo(50, 0)
this.canvasContext!.lineTo(150, 130)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
closePath() {
this.canvasContext!.beginPath()
this.canvasContext!.setLineWidth(1)
this.canvasContext!.moveTo(20, 20)
this.canvasContext!.lineTo(20, 100)
this.canvasContext!.lineTo(70, 100)
this.canvasContext!.closePath()
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
moveTo() {
this.canvasContext!.beginPath()
this.canvasContext!.moveTo(0, 0)
this.canvasContext!.lineTo(300, 150)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
lineTo() {
this.canvasContext!.beginPath()
this.canvasContext!.moveTo(20, 20)
this.canvasContext!.lineTo(20, 100)
this.canvasContext!.lineTo(70, 100)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
rect() {
this.canvasContext!.beginPath()
this.canvasContext!.rect(20, 20, 150, 100)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
arc() {
this.canvasContext!.beginPath()
this.canvasContext!.setLineWidth(2)
this.canvasContext!.arc(75, 75, 50, 0, Math.PI * 2, true)
this.canvasContext!.moveTo(110, 75)
this.canvasContext!.arc(75, 75, 35, 0, Math.PI, false)
this.canvasContext!.moveTo(65, 65)
this.canvasContext!.arc(60, 65, 5, 0, Math.PI * 2, true)
this.canvasContext!.moveTo(95, 65)
this.canvasContext!.arc(90, 65, 5, 0, Math.PI * 2, true)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
quadraticCurveTo() {
this.canvasContext!.beginPath()
this.canvasContext!.moveTo(20, 20)
this.canvasContext!.quadraticCurveTo(20, 100, 200, 20)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
bezierCurveTo() {
this.canvasContext!.beginPath()
this.canvasContext!.moveTo(20, 20)
this.canvasContext!.bezierCurveTo(20, 100, 200, 100, 200, 20)
this.canvasContext!.stroke()
this.canvasContext!.draw()
},
setFillStyle() {
['#fef957', 'rgb(242,159,63)', 'rgb(242,117,63)', '#e87e51'].forEach((item : string, index : number) => {
this.canvasContext!.setFillStyle(item)
this.canvasContext!.beginPath()
this.canvasContext!.rect(0 + 75 * index, 0, 50, 50)
this.canvasContext!.fill()
})
this.canvasContext!.draw()
},
setStrokeStyle() {
['#fef957', 'rgb(242,159,63)', 'rgb(242,117,63)', '#e87e51'].forEach((item : string, index : number) => {
this.canvasContext!.setStrokeStyle(item)
this.canvasContext!.beginPath()
this.canvasContext!.rect(0 + 75 * index, 0, 50, 50)
this.canvasContext!.stroke()
})
this.canvasContext!.draw()
},
setGlobalAlpha() {
this.canvasContext!.setFillStyle('#000000');
[1, 0.5, 0.1].forEach((item : number, index : number) => {
this.canvasContext!.setGlobalAlpha(item)
this.canvasContext!.beginPath()
this.canvasContext!.rect(0 + 75 * index, 0, 50, 50)
this.canvasContext!.fill()
})
this.canvasContext!.draw()
this.canvasContext!.setGlobalAlpha(1)
},
setShadow() {
this.canvasContext!.beginPath()
this.canvasContext!.setShadow(10, 10, 10, 'rgba(0, 0, 0, 199)')
this.canvasContext!.rect(10, 10, 100, 100)
this.canvasContext!.fill()
this.canvasContext!.draw()
},
setFontSize() {
[10, 20, 30, 40].forEach((item : number, index : number) => {
this.canvasContext!.setFontSize(item)
this.canvasContext!.fillText('Hello, world', 20, 20 + 40 * index)
})
this.canvasContext!.draw()
},
setLineCap() {
this.canvasContext!.setLineWidth(10);
['butt', 'round', 'square'].forEach((item : string, index : number) => {
this.canvasContext!.beginPath()
this.canvasContext!.setLineCap(item)
this.canvasContext!.moveTo(20, 20 + 20 * index)
this.canvasContext!.lineTo(100, 20 + 20 * index)
this.canvasContext!.stroke()
})
this.canvasContext!.draw()
},
setLineJoin() {
this.canvasContext!.setLineWidth(10);
['bevel', 'round', 'miter'].forEach((item : string, index : number) => {
this.canvasContext!.beginPath()
this.canvasContext!.setLineJoin(item)
this.canvasContext!.moveTo(20 + 80 * index, 20)
this.canvasContext!.lineTo(100 + 80 * index, 50)
this.canvasContext!.lineTo(20 + 80 * index, 100)
this.canvasContext!.stroke()
})
this.canvasContext!.draw()
},
setLineWidth() {
[2, 4, 6, 8, 10].forEach((item : number, index : number) => {
this.canvasContext!.beginPath()
this.canvasContext!.setLineWidth(item)
this.canvasContext!.moveTo(20, 20 + 20 * index)
this.canvasContext!.lineTo(100, 20 + 20 * index)
this.canvasContext!.stroke()
})
this.canvasContext!.draw()
},
setMiterLimit() {
this.canvasContext!.setLineWidth(4);
[2, 4, 6, 8, 10].forEach((item : number, index : number) => {
this.canvasContext!.beginPath()
this.canvasContext!.setMiterLimit(item)
this.canvasContext!.moveTo(20 + 80 * index, 20)
this.canvasContext!.lineTo(100 + 80 * index, 50)
this.canvasContext!.lineTo(20 + 80 * index, 100)
this.canvasContext!.stroke()
})
this.canvasContext!.draw()
}
}
}
</script>
<style>
.page {
flex: 1;
height: 100%;
overflow: hidden;
}
.scroll-view {
flex: 1;
padding-bottom: 50px;
}
.canvas-element {
width: 100%;
height: 250px;
background-color: #ffffff;
}
.grid-view {
padding: 10px;
flex-direction: row;
flex-wrap: wrap;
}
.grid-item {
width: 50%;
padding: 5px;
}
.btn-to-image {
margin: 10px;
}
</style>
<template>
<view>
<canvas class="child-canvas" id="canvas" style="height: 100px;border: 1px solid red;"></canvas> <view>isCanvasContextNull: {{isCanvasContextNull}}</view>
</view>
</template>
<script>
type getContext = {
ctx : boolean,
hasFillRect : boolean
}
export default {
data() {
return { isCanvasContextNull : false
}
},
mounted() {
this.$nextTick(() => {
uni.createCanvasContextAsync({
id: 'canvas',
component: this,
success: (res : CanvasContext) => {
const context = res.getContext('2d')!; this.isCanvasContextNull = true
// 若干绘制调用
// 绘制红色正方形
context.fillStyle = "rgb(200, 0, 0)";
context.fillRect(10, 10, 50, 50);
// 绘制蓝色半透明正方形
context.fillStyle = "rgba(0, 0, 200, 0.5)";
context.fillRect(30, 30, 50, 50);
context.draw();
}
})
})
}
}
</script>
\ No newline at end of file
<template>
<view>
<canvas class="child-canvas" id="canvas" style="height: 100px;border: 1px solid red;"></canvas>
<view>isCanvasContextNull: {{isCanvasContextNull}}</view>
</view>
</template>
<script>
type getContext = {
ctx : boolean,
hasFillRect : boolean
}
export default {
data() {
return {
isCanvasContextNull : false
}
},
mounted() {
this.$nextTick(() => {
uni.createCanvasContextAsync({
id: 'canvas',
component: this,
success: (res : CanvasContext) => {
const context = res.getContext('2d')!;
this.isCanvasContextNull = true
// 若干绘制调用
// 绘制红色正方形
context.fillStyle = "rgb(200, 0, 0)";
context.fillRect(10, 10, 50, 50);
// 绘制蓝色半透明正方形
context.fillStyle = "rgba(0, 0, 200, 0.5)";
context.fillRect(30, 30, 50, 50);
context.draw();
}
})
})
}
}
</script>
const PAGE_PATH = '/pages/API/create-canvas-context-async/create-canvas-context-async'
describe('create-canvas-context-async', () => {
let page
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor('view')
})
it('page canvas', async () => {
await page.waitFor(100)
const data = await page.data()
expect(data.isCanvasContextNull).toBe(true)
})
it('component canvas', async () => {
// child-canvas
await page.waitFor(100)
// const element = await page.$('.node-child-component')
const element = await page.$('child-canvas')
const data = await page.data()
expect(data.isCanvasContextNull).toBe(true)
})
})
\ No newline at end of file
const PAGE_PATH = '/pages/API/create-canvas-context-async/create-canvas-context-async'
describe('create-canvas-context-async', () => {
let page
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor('view')
})
it('page canvas', async () => {
await page.waitFor(100)
const data = await page.data()
expect(data.testCreateCanvasContextAsyncSuccess).toBe(true)
})
it('component canvas', async () => {
// child-canvas
await page.waitFor(100)
// const element = await page.$('.node-child-component')
const element = await page.$('child-canvas')
const data = await page.data()
expect(data.isCanvasContextNull).toBe(true)
})
})
......@@ -66,6 +66,12 @@ describe('nodes-info', () => {
expect(pageData.selectAllCount).toBe(2)
})
it('multi-child', async () => {
const pageData = await page.data()
expect(pageData.selectCount).toBe(1)
expect(pageData.selectAllCount).toBe(2)
})
// #ifdef APP
//检测onResize获取BoundingClientRect信息是否有效
/* it('check_resizeRectValid', async () => {
......@@ -74,6 +80,23 @@ describe('nodes-info', () => {
}) */
// #endif
it('test filelds', async () => {
if (process.env.uniTestPlatformInfo.startsWith('web')) {
expect(true).toBe(true)
} else {
const pageData = await page.data()
expect(pageData.fieldsResultContainNode).toBe(true)
}
})
it('test node', async () => {
if (process.env.uniTestPlatformInfo.startsWith('web')) {
expect(true).toBe(true)
} else {
const pageData = await page.data()
expect(pageData.nodeResultContainNode).toBe(true)
}
})
})
async function getRootNode(selector) {
......@@ -89,4 +112,4 @@ async function getRootNode(selector) {
const data = await page.data()
expect(data.rootNodeInfo != null).toBe(true)
}
}
<template>
<!-- #ifdef APP -->
<scroll-view class="page-scroll-view">
<!-- #endif -->
<view class="page" id="page">
<page-head :title="title"></page-head>
<button class="btn btn-get-node-info" @click="getNodeInfo">getNodeInfo</button>
<button class="btn btn-get-all-node-info" @click="getAllNodeInfo">getAllNodeInfo</button>
<view id="rect-1-2" class="rect-1-2">
<view class="rect rect1"></view>
<view class="rect rect2"></view>
</view>
<view class="rect-info-1-2">
<view class="rect-info" v-for="(nodeInfo, index) in nodeInfoList" :key="index">
<view class="node-info-item">
<text class="node-info-item-k">left: </text>
<text class="node-info-item-v">{{nodeInfo.left}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">top: </text>
<text class="node-info-item-v">{{nodeInfo.top}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">right: </text>
<text class="node-info-item-v">{{nodeInfo.right}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">bottom: </text>
<text class="node-info-item-v">{{nodeInfo.bottom}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">width: </text>
<text class="node-info-item-v">{{nodeInfo.width}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">height: </text>
<text class="node-info-item-v">{{nodeInfo.height}}</text>
</view>
</view>
<!-- #endif -->
<view class="page" id="page">
<page-head :title="title"></page-head>
<button class="btn btn-get-node-info" @click="getNodeInfo">getNodeInfo</button>
<button class="btn btn-get-all-node-info" @click="getAllNodeInfo">getAllNodeInfo</button>
<view id="rect-1-2" class="rect-1-2">
<view class="rect rect1"></view>
<view class="rect rect2"></view>
</view>
<view class="rect-info-1-2">
<view class="rect-info" v-for="(nodeInfo, index) in nodeInfoList" :key="index">
<view class="node-info-item">
<text class="node-info-item-k">left: </text>
<text class="node-info-item-v">{{nodeInfo.left}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">top: </text>
<text class="node-info-item-v">{{nodeInfo.top}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">right: </text>
<text class="node-info-item-v">{{nodeInfo.right}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">bottom: </text>
<text class="node-info-item-v">{{nodeInfo.bottom}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">width: </text>
<text class="node-info-item-v">{{nodeInfo.width}}</text>
</view>
<view class="node-info-item">
<text class="node-info-item-k">height: </text>
<text class="node-info-item-v">{{nodeInfo.height}}</text>
</view>
</view>
</view>
<node-child class="node-child"></node-child>
<text>子组件多根节点</text>
<multi-child ref="multi-child" id="multi-child"></multi-child>
<text>子组件多根节点(仅测试,用于验证查询是否超出范围)</text>
<multi-child id="multi-child-2"></multi-child>
<multi-child id="multi-child-2"></multi-child>
<view>
<text>测试.fields</text>
<text>{{fieldsResultContainNode}}</text>
</view>
<view>
<text>测试.node</text>
<text>{{nodeResultContainNode}}</text>
</view>
<canvas id="canvas1"></canvas>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script>
<!-- #endif -->
</template>
<script>
import nodeChild from './nodes-info-child.uvue'
import multiChild from './selector-query-child-multi.uvue'
type NodeInfoType = {
left : number | null,
top : number | null,
right : number | null,
bottom : number | null,
width : number | null,
height : number | null,
}
export default {
components: {
import multiChild from './selector-query-child-multi.uvue'
type NodeInfoType = {
left : number | null,
top : number | null,
right : number | null,
bottom : number | null,
width : number | null,
height : number | null,
}
export default {
components: {
nodeChild,
multiChild
},
data() {
return {
title: 'createSelectorQuery',
nodeInfoList: [] as NodeInfoType[],
// 仅用于自动化测试
rootNodeInfo: null as NodeInfoType | null,
//供自动化测试使用
multiChild
},
data() {
return {
title: 'createSelectorQuery',
nodeInfoList: [] as NodeInfoType[],
// 仅用于自动化测试
rootNodeInfo: null as NodeInfoType | null,
//供自动化测试使用
// resizeRectValid: false
// TODO
selectCount: 0,
selectAllCount: 0,
}
selectAllCount: 0,
fieldsResultContainNode: false,
nodeResultContainNode: false
}
},
onReady() {
const instance2 = (this.$refs['multi-child'] as ComponentPublicInstance)
this.selectCount = instance2.$data['selectCount'] as Number
this.selectAllCount = instance2.$data['selectAllCount'] as Number
console.log('selectCount', selectCount, selectAllCount)
},
onResize() {
//供自动化测试使用
/* var rect12Element = uni.getElementById("rect-1-2")
if(rect12Element != null) {
var domRect = rect12Element.getBoundingClientRect()
if(domRect.width > 100) {
this.resizeRectValid = true
}
} */
},
methods: {
// 仅用于自动化测试
getRootNodeInfo(selector : string) {
uni.createSelectorQuery().select(selector).boundingClientRect().exec((ret) => {
if (ret.length == 1) {
const nodeInfo = ret[0] as NodeInfo;
const nodeType = {
left: nodeInfo.left,
top: nodeInfo.top,
right: nodeInfo.right,
bottom: nodeInfo.bottom,
width: nodeInfo.width,
height: nodeInfo.height,
} as NodeInfoType;
this.rootNodeInfo = nodeType
}
})
},
getNodeInfo() {
uni.createSelectorQuery().select('.rect1').boundingClientRect().exec((ret) => {
this.nodeInfoList.length = 0
const i = ret[0] as NodeInfo
this.nodeInfoList.push({
left: i.left,
top: i.top,
right: i.right,
bottom: i.bottom,
width: i.width,
height: i.height,
} as NodeInfoType)
})
},
getAllNodeInfo() {
uni.createSelectorQuery().selectAll('.rect').boundingClientRect().exec((ret) => {
this.nodeInfoList.length = 0
const array = ret[0] as NodeInfo[]
array.forEach((i) => {
this.nodeInfoList.push({
left: i.left,
top: i.top,
right: i.right,
bottom: i.bottom,
width: i.width,
height: i.height,
} as NodeInfoType)
})
})
}
}
}
</script>
<style>
.page {
padding: 15px;
}
.btn {
margin-top: 15px;
}
.rect-1-2 {
flex-direction: row;
margin-top: 15px;
}
.rect {
width: 150px;
height: 100px;
}
.rect1 {
background-color: dodgerblue;
}
.rect2 {
margin-left: auto;
background-color: seagreen;
}
.rect-info-1-2 {
flex-direction: row;
margin-top: 15px;
}
.rect-info {
flex: 1;
flex-direction: column;
}
.node-info-item {
flex-direction: row;
}
.node-info-item-k {
width: 72px;
line-height: 2;
}
.node-info-item-v {
font-weight: bold;
line-height: 2;
}
this.testFields()
this.testNode()
},
onResize() {
//供自动化测试使用
/* var rect12Element = uni.getElementById("rect-1-2")
if(rect12Element != null) {
var domRect = rect12Element.getBoundingClientRect()
if(domRect.width > 100) {
this.resizeRectValid = true
}
} */
},
methods: {
// 仅用于自动化测试
getRootNodeInfo(selector : string) {
uni.createSelectorQuery().select(selector).boundingClientRect().exec((ret) => {
if (ret.length == 1) {
const nodeInfo = ret[0] as NodeInfo;
const nodeType = {
left: nodeInfo.left,
top: nodeInfo.top,
right: nodeInfo.right,
bottom: nodeInfo.bottom,
width: nodeInfo.width,
height: nodeInfo.height,
} as NodeInfoType;
this.rootNodeInfo = nodeType
}
})
},
getNodeInfo() {
uni.createSelectorQuery().select('.rect1').boundingClientRect().exec((ret) => {
this.nodeInfoList.length = 0
const i = ret[0] as NodeInfo
this.nodeInfoList.push({
left: i.left,
top: i.top,
right: i.right,
bottom: i.bottom,
width: i.width,
height: i.height,
} as NodeInfoType)
})
},
getAllNodeInfo() {
uni.createSelectorQuery().selectAll('.rect').boundingClientRect().exec((ret) => {
this.nodeInfoList.length = 0
const array = ret[0] as NodeInfo[]
array.forEach((i) => {
this.nodeInfoList.push({
left: i.left,
top: i.top,
right: i.right,
bottom: i.bottom,
width: i.width,
height: i.height,
} as NodeInfoType)
})
})
},
// test .fields
testFields() {
uni.createSelectorQuery().select('.rect1').fields({
node: true
} as NodeField, (ret) => {
const isElement = (ret as NodeInfo).node instanceof UniElement
if(isElement){
this.fieldsResultContainNode = true
} else {
this.fieldsResultContainNode = false
}
}).exec()
},
// test .node
testNode() {
uni.createSelectorQuery().select('#canvas1').node((ret) => {
const isElement = (ret as NodeInfo).node instanceof UniElement
const isCanvasElement = ((ret as NodeInfo).node as UniCanvasElement).tagName == 'CANVAS'
if(isElement && isCanvasElement){
this.nodeResultContainNode = true
} else {
this.nodeResultContainNode = false
}
}).exec()
},
}
}
</script>
<style>
.page {
padding: 15px;
}
.btn {
margin-top: 15px;
}
.rect-1-2 {
flex-direction: row;
margin-top: 15px;
}
.rect {
width: 150px;
height: 100px;
}
.rect1 {
background-color: dodgerblue;
}
.rect2 {
margin-left: auto;
background-color: seagreen;
}
.rect-info-1-2 {
flex-direction: row;
margin-top: 15px;
}
.rect-info {
flex: 1;
flex-direction: column;
}
.node-info-item {
flex-direction: row;
}
.node-info-item-k {
width: 72px;
line-height: 2;
}
.node-info-item-v {
font-weight: bold;
line-height: 2;
}
</style>
const PAGE_PATH = '/pages/API/get-enter-options-sync/get-enter-options-sync'
describe('getEnterOptionsSync', () => {
it('app onShow 和 getEnterOptionsSync 结果一致', async () => {
const page = await program.navigateTo(PAGE_PATH)
await page.waitFor('view')
const pageData = await page.data()
expect(pageData.testResult).toBe(true)
})
})
<template>
<page-head title="getEnterOptionsSync"></page-head>
<view class="uni-padding-wrap">
<view class="uni-common-mt">
<text>应用本次启动路径:</text>
<text style="margin-top: 5px">{{ enterOptionsString }}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
enterOptionsString: '',
testResult: false }
},
onReady() {
const app = getApp()
const appOnShow = app.globalData.onShowOption
const onShowOption = uni.getEnterOptionsSync()
this.enterOptionsString = JSON.stringify(onShowOption, null, 2)
this.testResult = (onShowOption.path == appOnShow.path && onShowOption.appScheme == appOnShow.appScheme && onShowOption.appLink == appOnShow.appLink)
}
}
</script>
......@@ -9,21 +9,9 @@ describe('getLaunchOptionsSync', () => {
expect(data.checked).toBe(true)
})
it('app onLaunch 和 getLaunchOptionsSync 结果一致', async () => {
const page = await program.navigateTo(PAGE_PATH)
await page.waitFor('view')
const res = await page.callMethod('compareOnLaunchRes')
expect(res.appOnLaunch).toEqual(res.launchOptions)
})
it('app onShow 和 getEnterOptionsSync 结果一致', async () => {
const page = await program.navigateTo(PAGE_PATH)
await page.waitFor('view')
const res = await page.callMethod('compareOnShowRes')
if (process.env.uniTestPlatformInfo.toLowerCase().startsWith('android')) {
// if android return
expect(true).toBe(true)
}
expect(res.appOnShow).toEqual(res.onShowOption)
const pageData = await page.data()
expect(pageData.testResult).toBe(true)
})
})
<template>
<page-head title="getLaunchOptionsSync"></page-head>
<view class="uni-padding-wrap">
<button @click="getLaunchOptionsSync">getLaunchOptionsSync</button>
<view v-if="launchOptionsPath.length > 0" class="uni-common-mt">
<text>应用启动路径:</text>
<text style="margin-top: 5px">{{ launchOptionsPath }}</text>
</view>
</view>
</template>
<script lang="uts">
type OnShowReturn = {
appOnShow : OnShowOptions,
onShowOption : OnShowOptions
}
type IOnLaunchOptions = {
appOnLaunch : OnLaunchOptions,
launchOptions : OnLaunchOptions
}
export default {
data() {
return {
checked: false,
homePagePath: 'pages/tabBar/component',
launchOptionsPath: '',
}
},
methods: {
// 自动化测试
compareOnLaunchRes() : IOnLaunchOptions {
const launchOptions = uni.getLaunchOptionsSync();
const app = getApp()
const appOnLaunch = app.globalData.launchOptions as OnLaunchOptions
const res : IOnLaunchOptions = {
appOnLaunch,
launchOptions
}
return res
},
compareOnShowRes() : OnShowReturn {
// #ifdef APP-ANDROID
const res : OnShowReturn = {
appOnShow: {
path: ''
} as OnShowOptions,
onShowOption: {
path: ''
} as OnShowOptions
}
return res
// #endif
// #ifndef APP-ANDROID
const onShowOption = uni.getEnterOptionsSync();
const app = getApp()
const appOnShow = app.globalData.onShowOption as OnShowOptions
return {
appOnShow,
onShowOption
}
// #endif
},
getLaunchOptionsSync() {
const launchOptions = uni.getLaunchOptionsSync()
this.launchOptionsPath = launchOptions.path
if (launchOptions.path == this.homePagePath) {
this.checked = true
}
},
},
}
<template>
<page-head title="getLaunchOptionsSync"></page-head>
<view class="uni-padding-wrap">
<button @click="getLaunchOptionsSync">getLaunchOptionsSync</button>
<view class="uni-common-mt">
<text>应用本次启动路径:</text>
<text style="margin-top: 5px">{{ launchOptionsPath }}</text>
</view>
<view class="uni-common-mt">
<text>应用本次启动:</text>
<text style="margin-top: 5px">{{ launchOptionsString }}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
checked: false,
homePagePath: 'pages/tabBar/component',
launchOptionsPath: '',
launchOptionsString: '',
testResult: false
}
},
onReady(){
this.compareOnLaunchRes()
},
methods: {
compareOnLaunchRes() {
const launchOptions = uni.getLaunchOptionsSync();
this.launchOptionsString = JSON.stringify(launchOptions, null, 2)
const app = getApp()
const appOnLaunch = app.globalData.launchOptions
const isPathSame = launchOptions.path == appOnLaunch.path
const isAppSchemeSame = launchOptions.appScheme == appOnLaunch.appScheme
const isAppLinkSame = launchOptions.appLink == appOnLaunch.appLink
this.testResult = isPathSame && isAppSchemeSame && isAppLinkSame
},
getLaunchOptionsSync() {
const launchOptions = uni.getLaunchOptionsSync()
this.launchOptionsPath = launchOptions.path
if (launchOptions.path == this.homePagePath) {
this.checked = true
}
},
},
}
</script>
const PAGE_PATH = '/pages/API/requestAnimationFrame/requestAnimationFrame'
describe('API-requestAnimationFrame', () => {
let page
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor(600);
});
it('requestAnimationFrame', async () => {
await page.callMethod('startRequestAnimationFrame')
await page.waitFor(100)
const data = await page.data()
expect(data.testFrameCount > 0).toBe(true)
});
});
<template>
<view class="page">
<page-head :title="title"></page-head>
<button @click="startRequestAnimationFrame">requestAnimationFrame</button>
<text class="frame-count">{{testFrameCount}}</text>
</view>
</template>
<script>
export default {
data() {
return {
title: 'requestAnimationFrame',
testFrameCount: 0
}
},
methods: {
startRequestAnimationFrame() {
uni.requestAnimationFrame((_ : number) => {
this.testFrameCount++;
})
}
}
}
</script>
<style>
.page {
padding: 15px;
}
.frame-count {
margin-top: 15px;
}
</style>
// uni-app自动化测试教程: uni-app自动化测试教程: https://uniapp.dcloud.net.cn/worktile/auto/hbuilderx-extension/
describe('API-toast', () => {
let page;
const isApp = process.env.UNI_OS_NAME === "android" || process.env.UNI_OS_NAME === "ios";
beforeAll(async () => {
page = await program.reLaunch('/pages/API/show-toast/show-toast')
await page.waitFor(600);
await page.waitFor("view");
});
it("onload-toast-test", async () => {
async function toScreenshot(imgName) {
if (isApp) {
await page.waitFor(500);
const res = await page.callMethod('jest_getWindowInfo')
const windowHeight = res.windowHeight * res.pixelRatio;
const windowWidth = res.windowWidth * res.pixelRatio;
const image = await program.screenshot({
deviceShot: true,
area: {
......@@ -31,163 +23,53 @@ describe('API-toast', () => {
width:windowWidth
},
});
expect(image).toSaveImageSnapshot();
expect(image).toSaveImageSnapshot({customSnapshotIdentifier() {
return imgName
}})
}else{
const image = await program.screenshot({
deviceShot: true,
fullPage: true
});
expect(image).toSaveImageSnapshot()
expect(image).toSaveImageSnapshot({customSnapshotIdentifier() {
return imgName
}})
}
})
await page.waitFor(500);
}
it("btn-toast-default-1", async () => {
const btnToastDefaultButton = await page.$('#btn-toast-default')
await btnToastDefaultButton.tap()
await page.waitFor(200)
if (isApp) {
const res = await page.callMethod('jest_getWindowInfo')
const windowHeight = res.windowHeight * res.pixelRatio;
const windowWidth = res.windowWidth * res.pixelRatio;
const image = await program.screenshot({
deviceShot: true,
area: {
x: 0,
y: 200,
height: windowHeight - 200,
width:windowWidth
},
});
expect(image).toSaveImageSnapshot();
}else{
const image = await program.screenshot({
deviceShot: true,
fullPage: true
});
expect(image).toSaveImageSnapshot()
}
it("onload-toast-test", async () => {
await toScreenshot('toast-onload')
})
it("btn-toast-duration-1", async () => {
const btnToastDurationButton = await page.$('#btn-toast-duration')
await btnToastDurationButton.tap()
await page.waitFor(2000)
if (isApp) {
const res = await page.callMethod('jest_getWindowInfo')
const windowHeight = res.windowHeight * res.pixelRatio;
const windowWidth = res.windowWidth * res.pixelRatio;
const image = await program.screenshot({
deviceShot: true,
area: {
x: 0,
y: 200,
height: windowHeight - 200,
width:windowWidth
},
});
expect(image).toSaveImageSnapshot();
}else{
const image = await program.screenshot({
deviceShot: true,
fullPage: true
});
expect(image).toSaveImageSnapshot()
it("icon-toast-test", async () => {
const icons = await page.$$('.radio-icon')
for (let i = 0; i < icons.length; i++) {
await icons[i].tap()
const iconText = await icons[i].text()
await page.callMethod('toast1Tap')
await page.waitFor(100);
await toScreenshot(`${iconText}`)
}
})
it("btn-toast-errorIcon-1", async () => {
const btnToastErrorIconButton = await page.$('#btn-toast-errorIcon')
await btnToastErrorIconButton.tap()
await page.waitFor(200)
if (isApp) {
const res = await page.callMethod('jest_getWindowInfo')
const windowHeight = res.windowHeight * res.pixelRatio;
const windowWidth = res.windowWidth * res.pixelRatio;
const image = await program.screenshot({
deviceShot: true,
area: {
x: 0,
y: 200,
height: windowHeight - 200,
width:windowWidth
},
});
expect(image).toSaveImageSnapshot();
}else{
const image = await program.screenshot({
deviceShot: true,
fullPage: true
});
expect(image).toSaveImageSnapshot()
}
it("image-toast-test", async () => {
await page.setData({imageSelect: true})
await page.waitFor(300);
await page.callMethod('toast1Tap')
await page.waitFor(300);
await toScreenshot('toast-image')
})
it("btn-toast-loading-1", async () => {
const btnToastLoadingButton = await page.$('#btn-toast-loading')
await btnToastLoadingButton.tap()
await page.waitFor(200)
const btnToastHideButton = await page.$('#btn-toast-hide')
await btnToastHideButton.tap()
await page.waitFor(1000)
if (isApp) {
const res = await page.callMethod('jest_getWindowInfo')
const windowHeight = res.windowHeight * res.pixelRatio;
const windowWidth = res.windowWidth * res.pixelRatio;
const image = await program.screenshot({
deviceShot: true,
area: {
x: 0,
y: 200,
height: windowHeight - 200,
width:windowWidth
},
});
expect(image).toSaveImageSnapshot();
}else{
const image = await program.screenshot({
deviceShot: true,
fullPage: true
});
expect(image).toSaveImageSnapshot()
}
it("duration-toast-test", async () => {
await page.setData({intervalSelect: 4000})
await page.callMethod('toast1Tap')
await page.waitFor(2000);
await toScreenshot('toast-duration-2000')
await page.waitFor(1000);
await page.callMethod('hideToast')
await page.waitFor(300);
await toScreenshot('toast-duration-end')
})
// it("btn-toast-postion-bottom-1", async () => {
// const btnToastButton = await page.$('#btn-toast-postion-bottom')
// await btnToastButton.tap()
// await page.waitFor(200)
// if (isApp) {
// const windowHeight = uni.getWindowInfo().windowHeight;
// const windowWidth = uni.getWindowInfo().windowWidth;
// const image = await program.screenshot({
// deviceShot: true,
// area: {
// x: 0,
// y: 200,
// height: windowHeight,
// width:windowWidth
// },
// });
// expect(image).toSaveImageSnapshot();
// }else{
// const image = await program.screenshot({
// deviceShot: true,
// fullPage: true
// });
// expect(image).toSaveImageSnapshot()
// }
// })
});
<template>
<view>
<!-- #ifdef APP -->
<scroll-view direction="vertical" style="flex:1">
<!-- #endif -->
<page-head :title="title"></page-head>
<view class="uni-padding-wrap">
<view class="uni-padding-wrap">
<text class="uni-title-text uni-common-mb">设置icon</text>
</view>
<view class="uni-list uni-common-pl">
<radio-group @change="radioChangeIcon">
<radio class="uni-list-cell uni-list-cell-pd radio-icon" v-for="(icon, index) in icon_enum" :key="icon.value"
:class="index < icon_enum.length - 1 ? 'uni-list-cell-line' : ''" :value="icon.value"
:checked="index === icon_current">{{icon.name}}</radio>
</radio-group>
</view>
<view class="uni-list-cell uni-list-cell-padding">
<view class="uni-list-cell-db">是否显示自定义图标</view>
<switch :checked="imageSelect" @change="change_image_boolean" />
</view>
<view class="uni-list-cell uni-list-cell-padding">
<view class="uni-list-cell-db">是否显示透明蒙层-屏蔽点击事件</view>
<switch :checked="maskSelect" @change="change_mask_boolean" />
</view>
<view class="uni-title uni-list-cell-padding">提示的延迟时间,默认:1500(单位毫秒)</view>
<view class="uni-list-cell-padding">
<slider @change="sliderChange" foreColor="#007AFF" :value="intervalSelect" :min="1500" :max="5000" :show-value="true" />
</view>
<view class="uni-btn-v">
<button class="uni-btn-v" type="default" @tap="toast1Tap" id="btn-toast-default" >点击弹出默认toast</button>
<button class="uni-btn-v" type="default" @tap="toastTapIconError" id="btn-toast-errorIcon">点击弹出设置icon的toast</button>
<button class="uni-btn-v" type="default" @tap="toast2Tap" id="btn-toast-duration">点击弹出设置duration的toast</button>
<button class="uni-btn-v" type="default" @tap="toast3Tap" id="btn-toast-loading">点击弹出显示loading的toast</button>
<!-- #ifndef MP-ALIPAY -->
<button class="uni-btn-v" type="default" @tap="toast4Tap">点击弹出显示自定义图片的toast</button>
<!-- #endif -->
<!-- #ifdef APP -->
<button class="uni-btn-v" type="default" @tap="toast5Tap" id="btn-toast-postion-bottom">点击显示无图标的居底toast</button>
<!-- #endif -->
<button class="uni-btn-v" type="default" @tap="toast1Tap" id="btn-toast-default">点击弹出toast</button>
<button class="uni-btn-v" type="default" @tap="hideToast" id="btn-toast-hide">点击隐藏toast</button>
</view>
<!-- #ifdef APP -->
<view class="uni-padding-wrap uni-common-mt">
<text class="uni-title-text uni-common-mb"> 设置position,仅App生效 </text>
</view>
<view class="uni-list uni-common-pl">
<radio-group @change="radioChangePosition">
<radio class="uni-list-cell uni-list-cell-pd radio-position" v-for="(position, index) in position_enum" :key="position.value"
:class="index < position_enum.length - 1 ? 'uni-list-cell-line' : ''" :value="position.value"
:checked="index === position_current">{{position.name}}</radio>
</radio-group>
</view>
<button class="uni-btn uni-btn-v uni-common-mb" type="default" @tap="toast2Tap">点击弹出设置position的toast</button>
<!-- #endif -->
<text>{{exeRet}}</text>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script>
<script lang="uts">
type IconItemType = {
value : "success" | "error" | "fail" | "exception" | "loading" | "none";
name : string
}
type PositionItemType = {
value : "top" | "center" | "bottom";
name : string
}
export default {
data() {
return {
title: 'toast',
exeRet: ''
exeRet: '',
imageSelect:false,
maskSelect: false,
intervalSelect: 1500,
position_current:0,
position_enum: [
{ "value": "top", "name": "top: 居上显示" },
{ "value": "center", "name": "center: 居中显示" },
{ "value": "bottom", "name": "bottom: 居底显示" },
] as PositionItemType[],
icon_current:0,
icon_enum: [
{
value: 'success',
name: '显示成功图标',
},
{
value: 'error',
name: '显示错误图标',
},
// {
// value: 'fail',
// name: '显示错误图标',
// },
// {
// value: 'exception',
// name: '显示异常图标,此时title文本无长度显示',
// },
{
value: 'loading',
name: '显示加载图标',
},
{
value: 'none',
name: '不显示图标',
},
] as IconItemType[],
}
},
onLoad() {
......@@ -41,59 +115,40 @@
jest_getWindowInfo() : GetWindowInfoResult {
return uni.getWindowInfo();
},
toast1Tap: function () {
uni.showToast({
title: "默认",
success: (res) => {
this.exeRet = "success:" + JSON.stringify(res)
},
fail: (res) => {
this.exeRet = "fail:" + JSON.stringify(res)
},
})
radioChangeIcon(e : UniRadioGroupChangeEvent) {
for (let i = 0; i < this.icon_enum.length; i++) {
if (this.icon_enum[i].value === e.detail.value) {
this.icon_current = i;
break;
}
}
},
toastTapIconError: function () {
uni.showToast({
title: "默认",
icon: 'error',
success: (res) => {
this.exeRet = "success:" + JSON.stringify(res)
},
fail: (res) => {
this.exeRet = "fail:" + JSON.stringify(res)
},
})
change_image_boolean: function (e : UniSwitchChangeEvent) {
this.imageSelect = e.detail.value
},
toast2Tap: function () {
uni.showToast({
title: "duration 3000",
duration: 3000,
success: (res) => {
this.exeRet = "success:" + JSON.stringify(res)
},
fail: (res) => {
this.exeRet = "fail:" + JSON.stringify(res)
},
})
change_mask_boolean: function (e : UniSwitchChangeEvent) {
this.maskSelect = e.detail.value
},
toast3Tap: function () {
uni.showToast({
title: "loading",
icon: "loading",
duration: 5000,
success: (res) => {
this.exeRet = "success:" + JSON.stringify(res)
},
fail: (res) => {
this.exeRet = "fail:" + JSON.stringify(res)
},
})
sliderChange(e : UniSliderChangeEvent) {
this.intervalSelect = e.detail.value
},
radioChangePosition(e : UniRadioGroupChangeEvent) {
for (let i = 0; i < this.position_enum.length; i++) {
if (this.position_enum[i].value === e.detail.value) {
this.position_current = i;
break;
}
}
},
toast4Tap: function () {
toast1Tap: function () {
uni.showToast({
title: "logo",
image: "/static/uni.png",
title: "默认",
icon: this.icon_enum[this.icon_current].value,
duration: this.intervalSelect,
image: this.imageSelect? "/static/uni.png" : null ,
mask: this.maskSelect,
success: (res) => {
console.log('success:',res)
this.exeRet = "success:" + JSON.stringify(res)
},
fail: (res) => {
......@@ -102,10 +157,11 @@
})
},
// #ifdef APP
toast5Tap: function () {
toast2Tap: function () {
let positionValue = this.position_enum[this.position_current].value
uni.showToast({
title: "显示一段轻提示",
position: 'bottom',
title: "显示一段轻提示,position:" + positionValue,
position: positionValue,
success: (res) => {
this.exeRet = "success:" + JSON.stringify(res)
},
......@@ -121,3 +177,4 @@
}
}
</script>
此差异已折叠。
......@@ -39,7 +39,7 @@
this.vy = Math.abs(this.vy)
return
}
if (this.y > this.width - this.radius) {
if (this.y > this.height - this.radius) {
this.vy = -Math.abs(this.vy)
}
}
......@@ -50,13 +50,10 @@
private ballList : Array<Ball> = []
private speed = 3
private layer = 3
private ballInlayer = 20
private interval : number = 0
private ballInlayer = 20
private runningFlag : boolean = false
// #ifdef WEB
private _animateResult: number = 0
// #endif
constructor(ctx : CanvasRenderingContext2D) {
this.ctx = ctx
......@@ -95,46 +92,24 @@
// this.ctx.ellipse(item.x, item.y, item.radius, item.radius, 0, 0, Math.PI * 2)
this.ctx.fill()
})
// #ifdef APP
this.ctx.draw()
// #endif
// #ifdef WEB
if (!this.runningFlag) {
return
}
this._animateResult = requestAnimationFrame(() => {
this._animateResult = uni.requestAnimationFrame((_: number) => {
this.animate()
})
// #endif
}
start() {
// #ifdef WEB
cancelAnimationFrame(this._animateResult)
start() {
uni.cancelAnimationFrame(this._animateResult)
this.runningFlag = true
this.animate()
// #endif
// #ifdef APP
//Todo.. requestAnimationFrame
clearInterval(this.interval)
this.interval = setInterval(() => {
this.animate()
}, 17)
// #endif
}
stop() {
// #ifdef WEB
stop() {
this.runningFlag = false
cancelAnimationFrame(this._animateResult)
// #endif
// #ifdef APP
//Todo.. requestAnimationFrame
clearInterval(this.interval)
// #endif
uni.cancelAnimationFrame(this._animateResult)
}
}
......
<template>
<canvas class="drawing" id="tablet" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd"></canvas>
<button @click="doClear()">清空</button>
<canvas class="drawing" id="tablet" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd"></canvas>
<button @click="doClear()">清空</button>
</template>
<script>
export default {
data() {
return {
lastPointX : 0,
lastPointY : 0
}
},
onShow() {
},
type Point = {
x : number
y : number
};
onReady() {
let element = uni.getElementById('tablet') as UniCanvasElement
let ctx = element.getContext("2d")
if ( ctx != null) {
const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1;
element.width = element.offsetWidth * dpr
element.height = element.offsetHeight * dpr
ctx.scale(dpr, dpr)
export default {
data() {
return {
lastPointX: 0,
lastPointY: 0,
canvasElement: null as UniCanvasElement | null,
renderingContext: null as CanvasRenderingContext2D | null,
}
},
methods: {
touchStart(event: TouchEvent){
let element = uni.getElementById('tablet') as UniCanvasElement
const elRect = element.getBoundingClientRect();
const touch = event.touches[0];
const x = touch.clientX - elRect.left
const y = touch.clientY - elRect.top
this.lastPointX = x
this.lastPointY = y
},
touchMove(event: TouchEvent){
let element = uni.getElementById('tablet') as UniCanvasElement
const elRect = element.getBoundingClientRect();
const touch = event.touches[0];
const x = touch.clientX - elRect.left
const y = touch.clientY - elRect.top
let ctx = element.getContext("2d")
if ( null != ctx) {
ctx.lineWidth = 5;
ctx.lineCap = "round";
ctx.lineJoin = "square";
ctx.beginPath()
ctx.moveTo(this.lastPointX, this.lastPointY)
ctx.lineTo(x, y)
ctx.stroke()
}
this.lastPointX = x
this.lastPointY = y
},
touchEnd(event: TouchEvent){
},
doClear(){
let element = uni.getElementById('tablet') as UniCanvasElement
let ctx = element.getContext("2d")
if (null != ctx ) {
ctx.clearRect(0, 0, element.width, element.height)
}
}
}
}
},
onReady() {
this.canvasElement = uni.getElementById('tablet') as UniCanvasElement
this.renderingContext = this.canvasElement!.getContext("2d")!
const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1;
this.canvasElement!.width = this.canvasElement!.offsetWidth * dpr
this.canvasElement!.height = this.canvasElement!.offsetHeight * dpr
this.renderingContext!.scale(dpr, dpr)
},
methods: {
touchStart(event : TouchEvent) {
const position = this.getPosition(event)
this.lastPointX = position.x
this.lastPointY = position.y
},
touchMove(event : TouchEvent) {
const position = this.getPosition(event)
const x = position.x
const y = position.y
const ctx = this.renderingContext!
ctx.lineWidth = 5
ctx.lineCap = "round"
ctx.lineJoin = "square"
ctx.beginPath()
ctx.moveTo(this.lastPointX, this.lastPointY)
ctx.lineTo(x, y)
ctx.stroke()
this.lastPointX = x
this.lastPointY = y
},
touchEnd(_ : TouchEvent) {
},
doClear() {
if (this.renderingContext != null) {
this.renderingContext!.clearRect(0, 0, this.canvasElement!.width, this.canvasElement!.height)
}
},
getPosition(event : TouchEvent) : Point {
const elRect = this.canvasElement!.getBoundingClientRect()
const touch = event.touches[0]
return {
x: touch.clientX - elRect.left,
y: touch.clientY - elRect.top
} as Point
}
}
}
</script>
<style>
.drawing {
width: 100%;
height: 500px;
background-color: lightgray;
margin-bottom: 15px;
}
.drawing {
width: 100%;
height: 500px;
background-color: lightgray;
margin-bottom: 15px;
}
</style>
<template>
<view>
<native-button class="native-button" style="width: 200px; height: 100px;" :text="buttonText" @buttonTap="ontap"
@load="onload"></native-button>
<view>
<!-- #ifdef APP-ANDROID -->
<native-button class="native-button" style="width: 200px; height: 100px;" :text="buttonText" @buttonTap="ontap"
@load="onload"></native-button>
<time-picker class="native-time-picker" :hour=2 :minute=3 @changed="onChanged"></time-picker>
<!-- #endif -->
</view>
......
......@@ -11,32 +11,39 @@
<u-link :href="'https://uniapp.dcloud.io/uni-app-x/api/'" :text="'https://uniapp.dcloud.io/uni-app-x/api/'"
:inWhiteList="true"></u-link>
</view>
<uni-collapse>
<uni-collapse-item ref="category" v-for="menuItem in menu" :key="menuItem!.id" :title="menuItem.name"
class="item">
<view v-for="page in menuItem.pages" :key="page!.path" class="uni-navigate-item" hover-class="is--active"
@click="goPage(`/${page.path}`)">
<text class="uni-navigate-text" :class="{'left-win-active': leftWinActive === page.path && hasLeftWin}">{{page.style["navigationBarTitleText"]}}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
<uni-collapse style="width: 100%" v-for="childMenu in menuItem.children" :key="childMenu!.id">
<uni-collapse-item :title="childMenu.name" class="item" style="margin-bottom: 0">
<view class="uni-navigate-item" hover-class="is--active" v-for="childPage in childMenu.pages"
:key="childPage!.path" @click="goPage(`/${childPage.path}`)">
<text class="uni-navigate-text">{{
childPage.style["navigationBarTitleText"]
}}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
</uni-collapse-item>
</uni-collapse>
</uni-collapse-item>
</uni-collapse>
<view v-if="!hasLeftWin" ref="pop" @click="hidePop()" class="popup">
<view style="width: 90%; background-color: white" @click="stopClickPop">
<api-set-tabbar></api-set-tabbar>
</view>
</view>
<uni-collapse>
<uni-collapse-item ref="category" v-for="menuItem in menu" :key="menuItem!.id" :title="menuItem.name"
class="item">
<template v-for="childMenuItem in menuItem.items" :key="childMenuItem!.id">
<view v-if="childMenuItem.items.length==0" style="padding-left: 18px" class="uni-navigate-item"
hover-class="is--active" @click="goPage(`/${childMenuItem.path}`)">
<text class="uni-navigate-text" :class="{
'left-win-active': leftWinActive === childMenuItem.path && hasLeftWin,
}">{{ childMenuItem.style["navigationBarTitleText"] }}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
<uni-collapse v-else style="width: 100%">
<uni-collapse-item :title="childMenuItem.name" class="item"
style="margin-bottom: 0; padding-left: 5px; padding-right: 5px">
<view style="padding-left: 18px" class="uni-navigate-item" hover-class="is--active"
v-for="grandChildMenuItem in childMenuItem.items" :key="grandChildMenuItem!.path"
@click="goPage(`/${grandChildMenuItem.path}`)">
<text class="uni-navigate-text" :class="{
'left-win-active':
leftWinActive === grandChildMenuItem.path && hasLeftWin,
}">{{ grandChildMenuItem.style["navigationBarTitleText"] }}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
</uni-collapse-item>
</uni-collapse>
</template>
</uni-collapse-item>
</uni-collapse>
<view v-if="!hasLeftWin" ref="pop" @click="hidePop()" class="popup">
<view style="width: 90%; background-color: white" @click="stopClickPop">
<api-set-tabbar></api-set-tabbar>
</view>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
......@@ -56,10 +63,10 @@
}
},
computed: {
hasLeftWin():boolean{
hasLeftWin() : boolean {
return !state.noMatchLeftWindow
},
leftWinActive():string{
leftWinActive() : string {
return state.leftWinActive.slice(1)
}
},
......@@ -67,8 +74,12 @@
goPage(url : string) {
if (url == '/set-tab-bar') {
this.showPop()
} else {
uni.navigateTo({ url })
} else {
if(this.hasLeftWin) {
uni.reLaunch({ url })
} else {
uni.navigateTo({ url })
}
}
},
showPop: function () {
......@@ -82,20 +93,20 @@
}
},
// #ifdef WEB
watch:{
$route: {
immediate: true,
handler(newRoute) {
if (newRoute.matched.length) {
const activeCategoryIndex = this.menu.findIndex(menuItem => menuItem?.pages.some(page => this.leftWinActive && this.leftWinActive === page?.path))
watch: {
$route: {
immediate: true,
handler(newRoute) {
if (newRoute.matched.length) {
const activeCategoryIndex = this.menu.findIndex(menuItem => menuItem?.items.some(item => this.leftWinActive && this.leftWinActive === item?.path))
if (activeCategoryIndex > -1) {
this.$nextTick(() => {
((this.$refs.category as ComponentPublicInstance[])[activeCategoryIndex])?.$callMethod('openCollapse', true)
})
}
}
}
}
}
}
}
},
// #endif
}
......@@ -122,4 +133,4 @@
display: none;
background-color: rgba(16, 16, 16, 0.5);
}
</style>
</style>
......@@ -11,28 +11,33 @@
<u-link :href="'https://uniapp.dcloud.io/uni-app-x/css/'" :text="'https://uniapp.dcloud.io/uni-app-x/css/'"
:inWhiteList="true"></u-link>
</view>
<uni-collapse>
<uni-collapse-item ref="category" v-for="menuItem in menu" :key="menuItem!.id" :title="menuItem.name"
class="item">
<view v-for="page in menuItem.pages" :key="page!.path" class="uni-navigate-item" hover-class="is--active"
@click="goPage(`/${page.path}`)">
<text class="uni-navigate-text" :class="{'left-win-active': leftWinActive === page.path && hasLeftWin}">{{
page.style["navigationBarTitleText"]
}}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
<uni-collapse style="width: 100%" v-for="childMenu in menuItem.children" :key="childMenu!.id">
<uni-collapse-item :title="childMenu.name" class="item" style="margin-bottom: 0">
<view class="uni-navigate-item" hover-class="is--active" v-for="childPage in childMenu.pages"
:key="childPage!.path" @click="goPage(`/${childPage.path}`)">
<text class="uni-navigate-text">{{
childPage.style["navigationBarTitleText"]
}}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
</uni-collapse-item>
</uni-collapse>
</uni-collapse-item>
<uni-collapse>
<uni-collapse-item ref="category" v-for="menuItem in menu" :key="menuItem!.id" :title="menuItem.name"
class="item">
<template v-for="childMenuItem in menuItem.items" :key="childMenuItem!.id">
<view v-if="childMenuItem.items.length==0" style="padding-left: 18px" class="uni-navigate-item"
hover-class="is--active" @click="goPage(`/${childMenuItem.path}`)">
<text class="uni-navigate-text" :class="{
'left-win-active': leftWinActive === childMenuItem.path && hasLeftWin,
}">{{ childMenuItem.style["navigationBarTitleText"] }}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
<uni-collapse v-else style="width: 100%">
<uni-collapse-item :title="childMenuItem.name" class="item"
style="margin-bottom: 0; padding-left: 5px; padding-right: 5px">
<view style="padding-left: 18px" class="uni-navigate-item" hover-class="is--active"
v-for="grandChildMenuItem in childMenuItem.items" :key="grandChildMenuItem!.path"
@click="goPage(`/${grandChildMenuItem.path}`)">
<text class="uni-navigate-text" :class="{
'left-win-active':
leftWinActive === grandChildMenuItem.path && hasLeftWin,
}">{{ grandChildMenuItem.style["navigationBarTitleText"] }}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
</uni-collapse-item>
</uni-collapse>
</template>
</uni-collapse-item>
</uni-collapse>
</view>
<!-- #ifdef APP -->
......@@ -41,9 +46,9 @@
</template>
<script lang="uts">
import { generateMenu } from './generateMenu.uts'
import { MenuItem } from './generateMenu.uts'
const menu = generateMenu('pages/CSS')
import { generateMenu } from './generateMenu.uts'
import { MenuItem } from './generateMenu.uts'
const menu = generateMenu('pages/CSS')
import { state } from '@/store/index.uts'
export default {
data() {
......@@ -51,36 +56,40 @@
menu: menu as (MenuItem | null)[],
arrowRightIcon: '/static/icons/arrow-right.png',
}
},
computed: {
hasLeftWin():boolean{
return !state.noMatchLeftWindow
},
leftWinActive():string{
return state.leftWinActive.slice(1)
}
},
computed: {
hasLeftWin() : boolean {
return !state.noMatchLeftWindow
},
leftWinActive() : string {
return state.leftWinActive.slice(1)
}
},
methods: {
goPage(url : string) {
uni.navigateTo({ url })
goPage(url : string) {
if(this.hasLeftWin) {
uni.reLaunch({ url })
} else {
uni.navigateTo({ url })
}
},
},
// #ifdef WEB
watch:{
$route: {
immediate: true,
handler(newRoute) {
if (newRoute.matched.length) {
const activeCategoryIndex = this.menu.findIndex(menuItem => menuItem?.pages.some(page => this.leftWinActive && this.leftWinActive === page?.path))
if (activeCategoryIndex > -1) {
this.$nextTick(() => {
((this.$refs.category as ComponentPublicInstance[])[activeCategoryIndex]).$callMethod('openCollapse', true)
})
}
}
}
}
},
},
// #ifdef WEB
watch: {
$route: {
immediate: true,
handler(newRoute) {
if (newRoute.matched.length) {
const activeCategoryIndex = this.menu.findIndex(menuItem => menuItem?.items.some(item => this.leftWinActive && this.leftWinActive === item?.path))
if (activeCategoryIndex > -1) {
this.$nextTick(() => {
((this.$refs.category as ComponentPublicInstance[])[activeCategoryIndex]).$callMethod('openCollapse', true)
})
}
}
}
}
},
// #endif
}
</script>
......
......@@ -11,28 +11,33 @@
<u-link :href="'https://uniapp.dcloud.io/uni-app-x/component/'"
:text="'https://uniapp.dcloud.io/uni-app-x/component/'" :inWhiteList="true"></u-link>
</view>
<uni-collapse>
<uni-collapse-item ref="category" v-for="menuItem in menu" :key="menuItem!.id" :title="menuItem.name"
class="item">
<view v-for="page in menuItem.pages" :key="page!.path" class="uni-navigate-item" hover-class="is--active"
@click="goPage(`/${page.path}`)">
<text class="uni-navigate-text" :class="{'left-win-active': leftWinActive === page.path && hasLeftWin}">{{
page.style["navigationBarTitleText"]
}}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
<uni-collapse style="width: 100%" v-for="childMenu in menuItem.children" :key="childMenu!.id">
<uni-collapse-item :title="childMenu.name" class="item" style="margin-bottom: 0">
<view class="uni-navigate-item" hover-class="is--active" v-for="childPage in childMenu.pages"
:key="childPage!.path" @click="goPage(`/${childPage.path}`)">
<text class="uni-navigate-text" :class="{'left-win-active': leftWinActive === childPage.path && hasLeftWin}">{{
childPage.style["navigationBarTitleText"]
}}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
</uni-collapse-item>
</uni-collapse>
</uni-collapse-item>
<uni-collapse>
<uni-collapse-item ref="category" v-for="menuItem in menu" :key="menuItem!.id" :title="menuItem.name"
class="item">
<template v-for="childMenuItem in menuItem.items" :key="childMenuItem!.id">
<view v-if="childMenuItem.items.length==0" style="padding-left: 18px" class="uni-navigate-item"
hover-class="is--active" @click="goPage(`/${childMenuItem.path}`)">
<text class="uni-navigate-text" :class="{
'left-win-active': leftWinActive === childMenuItem.path && hasLeftWin,
}">{{ childMenuItem.style["navigationBarTitleText"] }}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
<uni-collapse v-else style="width: 100%">
<uni-collapse-item :title="childMenuItem.name" class="item"
style="margin-bottom: 0; padding-left: 5px; padding-right: 5px">
<view style="padding-left: 18px" class="uni-navigate-item" hover-class="is--active"
v-for="grandChildMenuItem in childMenuItem.items" :key="grandChildMenuItem!.path"
@click="goPage(`/${grandChildMenuItem.path}`)">
<text class="uni-navigate-text" :class="{
'left-win-active':
leftWinActive === grandChildMenuItem.path && hasLeftWin,
}">{{ grandChildMenuItem.style["navigationBarTitleText"] }}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
</view>
</uni-collapse-item>
</uni-collapse>
</template>
</uni-collapse-item>
</uni-collapse>
<!-- #ifdef UNI-APP-X && APP -->
......@@ -59,18 +64,22 @@
arrowRightIcon: '/static/icons/arrow-right.png' as string.ImageURIString,
pageHiden: false
}
},
computed: {
hasLeftWin():boolean{
return !state.noMatchLeftWindow
},
leftWinActive():string{
return state.leftWinActive.slice(1)
}
},
},
computed: {
hasLeftWin() : boolean {
return !state.noMatchLeftWindow
},
leftWinActive() : string {
return state.leftWinActive.slice(1)
}
},
methods: {
goPage(url : string) {
uni.navigateTo({ url })
goPage(url : string) {
if(this.hasLeftWin) {
uni.reLaunch({ url })
} else {
uni.navigateTo({ url })
}
},
// #ifdef UNI-APP-X && APP
upgradePopupShow() {
......@@ -84,23 +93,23 @@
uni.showTabBar()?.catch(_ => { })
}
// #endif
},
// #ifdef WEB
watch:{
$route: {
immediate: true,
handler(newRoute) {
if (newRoute.matched.length) {
const activeCategoryIndex = this.menu.findIndex(menuItem => menuItem?.pages.some(page => this.leftWinActive && this.leftWinActive === page?.path))
if (activeCategoryIndex > -1) {
this.$nextTick(() => {
((this.$refs.category as ComponentPublicInstance[])[activeCategoryIndex])?.$callMethod('openCollapse', true)
})
}
}
}
}
},
},
// #ifdef WEB
watch: {
$route: {
immediate: true,
handler(newRoute) {
if (newRoute.matched.length) {
const activeCategoryIndex = this.menu.findIndex(menuItem => menuItem?.items.some(item => this.leftWinActive && this.leftWinActive === item?.path))
if (activeCategoryIndex > -1) {
this.$nextTick(() => {
((this.$refs.category as ComponentPublicInstance[])[activeCategoryIndex])?.$callMethod('openCollapse', true)
})
}
}
}
}
},
// #endif
onReady() {
// #ifdef UNI-APP-X && APP
......
......@@ -15,15 +15,17 @@ type PageGroup = {
type Page = {
path : string,
style : UTSJSONObject,
group ?: string | null
group ?: string | null,
}
export type MenuItem = {
id : string,
name : string,
index : number,
children : (MenuItem | null)[],
pages : (Page | null)[]
path : string,
style : UTSJSONObject,
group ?: string | null,
items : (MenuItem | null)[]
}
export function generateMenu(tabBarType : string) : (MenuItem | null)[] {
......@@ -37,6 +39,7 @@ export function generateMenu(tabBarType : string) : (MenuItem | null)[] {
let currentGroups : (Group | null)[] | null = groupTree
const pageGroups : PageGroup[] = []
groupIndexList.forEach((groupIndex, index) => {
// 跳过第一层 component API CSS
if (index > 0) {
pageGroups.push({ id: currentGroups![groupIndex]!.id, name: currentGroups![groupIndex]!.name, index: groupIndex } as PageGroup)
}
......@@ -60,27 +63,39 @@ export function generateMenu(tabBarType : string) : (MenuItem | null)[] {
id: id.split('.').slice(-1)[0],
name,
index: validIndex,
children: [] as (MenuItem | null)[],
pages: [] as (Page | null)[]
path: '',
style: {},
group: '',
items: [] as (MenuItem | null)[],
} as MenuItem
}
currentMenu = menuItemArr[validIndex]
if (groupIndex < groupLength - 1) {
menuItemArr = menuItemArr[validIndex]!.children as (MenuItem | null)[]
menuItemArr = menuItemArr[validIndex]!.items as (MenuItem | null)[]
}
})
const pageMenuItem : MenuItem = {
id: page.path,
name: page.style["navigationBarTitleText"] as string,
index: lastGroup.index,
path: page.path,
style: page.style,
group: page.group,
items: [] as (MenuItem | null)[],
}
if (hasPageGroup) {
const pageIndex = lastGroup.index
fillPageArrayWithNull(currentMenu!.pages, pageIndex)
if (currentMenu!.pages[pageIndex] == null) {
currentMenu!.pages[pageIndex] = page
fillMenuArrayWithNull(currentMenu!.items, pageIndex)
if (currentMenu!.items[pageIndex] == null) {
currentMenu!.items[pageIndex] = pageMenuItem
} else {
currentMenu!.pages.push(page)
currentMenu!.items.push(pageMenuItem)
}
} else {
currentMenu!.pages.push(page)
currentMenu!.items.push(pageMenuItem)
}
})
......@@ -116,24 +131,15 @@ function fillMenuArrayWithNull(arr : (MenuItem | null)[], index : number) : void
}
}
function fillPageArrayWithNull(arr : (Page | null)[], index : number) : void {
const len = arr.length
for (let i = 0; i <= index - (len - 1); i++) {
arr.push(null)
}
}
function removeNullItem(arr : (MenuItem | null)[]) : (MenuItem | null)[] {
const res = arr.filter((item : MenuItem | null) : boolean => item !== null)
res.forEach(menuItem => {
addSetTabBarPage(menuItem!)
// #ifdef APP-ANDROID
menuItem.children = removeNullItem(menuItem.children)
menuItem.pages = menuItem.pages.filter((item : Page | null) : boolean => item !== null)
menuItem.items = removeNullItem(menuItem.items)
// #endif
// #ifndef APP-ANDROID
menuItem!.children = removeNullItem(menuItem!.children)
menuItem!.pages = menuItem!.pages.filter((item : Page | null) : boolean => item !== null)
menuItem!.items = removeNullItem(menuItem!.items)
// #endif
})
return res
......@@ -142,11 +148,16 @@ function removeNullItem(arr : (MenuItem | null)[]) : (MenuItem | null)[] {
function addSetTabBarPage(menuItem : MenuItem) {
const { id, name } = menuItem
if (id == 'page' && name == '页面和路由' && !state.noMatchLeftWindow) {
menuItem.pages.push({
menuItem.items.push({
id: 'set-tab-bar',
name: '设置 TabBar',
index: 0,
path: 'set-tab-bar',
style: {
navigationBarTitleText: '设置 TabBar'
}
} as Page)
},
group: null,
items: []
} as MenuItem)
}
}
......@@ -23,7 +23,7 @@
</image>
</view>
<view v-if="item.open">
<view class="uni-navigate-item" :hover-class="page.enable == false ? '' : 'is--active'"
<view style="padding-left: 18px;" class="uni-navigate-item" :hover-class="page.enable == false ? '' : 'is--active'"
v-for="(page, key) in item.pages" :key="key" @click="goDetailPage(page)">
<text class="uni-navigate-text" :class="{'left-win-active': leftWinActive === page.url && hasLeftWin,'text-disabled' : page.enable == false}">{{ page.name }}</text>
<image :src="arrowRightIcon" class="uni-icon-size"></image>
......@@ -262,10 +262,12 @@
const url =
e.url.indexOf('platform') > -1
? e.url
: `/pages/template/${e.url}/${e.url}`
uni.navigateTo({
url,
})
: `/pages/template/${e.url}/${e.url}`
if(this.hasLeftWin) {
uni.reLaunch({ url })
} else {
uni.navigateTo({ url })
}
},
},
// #ifdef WEB
......
import { UIDatePicker, UIControl, UIDatePickerStyle } from "UIKit"
import { DateFormatter } from "CoreFoundation"
export class NativeTimePicker {
element : UniNativeViewElement
timePicker : UIDatePicker
h : number
m : number
constructor(element : UniNativeViewElement, hour : number, minute : number) {
this.element = element
this.timePicker = new UIDatePicker()
this.h = hour
this.m = minute
super.init()
// 在 swift target-action 对应的方法需要以OC的方式来调用,那么OC语言中用Selector来表示一个方法的名称(又称方法选择器),创建一个Selector可以使用 Selector("functionName") 的方式。
const method = Selector("timeChange")
// 监听时间变化回调
this.timePicker.addTarget(this, action = method, for = UIControl.Event.valueChanged)
// 设置为时间选择模式
this.timePicker.datePickerMode = UIDatePicker.Mode.time
// 设置外观样式为 wheels
if (UTSiOS.available("iOS 13.4, *")) {
this.timePicker.preferredDatePickerStyle = UIDatePickerStyle.wheels
}
this.updateTime()
this.bindView(hour, minute)
}
// element 绑定原生view
bindView(hour : number, minute : number) {
this.element.bindIOSView(this.timePicker);
}
// 设置时
setHour(hour : number) {
this.h = hour
this.updateTime()
}
// 设置分
setMinute(minute : number) {
this.m = minute
this.updateTime()
}
updateTime() {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm"
let date = formatter.date(from = `${this.h}:${this.m}`)
if (date != null) {
this.timePicker.date = date!
}
}
/**
* 按钮点击回调方法
* 在 swift 中,所有target-action (例如按钮的点击事件,NotificationCenter 的通知事件等)对应的 action 函数前面都要使用 @objc 进行标记。
*/
@objc timeChange() {
// 发送事件
console.log("timeChange")
let formatter = DateFormatter()
formatter.dateFormat = "HH"
let hourString = formatter.string(from = this.timePicker.date)
formatter.dateFormat = "mm"
let minuteString = formatter.string(from = this.timePicker.date)
const detail = { "hour": hourString, "minute": minuteString }
const event = new UniObjectCustomEvent("timechanged", detail)
this.element.dispatchEvent(event)
}
destroy() {
UTSiOS.destroyInstance(self)
}
}
## 0.8.3(2024-07-31)
- 修复 部分类型报错
## 0.8.2(2024-07-15)
- 更新 static 下的静态图片放入 static/app 目录下,防止编译除 app 平台以外的平台时带入
## 0.8.1(2024-04-28)
......
......@@ -228,7 +228,7 @@
.sort((cur: StoreListItem, next: StoreListItem): number => next.priority - cur.priority)
.map((item: StoreListItem): string => item.scheme)
.reduce((promise: Promise<boolean>, cur: string): Promise<boolean> => {
openSchemePromise = promise.catch((): Promise<boolean> => openSchema(cur))
openSchemePromise = promise.catch<boolean>((): Promise<boolean> => openSchema(cur))
return openSchemePromise!
}, openSchemePromise!)
return openSchemePromise!
......
{
"id": "uni-upgrade-center-app",
"displayName": "升级中心 uni-upgrade-center - App",
"version": "0.8.2",
"version": "0.8.3",
"description": "uni升级中心 - 客户端检查更新",
"keywords": [
"uniCloud",
......
......@@ -177,7 +177,7 @@ function updateUseModal(packageInfo : UniUpgradeCenterResult) : void {
fail: err => {
uni.showModal({
title: '更新失败',
content: err.message,
content: err.errMsg,
showCancel: false
});
}
......
......@@ -2,8 +2,8 @@
<view class="top-window-header">
<view class="left-header">
<navigator class="left-header" open-type="reLaunch" url="/pages/component/global-properties/global-properties">
<image src="/static/logo.png" class="logo" mode="heightFix"></image>
<text>hello uni-app</text>
<image src="/static/uni.png" class="logo" mode="heightFix"></image>
<text>hello uni-app x</text>
</navigator>
</view>
<custom-tab-bar class="tab-bar-flex" direction="horizontal" :show-icon="false" :selected="current"
......@@ -13,8 +13,8 @@
<script lang="uts">
type IndexPageItem = {
tabBar : string;
index : string;
tabBar : string.PageURIString;
indexPageUrl : string.PageURIString;
}
type OnTabItemTapEvent = {
pagePath : string;
......@@ -33,16 +33,16 @@
current: 0,
indexPage: [{
tabBar: '/pages/tabBar/component',
index: '/pages/component/global-properties/global-properties'
indexPageUrl: '/pages/component/global-properties/global-properties'
}, {
tabBar: '/pages/tabBar/API',
index: '/pages/API/get-app/get-app'
indexPageUrl: '/pages/API/get-app/get-app'
}, {
tabBar: '/pages/tabBar/CSS',
index: '/pages/CSS/layout/width'
indexPageUrl: '/pages/CSS/layout/width'
}, {
tabBar: '/pages/tabBar/template',
index: '/pages/template/long-list/long-list'
indexPageUrl: '/pages/template/slider-100/slider-100'
}] as IndexPageItem[]
}
},
......@@ -69,7 +69,7 @@
return
}
uni.redirectTo({
url: indexPageItem.index
url: indexPageItem.indexPageUrl
})
return
} else {
......@@ -86,7 +86,7 @@
for (const item of this.indexPage) {
if (activeTabBar === item.tabBar) {
uni.redirectTo({
url: item.index
url: item.indexPageUrl
})
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册