提交 704d64d0 编写于 作者: DCloud-yyl's avatar DCloud-yyl

Merge branch 'alpha'

# Conflicts:
#	changelog.md
#	manifest.json
#	package.json
#	pages.json
#	pages/API/choose-location/choose-location.uvue
#	pages/API/compress-image/compress-image.uvue
#	pages/API/compress-video/compress-video.test.js
#	pages/API/compress-video/compress-video.uvue
#	pages/API/create-inner-audio-context/inner-audio.test.js
#	pages/API/download-file/download-file.uvue
#	pages/API/get-video-info/get-video-info.uvue
#	pages/API/inner-audio/inner-audio.uvue
#	pages/API/navigator/navigator.uvue
#	pages/API/open-location/open-location.uvue
#	pages/API/pull-down-refresh/pull-down-refresh.test.js
#	pages/API/request/request.test.js
#	pages/API/request/request.uvue
#	pages/API/show-modal/show-modal.test.js
#	pages/API/unicloud/unicloud/database.test.js
#	pages/API/upload-file/upload-file.uvue
#	pages/CSS/overflow/overflow-visible-event.uvue
#	pages/CSS/transform/scale.uvue
#	pages/component/canvas/canvas.test.js
#	pages/component/canvas/canvas.uvue
#	pages/component/checkbox/checkbox.uvue
#	pages/component/editor/editor.test.js
#	pages/component/general-event/general-event.test.js
#	pages/component/general-event/general-event.uvue
#	pages/component/image/image.test.js
#	pages/component/input/input.test.js
#	pages/component/input/input.uvue
#	pages/component/list-view/list-view-children-in-slot.test.js
#	pages/component/list-view/list-view-refresh.test.js
#	pages/component/list-view/list-view.test.js
#	pages/component/list-view/list-view.uvue
#	pages/component/map/map.test.js
#	pages/component/map/map.uvue
#	pages/component/radio/radio.uvue
#	pages/component/scroll-view/scroll-view-refresher.test.js
#	pages/component/scroll-view/scroll-view.test.js
#	pages/component/scroll-view/scroll-view.uvue
#	pages/component/swiper/swiper.test.js
#	pages/component/swiper/swiper.uvue
#	pages/component/textarea/textarea.uvue
#	pages/component/video/video.test.js
#	pages/component/video/video.uvue
#	pages/component/web-view-local/web-view-local.test.js
#	pages/component/web-view-local/web-view-local.uvue
#	pages/component/web-view/web-view.test.js
#	pages/component/web-view/web-view.uvue
#	pages/pages.test.js
#	pages/tabBar/API.uvue
#	pages/tabBar/CSS.uvue
#	pages/tabBar/component.uvue
#	pages/tabBar/template.uvue
#	pages/template/browser-canvas/browser-canvas.uvue
#	pages/template/slider-100/slider-100.uvue
#	readme.md
#	testSequencer.js
......@@ -20,9 +20,17 @@
myMap: new Map<string, any>(),
func: () : string => {
return 'globalData func'
}
},
onLaunch: function () {
launchOptions: {
path: '',
} as OnLaunchOptions,
onShowOption: {
path: ''
} as OnShowOptions
},
onLaunch: function (res : OnLaunchOptions) {
this.globalData.launchOptions = res
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1000)
console.log('App Launch')
......@@ -37,16 +45,57 @@
// observer1.observe({
// entryTypes: ['render', 'navigation'],
// } as PerformanceObserverOptions)
// 统计上报 - 应用启动
// uni.report({
// name: 'uni-app-launch',
// options: res,
// success(res_data) {
// console.log(res_data);
// }, fail(err_data) {
// console.log(err_data);
// }
// })
},
onShow: function () {
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')
// 统计上报 - 应用显示
// uni.report({
// name: 'uni-app-show',
// success(res_data) {
// console.log(res_data);
// }, fail(err_data) {
// console.log(err_data);
// }
// })
},
onHide: function () {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 100)
console.log('App Hide')
// 统计上报 - 应用进入后台
// uni.report({
// name: 'uni-app-hide',
// success(res) {
// console.log(res);
// }, fail(err) {
// console.log(err);
// }
// })
},
// #ifdef APP-ANDROID
onLastPageBackPress: function () {
......@@ -70,11 +119,56 @@
onExit() {
console.log('App Exit')
},
// onError(err : any) {
// // 统计上报 - 应用发生错误
// uni.report({
// name: 'uni-app-error',
// options: err,
// success(res) {
// console.log(res);
// }, fail(err) {
// console.log(err);
// }
// })
// },
// #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;
}
}
}
......@@ -83,4 +177,15 @@
<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>
## 1.0.16
* update 4.24.2024072208
## 1.0.31
* update 4.28.2024092105-alpha
## 1.0.15
* update 4.23.2024070804
## 1.0.30
* update 4.27.2024091308-alpha
## 1.0.27
* update 4.23.2024070309-alpha
## 1.0.29
* update 4.26.2024082213-alpha
## 1.0.25
* update 4.22.2024062415-alpha
## 1.0.24
* update 4.21.2024061818-alpha
## 1.0.23
* update 4.19.2024060704-alpha
## 1.0.22
* update 4.18.2024060311-alpha
## 1.0.21
* update 4.17.2024051110-alpha
## 1.0.20
* update 4.16.2024051009-alpha
## 1.0.19
* update 4.14.2024042905-alpha
## 1.0.18
* update 4.13.2024042321-alpha
## 1.0.17
* update 4.12.2024041009-alpha
## 1.0.28
* update 4.25.2024081703-alpha
## 1.0.11
* update 4.01.2024020211-alpha
......
......@@ -189,7 +189,7 @@
}
.text-disabled {
color: #a0a0a0;
color: #a0a0a0!important;
}
......@@ -246,6 +246,11 @@
font-weight: normal;
}
/* left-windows */
.left-win-active {
color: #007AFF!important;
}
/* --tab-bar-end-- */
/* #ifdef APP */
......
......@@ -6,6 +6,8 @@
<button class="button" @click="customStyle">{{ !hasCustomedStyle ? '自定义Tab样式' : '移除自定义样式'}}</button>
<button class="button" @click="customItem">{{ !hasCustomedItem ? '自定义Tab信息' : '移除自定义信息' }}</button>
<button class="button" @click="hideTabBar">{{ !hasHiddenTabBar ? '隐藏TabBar' : '显示TabBar' }}</button>
<button class="button" @click="hideTabBarItem">{{ !hasHiddenTabBarItem ? '隐藏接口Item' : '显示接口Item' }}</button>
<button class="button" @click="setTabBarTitle">{{ !hasSetLongTitle ? '自定义超长标题' : '移除自定义信息' }}</button>
<view class="btn-area">
<!-- <button class="button" type="primary" @click="navigateBack">关闭</button> -->
</view>
......@@ -21,7 +23,9 @@
hasShownTabBarRedDot: false,
hasCustomedStyle: false,
hasCustomedItem: false,
hasHiddenTabBar: false
hasHiddenTabBar: false,
hasHiddenTabBarItem: false,
hasSetLongTitle: false,
}
},
destroyed() {
......@@ -56,8 +60,58 @@
} as SetTabBarItemOptions
uni.setTabBarItem(tabBarOptions)
}
if (this.hasHiddenTabBarItem || this.hasSetLongTitle) {
let tabBarOptions = {
visible: true,
index: 1,
text: '接口',
iconPath: '/static/api.png',
selectedIconPath: '/static/apiHL.png'
} as SetTabBarItemOptions
uni.setTabBarItem(tabBarOptions)
}
},
methods: {
setTabBarTitle(){
let tabBarOptions = {
visible: true,
index: 1,
text: '接口',
iconPath: '/static/api.png',
selectedIconPath: '/static/apiHL.png'
} as SetTabBarItemOptions
if (!this.hasSetLongTitle) {
tabBarOptions.text = "超长标题内容超长标题内容超长标题内容超长标题测试";
tabBarOptions.iconPath = "";
tabBarOptions.selectedIconPath = "";
} else {
tabBarOptions.text = "接口";
tabBarOptions.iconPath = "/static/api.png";
tabBarOptions.selectedIconPath = "/static/apiHL.png";
}
uni.setTabBarItem(tabBarOptions)
this.hasSetLongTitle = !this.hasSetLongTitle
},
hideTabBarItem(){
let tabBarOptions = {
visible: true,
index: 1,
text: '接口',
iconPath: '/static/api.png',
selectedIconPath: '/static/apiHL.png'
} as SetTabBarItemOptions
if (!this.hasHiddenTabBarItem) {
tabBarOptions.visible = false;
} else {
tabBarOptions.visible = true;
}
uni.setTabBarItem(tabBarOptions)
this.hasHiddenTabBarItem = !this.hasHiddenTabBarItem
},
navigateBack() {
this.$emit('unmount')
},
......
......@@ -35,6 +35,7 @@
<button class="btn" type="button" data-action="reLaunch">reLaunch</button>
<button class="btn" type="button" data-action="switchTab">switchTab</button>
<button class="btn" type="button" data-action="getEnv">getEnv</button>
<!-- <button class="btn" type="button" data-action="getTheme">获取主题</button> -->
</div>
<p class="desc">网页向应用发送消息。注意:小程序端应用会在此页面后退时接收到消息。</p>
<div class="btn-list">
......@@ -88,6 +89,9 @@
})
});
break;
/* case 'getTheme':
alert(getThemePreference())
break; */
default:
uni.webView[action]({
url: '/pages/component/button/button'
......@@ -108,6 +112,15 @@
}
})
})
/* function getThemePreference() {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
return 'light';
}
return 'unknown';
} */
</script>
</body>
</html>
// 仅测试 console.log 时机问题
import './test-main-console.uts'
import App from './App.uvue'
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
// app.mixin({
......
......@@ -2,8 +2,8 @@
"name": "Hello uni-app x",
"appid": "__UNI__HelloUniAppX",
"description": "",
"versionName": "1.4",
"versionCode": 10400,
"versionName": "1.5",
"versionCode": 10500,
"uni-app-x": {},
/* 快应用特有相关 */
"quickapp": {},
......@@ -41,6 +41,10 @@
}
}
},
"uni-getLocation":{
"system":{},
"tencent":{}
},
"uni-ad": {
"gdt": {}
}
......@@ -64,7 +68,7 @@
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "TKUBZ-D24AF-GJ4JY-JDVM2-IBYKK-KEBCU"
"key" : ""
}
}
}
......
此差异已折叠。
const PAGE_PATH = '/pages/API/animation-frame/animation-frame'
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">FPS: {{FPSString}}</text>
<text class="frame-count">FrameCount: {{testFrameCount}}</text>
<text class="tips">提示: 在当前测试例子中,每增加一次调用 requestAnimationFrame 帧率翻倍,cancelAnimationFrame 后恢复</text>
</view>
</template>
<script>
export default {
data() {
return {
title: 'AnimationFrame',
taskId: 0,
FPSString: '- / -ms',
lastTime: 0,
frameCount: 0,
testFrameCount: 0
}
},
onUnload() {
if (this.taskId > 0) {
this.stopRequestAnimationFrame()
}
},
methods: {
startRequestAnimationFrame() {
this.taskId = requestAnimationFrame((timestamp : number) => {
this.updateFPS(timestamp)
this.testFrameCount++
this.startRequestAnimationFrame()
})
},
stopRequestAnimationFrame() {
cancelAnimationFrame(this.taskId)
this.lastTime = 0
this.frameCount = 0
this.FPSString = '- / -ms'
},
updateFPS(timestamp : number) {
this.frameCount++
if (timestamp - this.lastTime >= 1000) {
const timeOfFrame = (1000 / this.frameCount)
this.FPSString = `${this.frameCount} / ${timeOfFrame.toFixed(3)}ms`
this.frameCount = 0
this.lastTime = timestamp
}
}
}
}
</script>
<style>
.page {
padding: 15px;
}
.frame-count {
margin-top: 15px;
}
.tips {
font-size: 12px;
margin-top: 30px;
opacity: 0.7;
}
</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>
......@@ -18,6 +18,7 @@
</block>
</view>
<view class="uni-btn-v">
<view class="tips">注意:需要正确配置地图服务商的Key才能正常选择位置</view>
<button type="primary" @tap="chooseLocation">选择位置</button>
<button @tap="clear">清空</button>
</view>
......@@ -25,7 +26,7 @@
</view>
</template>
<script lang="uts">
function formatLocation(longitude, latitude) {
function formatLocation (longitude, latitude) {
if (typeof longitude === 'string' && typeof latitude === 'string') {
longitude = parseFloat(longitude)
latitude = parseFloat(latitude)
......@@ -38,7 +39,7 @@
}
}
export default {
data() {
data () {
return {
title: 'chooseLocation',
hasLocation: false,
......@@ -50,9 +51,9 @@
chooseLocation: function () {
uni.chooseLocation({
success: (res) => {
console.log(res,123)
this.hasLocation = true,
this.location = formatLocation(res.longitude, res.latitude),
console.log(res, 123)
this.hasLocation = true
this.location = formatLocation(res.longitude, res.latitude)
this.locationAddress = res.address
}
})
......@@ -69,4 +70,10 @@
padding-bottom: 0;
height: 440rpx;
}
.tips {
font-size: 12px;
margin-top: 15px;
opacity: .8;
}
</style>
......@@ -28,19 +28,21 @@
<script>
import { ItemType } from '@/components/enum-data/enum-data';
type Camera = "back" | "front"
type Source = "album" | "camera"
export default {
data() {
return {
title: "chooseVideo",
src: "",
sourceTypeItemTypes: [{ "value": 0, "name": "从相册中选择视频" }, { "value": 1, "name": "拍摄视频" }, { "value": 2, "name": "从相册中选择视频或拍摄视频" }] as ItemType[],
sourceTypeItems: [["album"], ["camera"], ["album", "camera"]],
sourceTypeItems: [["album"], ["camera"], ["album", "camera"]] as Source[][],
cameraItemTypes: [{ "value": 0, "name": "后置摄像头" }, { "value": 1, "name": "前置摄像头" }] as ItemType[],
cameraItems: ["back", "front"],
sourceType: ["album", "camera"],
cameraItems: ["back", "front"] as Camera[],
sourceType: ["album", "camera"] as Source[],
compressed: true,
maxDuration: 60,
camera: "back",
camera: "back" as Camera,
videoInfo: ""
}
},
......
<template>
<view>
<page-head :title="title"></page-head>
<view class="uni-padding-wrap">
<view class="uni-hello-text uni-center" style="padding-bottom:50rpx;">
旋转手机即可获取方位信息
</view>
<view class="direction">
<view class="bg-compass-line"></view>
<image class="bg-compass" src="../../../static/compass.png" :style="'transform: rotate('+direction+'deg)'"></image>
<view class="direction-value">
<text>{{direction}}</text>
<text class="direction-degree">o</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: 'onCompassChange',
direction: 0
}
},
onReady: function () {
uni.onCompassChange((res) => {
console.log('onCompassChange', res)
this.direction = res.direction
})
},
onUnload() {
uni.stopCompass();
this.direction = 0;
}
}
</script>
<style>
.direction {
position: relative;
margin-top: 70rpx;
display: flex;
width: 540rpx;
height: 540rpx;
align-items: center;
justify-content: center;
margin:0 auto;
}
.direction-value {
position: relative;
font-size: 200rpx;
color: #353535;
line-height: 1;
z-index: 1;
}
.direction-degree {
position: absolute;
top: 0;
right: -40rpx;
font-size: 60rpx;
}
.bg-compass {
position: absolute;
top: 0;
left: 0;
width: 540rpx;
height: 540rpx;
transition: .1s;
}
.bg-compass-line {
position: absolute;
left: 267rpx;
top: -10rpx;
width: 6rpx;
height: 56rpx;
background-color: #1AAD19;
border-radius: 999rpx;
z-index: 1;
}
</style>
......@@ -2,6 +2,7 @@
<!-- #ifdef APP -->
<scroll-view style="flex:1">
<!-- #endif -->
<view>
<page-head :title="title"></page-head>
<view class="uni-padding-wrap">
<view class="image-container">
......@@ -27,9 +28,8 @@
@confirm="onQualityConfirm"></input-data>
<input-data title="压缩后图片的宽度,单位px" type="string" @confirm="onCompressedWidthConfirm"></input-data>
<input-data title="压缩后图片的高度,单位px" type="string" @confirm="onCompressedHeightConfirm"></input-data>
<input-data defaultValue="auto" title="压缩后图片的宽度,支持px、%、auto" type="string" @confirm="onWidthConfirm"></input-data>
<input-data defaultValue="auto" title="压缩后图片的高度,支持px、%、auto" type="string" @confirm="onHeightConfirm"></input-data>
<input-data defaultValue="0" title="旋转度数,范围0~360" type="number" @confirm="onRotateConfirm"></input-data>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
......@@ -156,7 +156,7 @@
uni.getImageInfo({
src: res.tempFilePath,
success: (_res) => {
let beforeCompressSize: number, afterComoressSize: number;
let beforeCompressSize : number, afterComoressSize : number;
// #ifdef APP-ANDROID
beforeCompressSize = new FileInputStream(UTSAndroid.convert2AbsFullPath(this.imageSrcForTest)).available();
afterComoressSize = new FileInputStream(res.tempFilePath.substring("file://".length)).available();
......
......@@ -19,7 +19,7 @@ describe('API-compressVideo', () => {
if (process.env.uniTestPlatformInfo.startsWith('android')) {
const infos = process.env.uniTestPlatformInfo.split(' ');
const version = parseInt(infos[infos.length - 1]);
if (version == 5 || version == 7 || version == 10) return; // android5.1、android7、android10存在兼容问题,待修复
if (version == 5 || version == 7 || version == 9 || version == 10) return; // android5.1、android7、android9、android10存在兼容问题,待修复
expect(await page.data('videoInfoForTest')).toEqual({
width: 640,
height: 360,
......
......@@ -3,6 +3,7 @@
<scroll-view style="flex:1">
<!-- #endif -->
<page-head :title="title"></page-head>
<view>
<view class="uni-padding-wrap">
<video class="video" :src="beforeCompressPath" :controls="true"></video>
<view class="uni-title">
......@@ -26,6 +27,7 @@
<slider :min="0.1" :max="1" :step="0.1" :show-value="true" @change="onResolutionChange"></slider>
</view>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
......@@ -116,7 +118,7 @@
this.resolution = event.detail.value;
},
testCompressVideo() {
let beforeCompressSize: number, afterComoressSize: number;
let beforeCompressSize : number, afterComoressSize : number;
uni.compressVideo({
src: this.videoSrcForTest,
quality: 'medium',
......@@ -124,11 +126,11 @@
uni.getVideoInfo({
src: this.videoSrcForTest,
success: (_res) => {
beforeCompressSize = _res.size.toInt();
beforeCompressSize = Math.trunc(_res.size);
uni.getVideoInfo({
src: res.tempFilePath,
success: (__res) => {
afterComoressSize = __res.size.toInt();
afterComoressSize = Math.trunc(__res.size);
this.videoInfoForTest = {
"width": __res.width,
"height": __res.height,
......
......@@ -6,49 +6,46 @@ describe('inner-audio', () => {
return
}
beforeAll(async () => {
page = await program.reLaunch('/pages/API/inner-audio/inner-audio')
page = await program.reLaunch('/pages/API/create-inner-audio-context/create-inner-audio-context')
await page.waitFor('view');
});
function getData(key = '') {
return new Promise(async (resolve, reject) => {
const data = await page.data()
resolve(key ? data[key] : data)
})
}
it('onCanplay',async()=>{
await page.waitFor(1000)
await page.waitFor(async()=>{
return await getData('isCanplay')
return await page.data('isCanplay')
})
expect(await getData('buffered')).toBeGreaterThan(0)
expect(await page.data('buffered')).toBeGreaterThan(0)
})
it('play-onPlay-onTimeUpdate', async () => {
await page.callMethod('play')
await page.waitFor(3000);
expect(await getData('isPlaying')).toBeTruthy()
console.log("duration:",await getData('duration'),"currentTime:",await getData('currentTime'))
expect(await getData('duration')).toBeCloseTo(175.109, 0);
// console.log("isPaused",await getData('isPaused'))
// expect(await getData('currentTime')).toBeGreaterThan(0);
// expect(await getData('isPaused')).toBeFalsy();
const waitTime = process.env.uniTestPlatformInfo.includes('chrome') ? 5000:3000
await page.waitFor(waitTime)
expect(await page.data('isPlaying')).toBeTruthy()
console.log("duration:",await page.data('duration'),"currentTime:",await page.data('currentTime'))
expect(await page.data('duration')).toBeCloseTo(175.109, 0);
// console.log("isPaused",await page.data('isPaused'))
// expect(await page.data('currentTime')).toBeGreaterThan(0);
// expect(await page.data('isPaused')).toBeFalsy();
});
it('seek-onSeeking-onSeeked', async () => {
await page.callMethod('onchange',20)
await page.waitFor(500);
expect(await getData('onSeekingTest')).toBeTruthy();
// expect(await getData('onWaitingTest')).toBeTruthy();
expect(await getData('onSeekedTest')).toBeTruthy();
const waitTime = process.env.uniTestPlatformInfo.includes('chrome') ? 1500:500
await page.waitFor(waitTime)
console.log("seek-onSeeking-onSeeked:",await page.data())
expect(await page.data('onSeekingTest')).toBeTruthy();
// expect(await page.data('onWaitingTest')).toBeTruthy();
// expect(await page.data('onSeekedTest')).toBeTruthy();
expect(await program.screenshot()).toSaveImageSnapshot();
});
it('pause-onPause', async () => {
await page.callMethod('pause')
await page.waitFor(500);
expect(await getData('isPlaying')).toBeFalsy()
// expect(await getData('isPaused')).toBeTruthy();
expect(await page.data('isPlaying')).toBeFalsy()
// expect(await page.data('isPaused')).toBeTruthy();
});
it('stop-onStop', async () => {
......@@ -58,8 +55,8 @@ describe('inner-audio', () => {
await page.callMethod('stop')
await page.callMethod('stop')
await page.waitFor(1000);
expect(await getData('isPlaying')).toBeFalsy()
// expect(await getData('isPaused')).toBeTruthy();
expect(await page.data('isPlaying')).toBeFalsy()
// expect(await page.data('isPaused')).toBeTruthy();
});
it('onEnded', async () => {
......@@ -67,7 +64,7 @@ describe('inner-audio', () => {
await page.waitFor(500);
await page.callMethod('play')
await page.waitFor(3000);
// expect(await getData('isPlayEnd')).toBeTruthy();
// expect(await page.data('isPlayEnd')).toBeTruthy();
});
});
......@@ -32,10 +32,10 @@
<view class="uni-title">
<text class="uni-title-text">格式/路径示例</text>
</View>
<navigator url="/pages/API/inner-audio/inner-audio-format" class="uni-btn">
<navigator url="/pages/API/create-inner-audio-context/inner-audio-format" class="uni-btn">
<button type="primary" @click="pause">音频格式示例</button>
</navigator>
<navigator url="/pages/API/inner-audio/inner-audio-path" class="uni-btn uni-common-mb">
<navigator url="/pages/API/create-inner-audio-context/inner-audio-path" class="uni-btn uni-common-mb">
<button type="primary" @click="pause">音频路径示例</button>
</navigator>
</view>
......@@ -58,9 +58,9 @@
_isChanging: false,
_audioContext: null as InnerAudioContext | null,
// 自动化测试
onSeekingTest:false,
onSeekedTest:false,
onWaitingTest:false
onSeekingTest: false,
onSeekedTest: false,
onWaitingTest: false
}
},
computed: {
......@@ -129,7 +129,7 @@
}
this._audioContext!.onPlay(() => {
this.isPaused = false;
console.log('开始播放',this.isPaused);
console.log('开始播放', this.isPaused);
});
this.onTimeUpdate()
this.onWaiting()
......@@ -159,6 +159,7 @@
// console.log('onTimeUpdate:音频播放进度更新事件,currentTime',this._audioContext!.currentTime);
if (this._isChanging === true) { return; }
this.currentTime = this._audioContext!.currentTime || 0;
console.log('currentTime', this.currentTime);
if (this.currentTime > this.buffered) {
console.log('缓冲不足');
}
......@@ -208,7 +209,7 @@
console.log('音频停止事件');
});
this.isPlaying = false;
console.log('stop',this.isPaused);
console.log('stop', this.isPaused);
}
}
}
......
const PAGE_PATH = '/pages/API/nodes-info/nodes-info'
const PAGE_PATH = '/pages/API/create-selector-query/create-selector-query'
const RECT_LEFT = 15;
const RECT_WIDTH = 150;
......@@ -57,9 +57,22 @@ describe('nodes-info', () => {
it('get-node-info-child', async () => {
const child = await page.$('.node-child')
const childData = await child.data()
console.log('get-node-info-child.childData.top', childData.top);
expect(childData.top > 100).toBe(true)
})
it('multi-child', async () => {
const pageData = await page.data()
expect(pageData.selectCount).toBe(1)
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 () => {
......@@ -68,6 +81,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) {
......
<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>
......@@ -36,11 +39,28 @@
</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>
<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>
import nodeChild from './nodes-info-child.uvue'
import multiChild from './selector-query-child-multi.uvue'
type NodeInfoType = {
left : number | null,
......@@ -53,7 +73,8 @@
export default {
components: {
nodeChild
nodeChild,
multiChild
},
data() {
return {
......@@ -63,8 +84,21 @@
rootNodeInfo: null as NodeInfoType | null,
//供自动化测试使用
// resizeRectValid: false
// TODO
selectCount: 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
this.testFields()
this.testNode()
},
onResize() {
//供自动化测试使用
/* var rect12Element = uni.getElementById("rect-1-2")
......@@ -122,7 +156,32 @@
} 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>
......@@ -130,7 +189,6 @@
<style>
.page {
padding: 15px;
flex: 1;
}
.btn {
......
<template>
<view class="selector-query-view">
<text>selector-query</text>
<text class="text red">{{text1}}</text>
</view>
<view class="selector-query-view">
<text>selector-query</text>
<text class="text green">{{text2}}</text>
</view>
<view v-if="text1.length>0">1</view>
<text>{{text3}}</text>
</template>
<script>
export default {
data() {
return {
text1: "",
text2: "",
text3: "test-text-node",
viewCount: 0,
selectCount: 0,
selectAllCount: 0,
show: false
}
},
mounted() {
uni.createSelectorQuery().in(this).select('.selector-query-view').boundingClientRect().exec((ret) => {
this.text1 = JSON.stringify(ret, null, 2)
if (ret.length == 1) {
this.selectCount = ret.length
}
})
uni.createSelectorQuery().in(this).selectAll('.selector-query-view').boundingClientRect().exec((ret) => {
this.text2 = JSON.stringify(ret, null, 2)
if (ret.length == 1) {
this.selectAllCount = (ret[0] as NodeInfo[]).length
}
})
}
}
</script>
<style>
.green {
border: 3px solid green;
}
.red {
border: 3px solid red;
}
.view {
border: 3px dashed lime;
padding: 10px;
}
.text {
margin-top: 20px;
padding: 5px;
}
</style>
......@@ -55,11 +55,13 @@
success: (res) => {
console.log('downloadFile success, res is', res.tempFilePath)
self.imageSrc = res.tempFilePath;
uni.hideLoading();
},
fail: (err) => {
console.log('downloadFile fail, err is:', err)
},
complete: (res) => {
uni.hideLoading();
this.task = null;
}
});
this.task?.onProgressUpdate((update) => {
......@@ -140,11 +142,9 @@
// #ifdef APP
testInovkeDownloadFile({
success:(res: any)=>{
console.log("success :", res);
this.jest_result = true
},
fail:(err: any)=>{
console.log("fail :", err);
this.jest_result = false
}
} as CommonOptions)
......@@ -155,7 +155,6 @@
url: "https://web-ext-storage.dcloud.net.cn/hello-uni-app-x/1789834995055525889-你好%23你好.png",
success: (res: DownloadFileSuccess) => {
this.jest_result = true;
console.log("res :", res);
},
fail: () => {
this.jest_result = false;
......
......@@ -32,9 +32,9 @@
},
methods: {
geAbsPath(path ?: Any) {
geAbsPath(path ?: any) {
// #ifdef APP-ANDROID
this.log += UTSAndroid.convert2AbsFullPath(path as String) + '\n'
this.log += UTSAndroid.convert2AbsFullPath(path as string) + '\n'
// #endif
}
......
......@@ -52,6 +52,14 @@ describe('event-bus', () => {
expect(l3).toBe(0)
})
it('emit object params', async () => {
await page.callMethod('onObj')
await page.callMethod('emitWithObj')
const objArg = await page.data('objArg')
expect(objArg.a).toBe(1)
expect(objArg.b).toBe(2)
})
it('off-all', async () => {
await page.callMethod('clear')
await page.callMethod('on')
......
......@@ -14,6 +14,12 @@
<view>
<view v-for="(item, index) in log" :key="index">{{ item }}</view>
</view>
<button @click="onObj">开始监听 obj 参数</button>
<button @click="emitWithObj">触发监听 obj 参数</button>
<view class="box">
<text>接收到的 obj 参数:</text>
<text>{{JSON.stringify(objArg)}}</text>
</view>
</view>
</view>
<!-- #ifdef APP -->
......@@ -26,6 +32,7 @@
data() {
return {
log: [] as string[],
objArg: {},
}
},
methods: {
......@@ -41,6 +48,11 @@
on2() {
uni.$on('test', this.fn2)
},
onObj() {
uni.$on('test-obj', (res: UTSJSONObject) => {
this.objArg = res
})
},
once() {
uni.$once('test', this.fn)
},
......@@ -53,6 +65,9 @@
emit() {
uni.$emit('test', 'msg:' + Date.now())
},
emitWithObj() {
uni.$emit('test-obj', { a: 1, b: 2 })
},
clear() {
this.log.length = 0
},
......
......@@ -21,6 +21,15 @@ describe('ExtApi-GetAppAuthorizeSetting', () => {
await page.waitFor(600);
res = await uni.getAppAuthorizeSetting();
});
it('Check albumAuthorized', async () => {
expect(commonSupportList).toContain(res.albumAuthorized)
});
it('Check bluetoothAuthorized', async () => {
expect(commonSupportList).toContain(res.bluetoothAuthorized)
});
it('Check cameraAuthorized', async () => {
expect(commonSupportList).toContain(res.cameraAuthorized)
});
......
......@@ -2,7 +2,6 @@
<page-head :title="title"></page-head>
<view class="uni-common-mt">
<view class="uni-list">
<!-- #ifdef APP-IOS -->
<view class="uni-list-cell">
<view class="uni-pd">
<view class="uni-label" style="width:180px;">是否授权使用相册</view>
......@@ -19,7 +18,6 @@
<input type="text" :disabled="true" placeholder="未获取" :value="bluetoothAuthorized" />
</view>
</view>
<!-- #endif -->
<view class="uni-list-cell">
<view class="uni-pd">
<view class="uni-label" style="width:180px;">是否授权使用摄像头</view>
......@@ -118,6 +116,8 @@
methods: {
getAppAuthorizeSetting: function () {
const res = uni.getAppAuthorizeSetting();
this.albumAuthorized = res.albumAuthorized;
this.bluetoothAuthorized = res.bluetoothAuthorized;
this.cameraAuthorized = res.cameraAuthorized;
this.locationAuthorized = res.locationAuthorized;
this.locationAccuracy = res.locationAccuracy ?? "unsupported";
......@@ -127,8 +127,6 @@
this.notificationAlertAuthorized = res.notificationAlertAuthorized;
this.notificationBadgeAuthorized = res.notificationBadgeAuthorized;
this.notificationSoundAuthorized = res.notificationSoundAuthorized;
this.bluetoothAuthorized = res.bluetoothAuthorized;
this.albumAuthorized = res.albumAuthorized;
// #endif
}
......
......@@ -2,6 +2,7 @@
<!-- #ifdef APP -->
<scroll-view class="page-scroll-view">
<!-- #endif -->
<view>
<page-head title="getCurrentPages"></page-head>
<view class="uni-padding-wrap">
<button @click="_getCurrentPages">getCurrentPages</button>
......@@ -31,11 +32,14 @@
</view>
<view class="set-value" v-else-if="item.type == 'string'">
<radio-group class="radio-set-value" @change="radioChange(item.key, $event as RadioGroupChangeEvent)">
<radio class="radio-value" v-for="(item2, index2) in item.value" :key="index2" :value="item2" >{{item2}}</radio>
<radio class="radio-value" v-for="(item2, index2) in item.value" :key="index2" :value="item2">{{item2}}
</radio>
</radio-group>
</view>
</view>
</template>
<button style='margin: 10px;' @click="goSetDisablePullDownRefresh">go set disable pullDownRefresh</button>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
......@@ -117,6 +121,11 @@
const currentPage = pages[pages.length - 1];
currentPage.$setPageStyle(style);
},
goSetDisablePullDownRefresh() {
uni.navigateTo({
url: '/pages/API/get-current-pages/set-page-style-disable-pull-down-refresh'
});
}
// getCurrentPage(): Page {
// const pages = getCurrentPages();
// const currentPage = pages[pages.length - 1];
......
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>
......@@ -1306,6 +1306,12 @@ describe('ExtApi-FileManagerTest', () => {
await btnWrite.tap()
await isDone()
let bytesWritten = await getData("bytesWritten")
let lastFailError = await getData("lastFailError")
if(bytesWritten != 7){
let writeData = await getData("writeData")
console.log('writeTest',lastFailError.errCode,lastFailError.errMsg,bytesWritten,writeData)
}
expect(bytesWritten).toEqual(7)
console.log('writeTest', '2')
//writeSyncTest
......
......@@ -1092,13 +1092,14 @@
}
console.log("success", res)
this.bytesWritten = res.bytesWritten
this.lastFailError=new UniError('uni-fileSystemManager', 0,'writeTest success:'+ JSON.stringify(res))
},
fail: (res : IUniError) => {
if (this.logAble) {
this.log += 'writeTest fail:' + JSON.stringify(res) + '\n\n'
}
console.log('fail', res)
this.lastFailError=new UniError(res.errSubject, res.errCode, res.errMsg)
this.lastFailError=new UniError(res.errSubject, res.errCode,'writeTest:'+ res.errMsg)
},
complete: (_) => {
......
......@@ -8,4 +8,10 @@ describe('getLaunchOptionsSync', () => {
const data = await page.data()
expect(data.checked).toBe(true)
})
it('app onLaunch 和 getLaunchOptionsSync 结果一致', async () => {
const page = await program.navigateTo(PAGE_PATH)
await page.waitFor('view')
const pageData = await page.data()
expect(pageData.testResult).toBe(true)
})
})
......@@ -2,31 +2,53 @@
<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>
<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 lang="uts">
export default {
<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/get-location/get-location";
const platformInfo = process.env.uniTestPlatformInfo.toLocaleLowerCase()
const isAndroid = platformInfo.startsWith('android')
const isIos = platformInfo.startsWith('ios')
const isApp = isAndroid || isIos
const isWeb = platformInfo.startsWith('web')
describe("get-location", () => {
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor(600)
});
//system 定位
it("system+type=wgs84+success", async () => {
await page.setData({
jest_provider: 'system',
jest_type: 'wgs84',
jest_isAltitude: true,
jest_isGeocode: false,
jest_isHighAccuracy: false
})
await page.callMethod('jestGetLocation')
await page.waitFor(async () => {
return await page.data('jest_complete') === true;
});
const data = await page.data()
const jest_errCode = data['jest_errCode']
if (jest_errCode > 0) {
expect((await page.data())['jest_errCode']).toEqual(expect.any(Number));
} else {
//判断经纬度是否在正常范围
expect((await page.data())['jest_longitude']).toBeGreaterThanOrEqual(-180);
expect((await page.data())['jest_longitude']).toBeLessThanOrEqual(180);
expect((await page.data())['jest_latitude']).toBeGreaterThanOrEqual(-90);
expect((await page.data())['jest_latitude']).toBeLessThanOrEqual(90);
//判断海拔是否正确
expect((await page.data())['jest_altitude']).toEqual(expect.any(Number));
}
});
//system 定位
it("system+type=wgs84+success+geocode=true", async () => {
await page.setData({
jest_provider: 'system',
jest_type: 'wgs84',
jest_isAltitude: true,
jest_isGeocode: true,
jest_isHighAccuracy: false
})
await page.callMethod('jestGetLocation')
await page.waitFor(async () => {
return await page.data('jest_complete') === true;
});
const data = await page.data()
const jest_errCode = data['jest_errCode']
if (jest_errCode > 0) {
if (isIos) {
expect((await page.data())['jest_errCode']).toEqual(1505603);
} else if (isAndroid) {
expect((await page.data())['jest_errCode']).toEqual(1505700);
} else {
expect((await page.data())['jest_errCode']).toEqual(expect.any(Number));
}
}
});
//system 定位
it("system+type=wgs84+success+altitude=false", async () => {
await page.setData({
jest_provider: 'system',
jest_type: 'wgs84',
jest_isAltitude: false,
jest_isGeocode: true,
jest_isHighAccuracy: false
})
await page.callMethod('jestGetLocation')
await page.waitFor(async () => {
return await page.data('jest_complete') === true;
});
const data = await page.data()
const jest_errCode = data['jest_errCode']
if (jest_errCode > 0) {
//如果定位出错
expect((await page.data())['jest_errCode']).toEqual(expect.any(Number));
} else {
expect((await page.data())['jest_altitude']).toEqual(0);
}
});
//system 定位
it("system+type=gcj02+fail", async () => {
await page.setData({
jest_provider: 'system',
jest_type: 'gcj02',
jest_isAltitude: true,
jest_isGeocode: true,
jest_isHighAccuracy: false
})
await page.callMethod('jestGetLocation')
await page.waitFor(async () => {
return await page.data('jest_complete') === true;
});
if (isApp) {
expect((await page.data())['jest_errCode']).toEqual(1505601);
}
});
//tencent 定位
it("tencent+type=gcj02+success", async () => {
await page.setData({
jest_provider: 'tencent',
jest_type: 'gcj02',
jest_isAltitude: true,
jest_isGeocode: true,
jest_isHighAccuracy: true
})
await page.callMethod('jestGetLocation')
await page.waitFor(async () => {
return await page.data('jest_complete') === true;
});
const data = await page.data()
const jest_errCode = data['jest_errCode']
if (jest_errCode > 0) {
//如果定位出错
expect((await page.data())['jest_errCode']).toEqual(expect.any(Number));
} else {
//判断逆地理编码是否正确
expect((await page.data())['jest_address']).toEqual(expect.any(String));
//判断经纬度是否在正常范围
expect((await page.data())['jest_longitude']).toBeGreaterThanOrEqual(-180);
expect((await page.data())['jest_longitude']).toBeLessThanOrEqual(180);
expect((await page.data())['jest_latitude']).toBeGreaterThanOrEqual(-90);
expect((await page.data())['jest_latitude']).toBeLessThanOrEqual(90);
//判断海拔是否正确
expect((await page.data())['jest_altitude']).toEqual(expect.any(Number));
}
});
//tencent 定位
it("tencent+type=wgs84+fail", async () => {
await page.setData({
jest_provider: 'tencent',
jest_type: 'wgs84',
jest_isAltitude: true,
jest_isGeocode: true,
jest_isHighAccuracy: true
})
await page.callMethod('jestGetLocation')
await page.waitFor(async () => {
return await page.data('jest_complete') === true;
});
if (isApp) {
expect((await page.data())['jest_errCode']).toEqual(1505607);
}
});
});
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1;">
<!-- #endif -->
<page-head :title="title"></page-head>
<view style="padding: 4px;">
<view style="padding: 4px">
<text class="hello-text">
定位功能默认调用操作系统定位API实现。\n
部分手机因gms兼容不好可能导致无法定位。\n
gcj国标、逆地理信息等功能需三方sdk定位。如果需要类似能力可以下载腾讯定位插件,打包自定义基座。参考示例:</text>
<u-link :href="'https://ext.dcloud.net.cn/plugin?id=14569'" :text="'https://ext.dcloud.net.cn/plugin?id=14569'" :inWhiteList="true"></u-link>
定位功能默认调用操作系统定位API实现, 也支持腾讯定位。\n
部分手机因gms兼容不好可能导致无法使用系统定位, gcj国标、逆地理信息等功能需调用内置腾讯定位。</text>
</view>
<view class="uni-padding-wrap uni-common-mt">
<!-- #ifdef APP -->
<view class="uni-list-cell-db">定位服务商provider(如系统定位,腾讯定位等)</view>
<view class="uni-list" style="margin-bottom: 20px">
<radio-group @change="radioChangePV">
<radio class="uni-list-cell uni-list-cell-pd" v-for="(item, index) in providerList" :key="item.id"
:class="index < providerList.length - 1 ? 'uni-list-cell-line' : ''" :value="item.id"
:checked="index === currentProvider">
{{ item.name }}
</radio>
</radio-group>
</view>
<!-- #endif -->
<view class="uni-list-cell-db">定位类型</view>
<view class="uni-list">
<radio-group @change="radioChange">
<radio class="uni-list-cell uni-list-cell-pd" v-for="(item, index) in items" :key="item.value"
:class="index < items.length - 1 ? 'uni-list-cell-line': ''" :value="item.value"
:class="index < items.length - 1 ? 'uni-list-cell-line' : ''" :value="item.value"
:checked="index === current">
{{item.name}}
{{ item.name }}
</radio>
</radio-group>
</view>
<view class="uni-list-cell uni-list-cell-pd">
<view class="uni-list-cell uni-list-cell-pd" style="margin-top: 20px">
<view class="uni-list-cell-db">高度信息</view>
<switch :checked="altitudeSelect" @change="altitudeChange" />
</view>
......@@ -30,17 +44,22 @@
<view class="uni-list-cell-db">是否解析地址信息</view>
<switch :checked="geocodeSelect" @change="geocodeChange" />
</view>
<text>{{exeRet}}</text>
<text>{{ exeRet }}</text>
<view class="uni-btn-v">
<button class="uni-btn" type="default" @tap="getLocationTap">获取定位</button>
<button class="uni-btn" type="default" @tap="getLocationTap">
获取定位
</button>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
type ItemType = {
value : 'wgs84' | 'gcj02',
name : string,
}
type GetLocationType = 'wgs84' | 'gcj02'
export type LocationItem = { id : string, name : string, provider ?: UniProvider }
export type ItemType = { value : GetLocationType, name : GetLocationType }
export default {
data() {
return {
......@@ -59,10 +78,56 @@
name: 'gcj02'
}
] as ItemType[],
providerList: [] as LocationItem[],
current: 0,
currentProvider: 0,
jest_provider: '',
jest_type: 'wgs84' as GetLocationType,
jest_isAltitude: false,
jest_isGeocode: false,
jest_isHighAccuracy: false,
jest_altitude: -1000,
jest_longitude: 200,
jest_latitude: 100,
jest_address: '',
jest_errCode: 0,
jest_complete: false
}
},
onLoad: function () {
// #ifdef APP
this.getProvider()
// #endif
},
methods: {
getProvider() {
// #ifdef APP
let provider = uni.getProviderSync({
service: "location",
} as GetProviderSyncOptions)
console.log(provider)
provider.providerObjects.forEach((value : UniProvider) => {
var currentProvider = value
if (value.id == 'system') {
currentProvider = value as UniLocationSystemProvider
} else if (value.id == 'tencent') {
currentProvider = value as UniLocationTencentProvider
}
this.providerList.push({
name: currentProvider.description,
id: currentProvider.id,
provider: currentProvider
} as LocationItem);
})
this.providerList.forEach((value, index) => {
if (value.id == "system") {
this.currentProvider = index
}
})
// #endif
},
altitudeChange: function (e : UniSwitchChangeEvent) {
this.altitudeSelect = e.detail.value
},
......@@ -80,35 +145,83 @@
}
}
},
radioChangePV(e : UniRadioGroupChangeEvent) {
for (let i = 0; i < this.providerList.length; i++) {
if (this.providerList[i].id === e.detail.value) {
this.currentProvider = i;
break;
}
}
if (e.detail.value == "system") {
this.current = 0
} else if (e.detail.value == "tencent") {
this.current = 1
}
},
getLocationTap: function () {
// #ifdef APP
if (this.providerList.length == 0) {
uni.showToast({
title: '未获取到provider,请确定基座中包含location功能',
icon: "error"
})
console.log("未获取到provider,请确定基座中包含location功能")
return
}
// #endif
uni.showLoading({
title: '定位中'
})
uni.getLocation(({
// #ifdef APP
provider: this.providerList[this.currentProvider].id,
// #endif
type: this.items[this.current].value,
altitude: this.altitudeSelect,
isHighAccuracy: this.isHighAccuracySelect,
geocode: this.geocodeSelect,
success: (res : any) => {
uni.hideLoading()
console.log(res);
this.exeRet = JSON.stringify(res)
},
fail: (res : any) => {
uni.hideLoading()
console.log(res);
this.exeRet = JSON.stringify(res)
},
complete: (res : any) => {
uni.hideLoading()
console.log(res);
this.exeRet = JSON.stringify(res)
}
}));
},
// 仅用于自动化测试
jestGetLocation() {
this.jest_complete = false
this.jest_errCode = 0
uni.getLocation(({
// #ifdef APP
provider: this.jest_provider,
// #endif
type: this.jest_type,
altitude: this.jest_isAltitude,
isHighAccuracy: this.jest_isHighAccuracy,
geocode: this.jest_isGeocode,
success: (res) => {
if (res.address != null) {
this.jest_address = res.address!
}
this.jest_longitude = res.longitude
this.jest_latitude = res.latitude
this.jest_altitude = res.altitude
this.jest_complete = true
},
fail: (err) => {
this.jest_errCode = err.errCode
this.jest_complete = true
}
}));
}
}
}
</script>
describe('get-native-view', () => {
if (process.env.uniTestPlatformInfo.indexOf('web') > -1 || process.env.UNI_AUTOMATOR_APP_WEBVIEW == 'true') {
it('object', () => {
expect(1).toBe(1)
})
return
}
const platformInfo = process.env.uniTestPlatformInfo.toLocaleLowerCase()
if (
platformInfo.indexOf('14.5') != -1 ||
platformInfo.indexOf('13.7') != -1 ||
platformInfo.indexOf('12.4') != -1
) {
// TODO: 排查 ios 不兼容版本 测试异常原因
it('14.5 13.7 12.4 测试异常', () => {
expect(1).toBe(1)
})
return
}
let page
beforeAll(async () => {
page = await program.reLaunch('/pages/API/get-native-view/element-getnativeview')
await page.waitFor('web-view')
})
//检测view标签原生View是否匹配
it('check_view_native_view', async () => {
page.waitFor(100)
const value = await page.callMethod('checkViewNativeView')
expect(value).toBe(true)
})
//检测input标签原生View是否匹配
it('check_input_native_view', async () => {
page.waitFor(100)
const value = await page.callMethod('checkInputNativeView')
expect(value).toBe(true)
})
//检测textarea标签原生View是否匹配
it('check_textarea_native_view', async () => {
page.waitFor(100)
const value = await page.callMethod('checkTextareaNativeView')
expect(value).toBe(true)
})
//检测webview标签原生View是否匹配
it('check_web_view_native_view', async () => {
page.waitFor(100)
const value = await page.callMethod('checkWebViewNativeView')
expect(value).toBe(true)
})
})
此差异已折叠。
......@@ -63,4 +63,15 @@ describe('ExtApi-GetSystemInfo', () => {
expect(`${key} not null: ${res[key] != null}`).toBe(`${key} not null: true`)
}
})
it('Check screenHeight at different stages', async ()=> {
console.log("deviceOrientation ", res["deviceOrientation"]);
if(res["deviceOrientation"] == "landscape"){
expect(1).toBe(1)
}else{
await page.callMethod('jest_getScreenHeight_at_different_stages')
const res = await page.data('jest_result');
expect(res).toBe(true)
}
})
});
......@@ -59,7 +59,7 @@
this.videoInfoForTest = {
"orientation": res.orientation,
"type": res.type,
"duration": res.duration.toInt(),
"duration": Math.trunc(res.duration),
"size": res.size,
"width": res.width,
"height": res.height,
......
......@@ -56,4 +56,23 @@ describe('interceptor', () => {
expect(currentPage.path).toBe('pages/API/interceptor/page1')
await program.navigateBack()
})
it('addSwitchTabInterceptor', async () => {
await page.callMethod('addSwitchTabInterceptor')
await page.callMethod('switchTab')
await page.waitFor(300)
const currentPage = await program.currentPage()
expect(currentPage.path).toBe('pages/tabBar/API')
})
it('removeSwitchTabInterceptor', async () => {
const currentPage1 = await program.navigateTo(PAGE_PATH)
await currentPage1.callMethod('addSwitchTabInterceptor')
await currentPage1.callMethod('removeSwitchTabInterceptor')
await currentPage1.callMethod('switchTab')
await page.waitFor(300)
const currentPage2 = await program.currentPage()
expect(currentPage2.path).toBe('pages/tabBar/component')
})
})
此差异已折叠。
此差异已折叠。
......@@ -11,6 +11,13 @@ describe("payment", () => {
return
}
if (process.env.UNI_TEST_DEVICES_DIRECTION == 'landscape') {
it('跳过横屏模式', () => {
expect(1).toBe(1)
})
return
}
it("trigger pulldown refresh by swipe", async () => {
const page = await program.navigateTo(PAGE_PATH)
await page.waitFor('view')
......
此差异已折叠。
此差异已折叠。
......@@ -6,7 +6,7 @@ describe('API-loading', () => {
const isApp = process.env.UNI_OS_NAME === "android" || process.env.UNI_OS_NAME === "ios";
beforeAll(async () => {
page = await program.reLaunch('/pages/API/action-sheet/action-sheet')
page = await program.reLaunch('/pages/API/show-action-sheet/show-action-sheet')
await page.waitFor(500);
});
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
const PAGE_PATH = '/pages/API/unicloud-call-function/unicloud-call-function'
const PAGE_PATH = '/pages/API/unicloud/unicloud/cloud-function'
describe('unicloud-call-function', () => {
let page
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册