From e625cf2007c260800b6da047e9db81ad9835ddba Mon Sep 17 00:00:00 2001
From: wuya_smile
Date: Thu, 14 Mar 2024 11:37:49 +0800
Subject: [PATCH 1/3] =?UTF-8?q?[Issues:=20#I98F1V]=20=E6=B7=BB=E5=8A=A0rea?=
=?UTF-8?q?ct-native-image-zoom=E6=93=8D=E4=BD=9C=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
1224/react-native-image-zoom.md | 341 ++++++++++++++++++++++++++++++++
1 file changed, 341 insertions(+)
create mode 100644 1224/react-native-image-zoom.md
diff --git a/1224/react-native-image-zoom.md b/1224/react-native-image-zoom.md
new file mode 100644
index 00000000..35d55252
--- /dev/null
+++ b/1224/react-native-image-zoom.md
@@ -0,0 +1,341 @@
+模板版本:v0.1.3
+
+
+
react-native-image-zoom
+
+
+
+
+
+
+
+
+
+
+> [!TIP] [Github 地址](https://github.com/ascoders/react-native-image-zoom)
+
+## 安装与使用
+
+进入到工程目录并输入以下命令:
+
+
+
+#### **npm**
+
+```bash
+npm i react-native-image-pan-zoom --save
+```
+
+#### **yarn**
+
+```bash
+yarn add react-native-image-pan-zoom
+```
+
+
+
+下面的代码展示了这个库的基本使用场景:
+
+```tsx
+import React, { useState } from 'react';
+import { Button, Dimensions, Image, LayoutChangeEvent, StyleSheet, Switch, Text, View } from 'react-native';
+import ImageZoom from 'react-native-image-pan-zoom'
+import type { IOnClick, IOnMove, ICenterOn } from 'react-native-image-pan-zoom';
+
+const formatEventData = (evt: IOnClick) => {
+ const { locationX, locationY, pageX, pageY } = evt;
+ return `x ${locationX.toFixed(2)} y ${locationY.toFixed(2)} pageX ${pageX.toFixed(2)} pageY ${pageY.toFixed(2)}`;
+}
+
+const ImageZoomDemo = () => {
+ //长按
+ const [longPressData, setLongPressData] = useState("LongPress: Haven't long pressed yet");
+ const longPressHandler = (evt: IOnClick) => {
+ const data = formatEventData(evt);
+ setLongPressData(`LongPress: ${data}`);
+ }
+ const [longPressTime, setLongPressTime] = useState(800);
+
+ // 双击
+ const [doubleClickData, setDoubleClickData] = useState("DoubleClick: Haven't doubleclicked yet");
+ const [isEnabled, setIsEnabled] = useState(false);
+ const toggleDoubleClickSwitch = () => setIsEnabled(previousState => !previousState);
+ const doubleClickHandler = (evt: IOnClick) => {
+ setDoubleClickData(`DoubleClick: x: ${evt.locationX.toFixed(2)} y: ${evt.locationY.toFixed(2)}}`);
+ }
+ const [clickInterval, setClickInterval] = useState(175);
+
+ // 单击
+ const [singleClickData, setSingleClickData] = useState("SingleClick: Haven't singleClicked yet");
+ const singleClickHandler = (evt: IOnClick) => {
+ setSingleClickData(`SingleClick: x: ${evt.locationX.toFixed(2)} y: ${evt.locationY.toFixed(2)}`);
+ }
+
+ // 下滑
+ const [swiperDownData, setSwiperDownData] = useState("SwiperDown: Haven't swipe down yet");
+ const [enableSwiperDown, setSwiperDownEnabled] = useState(false);
+ const [downThreshold, setDownThreshold] = useState(230);
+ const swiperDownHandler = () => {
+ setSwiperDownData('SwiperDown: already swipe down');
+ }
+ const toggleSwipeDownSwitch = () => {
+ setSwiperDownEnabled(previousState => !previousState);
+ }
+
+ // 移动
+ const [moveData, setMoveData] = useState("Move: Haven't move yet");
+ const moveHandler = (data: IOnMove) => {
+ const { type, positionX, positionY, scale, zoomCurrentDistance } = data;
+ setMoveData(`Move: type:${type} positionX:${positionX.toFixed(2)} positionY:${positionY.toFixed(2)} scale:${scale} zoomCurrentDistance:${zoomCurrentDistance}`);
+ }
+
+ // 窗口和图片尺寸
+ const winWidth = Dimensions.get('window').width;
+ const winHeight = winWidth;
+ const [zoomWidth, setZoomWidth] = useState(winWidth);
+ const [zoomHeight, setZoomHeight] = useState(winHeight);
+ const defaultViewData = `Current view data:width:${zoomWidth.toFixed(2)}, height:${zoomHeight.toFixed(2)}`;
+ const [viewData, setViewData] = useState(defaultViewData);
+ const toggleAdd = () => {
+ setZoomWidth(winWidth);
+ setZoomHeight(winHeight);
+ setViewData(getViewData(winWidth, winHeight));
+ }
+ const toggleDecrease = () => {
+ setZoomWidth(winWidth * 0.8);
+ setZoomHeight(winHeight * 0.8);
+ setViewData(getViewData(winWidth * 0.8, winHeight * 0.8));
+ }
+ const getViewData = (width: number, height: number) => {
+ return `Current view data:width: ${width.toFixed(2)} height: ${height.toFixed(2)}`
+ }
+
+ // 双指缩放
+ const [isEnablePinchToZoom, setPinchToZoomState] = useState(false);
+ const togglePinchToZoomSwitch = () => {
+ setPinchToZoomState(previousState => previousState);
+ }
+ const [minScale, setMinScale] = useState(0.6);
+ const [maxScale, setMaxScale] = useState(10);
+
+ // 单指移动
+ const [isEnablePanToMove, setPanToMoveState] = useState(false);
+ const [singleClickDistance, setSingleClickDistance] = useState(10);
+ const togglePanToMoveSwitch = () => {
+ setPanToMoveState(previousState => !previousState);
+ }
+ const [maxOverflow, setMaxOverFlow] = useState(100);
+
+ // 自定义中心
+ const defaultCenter: ICenterOn = {
+ x: 0,
+ y: 0,
+ scale: 1,
+ duration: 300
+ }
+ const [centerData, setCenterDatat] = useState((): ICenterOn | undefined => undefined);
+ const leftCenter: ICenterOn = {
+ x: 100,
+ y: 100,
+ scale: 1,
+ duration: 300
+ }
+ const toggleSettingCenter = (val: boolean) => {
+ if (val) {
+ setCenterDatat(leftCenter);
+ } else {
+ setCenterDatat(defaultCenter);
+ }
+ }
+
+ // 中心焦点
+ const [enableCenterFocus, setEnterFocusIsEnable] = useState(false);
+
+ // 动画驱动
+ const [enableNativeDriver, setNativeDriverIsEnable] = useState(false);
+
+ // 禁用安卓渲染
+ const [enableAndroidRender, setAndroidRenderEnable] = useState(false);
+
+ // 触发想切换到左边的图,向左滑动速度超出阈值触发
+ const dragLeftHandler = () => {
+ console.log('dragLeftHandler!!!');
+ }
+
+ // 松手但是没有取消看图
+ const responderReleaseHandler = (vx: number, scale: number) => {
+ setViewData(`ResponderRelease: scale: ${scale}`);
+ }
+
+ // 成为响应者
+ const startPanResponderHandler = () => {
+ console.log('startPanResponderHandler!!!');
+ return true;
+ }
+ const movePanResponderHandler = () => {
+ console.log('movePanResponderHandler!!!');
+ return false;
+ }
+ const terminationRequestHandler = () => {
+ console.log('movePanResponderHandler!!!');
+ return false;
+ }
+
+ const getButtonColor = (current: number, origin: number): string => {
+ return current === origin ? '#007AFF99' : '#007AFF';
+ }
+
+ // 横向超出距离
+ const offsetHandler = (offsetX?: number) => {
+ setViewData(`HorizontalOuterRangeOffset: offsetX: ${offsetX?.toFixed(2)}`);
+ }
+
+ // 图片渲染完成
+ const layoutChangeHandler = (event: LayoutChangeEvent) => {
+ console.log('layoutChangeHandler!!!');
+ }
+
+ return (
+
+ 操作区域:
+ {viewData}
+ {longPressData}
+ {doubleClickData}
+ {singleClickData}
+ {swiperDownData}
+
+ 单指移动:
+ 双指缩放:
+ 双击放大:
+ 开启下滑:
+ 自定义中心: toggleSettingCenter(value)}>
+ 中心禁用: setEnterFocusIsEnable(value)} value={enableCenterFocus}>
+ {/* 动画驱动: setNativeDriverIsEnable(value)} value={enableNativeDriver}> */}
+ {/* 禁用安卓硬解: setAndroidRenderEnable(value)} value={enableAndroidRender}> */}
+ 单击距离:
+ 滑动阈值:
+ 长按阈值:
+ 下滑阈值:
+ 最小缩放:
+ 最大缩放:
+ 双击间隔:
+
+ {moveData}
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ button: {
+ width: 55
+ },
+ switchText: {
+ width: '50%'
+ },
+ box: {
+ display: 'flex',
+ flexDirection: 'row',
+ flexWrap: 'wrap'
+ }
+})
+
+export default ImageZoomDemo;
+```
+
+
+
+## 兼容性
+
+在以下版本验证通过
+
+1. RNOH:0.72.13; SDK:HarmonyOS NEXT Developer Preview1; IDE:DevEco Studio 4.1.3.500; ROM:204.1.0.59;
+
+## 属性
+
+详情见[react-native-image-zoom](https://github.com/ascoders/react-native-image-zoom)
+
+# `ImageZoom`
+
+| Name | Description | Type | Required | Platform | HarmonyOS Support | note |
+| -------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | -------- | ----------------- | ------------------------------------------------------------ |
+| **cropWidth(required)** | operating area width | number | YES | ALL | YES | |
+| **cropHeight(required)** | operating area height | number | YES | ALL | YES | |
+| **imageWidth(required)** | picture width | number | YES | ALL | YES | |
+| **imageHeight(required)** | picture height | number | YES | ALL | YES | |
+| onClick | onClick | (eventParams: [IOnClick](https://github.com/ascoders/react-native-image-zoom/blob/master/src/image-zoom/image-zoom.type.ts))=>void | NO | ALL | YES | |
+| `onDoubleClick` | onDoubleClick | `(eventParams: IOnClick)=>void ` | NO | ALL | YES | |
+| `panToMove` | allow to move picture with one finger | `boolean` | NO | ALL | YES | |
+| pinchToZoom | allow scale with two fingers | `boolean` | NO | ALL | YES | |
+| clickDistance | how many finger movement can also trigger `onClick` | number | NO | ALL | YES | |
+| `horizontalOuterRangeOffset` | horizontal beyond the distance, the parent to do picture switching, you can listen to this function. When this function is triggered, you can do the switch operation | (offsetX?: number)=>void | NO | ALL | YES | |
+| onDragLeft | trigger to switch to the left of the graph, the left sliding speed exceeds the threshold when triggered | ()=>void | NO | ALL | NO | https://github.com/ascoders/react-native-image-zoom/issues/103 |
+| `responderRelease` | let go but do not cancel | (vx: number)=>void | NO | ALL | YES | |
+| `maxOverflow` | maximum sliding threshold | `number` | NO | ALL | YES | |
+| `longPressTime` | long press threshold | `number` | NO | ALL | YES | |
+| onLongPress | on longPress | (eventParams: [IOnClick](https://github.com/ascoders/react-native-image-zoom/blob/master/src/image-zoom/image-zoom.type.ts))=>void | NO | ALL | YES | |
+| `doubleClickInterval ` | time allocated for second click to be considered as doublClick event | number | NO | ALL | YES | |
+| `onMove` | reports movement position data (helpful to build overlays) | ( position: [IOnMove](https://github.com/ascoders/react-native-image-zoom/blob/master/src/image-zoom/image-zoom.type.ts) )=>void | NO | ALL | YES | |
+| `centerOn` | if given this will cause the map to pan and zoom to the desired location | { x: number, y: number, scale: number, duration: number } | NO | ALL | YES | |
+| `enableSwipeDown` | for enabling vertical movement if user doesn't want it | `boolean` | NO | ALL | YES | |
+| `enableCenterFocus` | for disabling focus on image center if user doesn't want it | `boolean` | NO | ALL | YES | `` |
+| `onSwipeDown` | function that fires when user swipes down | `() => void ` | NO | ALL | YES | |
+| `swipeDownThreshold` | threshold for firing swipe down function | `number` | NO | ALL | YES | |
+| `minScale` | minimum zoom scale | `number` | NO | ALL | YES | |
+| maxScale | maximum zoom scale | number | NO | | YES | |
+| useNativeDriver | Whether to animate using [`useNativeDriver`](https://reactnative.dev/docs/animations#using-the-native-driver) | boolean | NO | ALL | YES | |
+| onStartShouldSetPanResponder | Override onStartShouldSetPanResponder behavior | () => boolean | NO | ALL | YES | |
+| onMoveShouldSetPanResponder | Override onMoveShouldSetPanResponder behavior | () => boolean | NO | ALL | YES | |
+| onPanResponderTerminationRequest | Override onMoveShouldSetPanResponder behavior | () => boolean | NO | ALL | YES | |
+| useHardwareTextureAndroid | for disabling rendering to hardware texture on Android | boolean | NO | Android | YES | |
+
+
+
+
+
+## 遗留问题
+
+## 其他
--
Gitee
From 2b3803f18355eaa4beba5668851b640daab2cdbb Mon Sep 17 00:00:00 2001
From: wuya_smile
Date: Fri, 22 Mar 2024 17:59:44 +0800
Subject: [PATCH 2/3] =?UTF-8?q?[Issues:=20#I98F1V]=20=E6=B7=BB=E5=8A=A0rea?=
=?UTF-8?q?ct-native-image-zoom=E6=93=8D=E4=BD=9C=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
1224/react-native-image-zoom.md | 44 +++++++++++++++++++--------------
1 file changed, 26 insertions(+), 18 deletions(-)
diff --git a/1224/react-native-image-zoom.md b/1224/react-native-image-zoom.md
index 35d55252..de7a3e8d 100644
--- a/1224/react-native-image-zoom.md
+++ b/1224/react-native-image-zoom.md
@@ -41,6 +41,7 @@ import React, { useState } from 'react';
import { Button, Dimensions, Image, LayoutChangeEvent, StyleSheet, Switch, Text, View } from 'react-native';
import ImageZoom from 'react-native-image-pan-zoom'
import type { IOnClick, IOnMove, ICenterOn } from 'react-native-image-pan-zoom';
+// const LOCAL_IMAGE_ASSET_ID = require('../')
const formatEventData = (evt: IOnClick) => {
const { locationX, locationY, pageX, pageY } = evt;
@@ -113,10 +114,10 @@ const ImageZoomDemo = () => {
// 双指缩放
const [isEnablePinchToZoom, setPinchToZoomState] = useState(false);
const togglePinchToZoomSwitch = () => {
- setPinchToZoomState(previousState => previousState);
+ setPinchToZoomState(previousState => !previousState);
}
const [minScale, setMinScale] = useState(0.6);
- const [maxScale, setMaxScale] = useState(10);
+ const [maxScale, setMaxScale] = useState(2);
// 单指移动
const [isEnablePanToMove, setPanToMoveState] = useState(false);
@@ -133,19 +134,22 @@ const ImageZoomDemo = () => {
scale: 1,
duration: 300
}
- const [centerData, setCenterDatat] = useState((): ICenterOn | undefined => undefined);
+ const [centerData, setCenterData] = useState((): ICenterOn | undefined => undefined);
const leftCenter: ICenterOn = {
x: 100,
y: 100,
scale: 1,
duration: 300
}
+ const [centerEnable, setCenterIsEnable] = useState(false);
const toggleSettingCenter = (val: boolean) => {
+ console.log(`toggleSettingCenter: ${val}`)
if (val) {
- setCenterDatat(leftCenter);
+ setCenterData(leftCenter);
} else {
- setCenterDatat(defaultCenter);
+ setCenterData(defaultCenter);
}
+ setCenterIsEnable(previousState => !previousState)
}
// 中心焦点
@@ -169,15 +173,15 @@ const ImageZoomDemo = () => {
// 成为响应者
const startPanResponderHandler = () => {
- console.log('startPanResponderHandler!!!');
+ console.log('respondercallback:startPanResponderHandler!!!');
return true;
}
const movePanResponderHandler = () => {
- console.log('movePanResponderHandler!!!');
+ console.log('respondercallback:movePanResponderHandler!!!');
return false;
}
const terminationRequestHandler = () => {
- console.log('movePanResponderHandler!!!');
+ console.log('respondercallback:terminationRequestHandler!!!');
return false;
}
@@ -198,8 +202,8 @@ const ImageZoomDemo = () => {
return (
操作区域:
+ title='100%' onPress={toggleAdd} color={getButtonColor(zoomWidth, winWidth)} />
{viewData}
{longPressData}
{doubleClickData}
@@ -210,17 +214,17 @@ const ImageZoomDemo = () => {
双指缩放:
双击放大:
开启下滑:
- 自定义中心: toggleSettingCenter(value)}>
+ 自定义中心: toggleSettingCenter(value)} value={centerEnable}>
中心禁用: setEnterFocusIsEnable(value)} value={enableCenterFocus}>
- {/* 动画驱动: setNativeDriverIsEnable(value)} value={enableNativeDriver}> */}
+ 动画驱动: setNativeDriverIsEnable(value)} value={enableNativeDriver}>
{/* 禁用安卓硬解: setAndroidRenderEnable(value)} value={enableAndroidRender}> */}
单击距离:
滑动阈值:
- 长按阈值:
- 下滑阈值:
+ 长按阈值:
+ 下滑阈值:
最小缩放:
最大缩放:
- 双击间隔:
+ 双击间隔:
{moveData}
{
>
{
const styles = StyleSheet.create({
button: {
- width: 55
+ width: 50
},
switchText: {
width: '50%'
@@ -332,10 +336,14 @@ export default ImageZoomDemo;
| onPanResponderTerminationRequest | Override onMoveShouldSetPanResponder behavior | () => boolean | NO | ALL | YES | |
| useHardwareTextureAndroid | for disabling rendering to hardware texture on Android | boolean | NO | Android | YES | |
+## 遗留问题
+- [ ] 双指缩放在harmony框架侧不支持使用[#I99NP7](https://gitee.com/wuyasmile/usage-docs/issues/I99NP7)
+- [ ] 长按图片后,rn框架图片拖拽属性导致程序崩溃[#I99NQ7](https://gitee.com/wuyasmile/usage-docs/issues/I99NQ7)
+- [x] 下滑图片,然后双击放大,再次下滑表现异常,已在源库基础上修改[上库地址](https://github.com/react-native-oh-library/react-native-image-zoom),源库已被存档,不能新建issue,特此记录
-## 遗留问题
+- [ ] onPanResponderTerminationRequest方法,harmony端与android端表现不一致[#I9ATFE](https://gitee.com/wuyasmile/usage-docs/issues/I9ATFE)
## 其他
--
Gitee
From 8068761a91b69f5c7ace6d554e5d4a45c30afddd Mon Sep 17 00:00:00 2001
From: wuya_smile
Date: Mon, 25 Mar 2024 17:56:09 +0800
Subject: [PATCH 3/3] =?UTF-8?q?[Issues:=20#I98F1V]=20=E6=B7=BB=E5=8A=A0rea?=
=?UTF-8?q?ct-native-image-zoom=E6=93=8D=E4=BD=9C=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
1224/react-native-image-zoom.md | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/1224/react-native-image-zoom.md b/1224/react-native-image-zoom.md
index de7a3e8d..d3e7ff69 100644
--- a/1224/react-native-image-zoom.md
+++ b/1224/react-native-image-zoom.md
@@ -12,24 +12,28 @@
-> [!TIP] [Github 地址](https://github.com/ascoders/react-native-image-zoom)
+> [!TIP] [Github 地址](https://github.com/react-native-oh-library/react-native-image-zoom)
## 安装与使用
+请到三方库的 Releases 发布地址查看配套的版本信息:[<@react-native-oh-library/react-native-image-zoom> Releases](https://github.com/react-native-oh-library/react-native-image-zoom/releases),并下载适用版本的 tgz 包。
+
进入到工程目录并输入以下命令:
+>[!TIP] # 处替换为 tgz 包的路径
+
#### **npm**
```bash
-npm i react-native-image-pan-zoom --save
+npm install @react-native-oh-tpl/react-native-image-zoom@file:#
```
#### **yarn**
```bash
-yarn add react-native-image-pan-zoom
+yarn add @react-native-oh-tpl/react-native-image-zoom@file:#
```
@@ -313,10 +317,10 @@ export default ImageZoomDemo;
| onClick | onClick | (eventParams: [IOnClick](https://github.com/ascoders/react-native-image-zoom/blob/master/src/image-zoom/image-zoom.type.ts))=>void | NO | ALL | YES | |
| `onDoubleClick` | onDoubleClick | `(eventParams: IOnClick)=>void ` | NO | ALL | YES | |
| `panToMove` | allow to move picture with one finger | `boolean` | NO | ALL | YES | |
-| pinchToZoom | allow scale with two fingers | `boolean` | NO | ALL | YES | |
+| pinchToZoom | allow scale with two fingers | `boolean` | NO | ALL | NO | 详见遗留问题 |
| clickDistance | how many finger movement can also trigger `onClick` | number | NO | ALL | YES | |
| `horizontalOuterRangeOffset` | horizontal beyond the distance, the parent to do picture switching, you can listen to this function. When this function is triggered, you can do the switch operation | (offsetX?: number)=>void | NO | ALL | YES | |
-| onDragLeft | trigger to switch to the left of the graph, the left sliding speed exceeds the threshold when triggered | ()=>void | NO | ALL | NO | https://github.com/ascoders/react-native-image-zoom/issues/103 |
+| onDragLeft | trigger to switch to the left of the graph, the left sliding speed exceeds the threshold when triggered | ()=>void | NO | \ | NO | 此回调函数源码未实现,详见:https://github.com/ascoders/react-native-image-zoom/issues/103 |
| `responderRelease` | let go but do not cancel | (vx: number)=>void | NO | ALL | YES | |
| `maxOverflow` | maximum sliding threshold | `number` | NO | ALL | YES | |
| `longPressTime` | long press threshold | `number` | NO | ALL | YES | |
@@ -328,13 +332,13 @@ export default ImageZoomDemo;
| `enableCenterFocus` | for disabling focus on image center if user doesn't want it | `boolean` | NO | ALL | YES | `` |
| `onSwipeDown` | function that fires when user swipes down | `() => void ` | NO | ALL | YES | |
| `swipeDownThreshold` | threshold for firing swipe down function | `number` | NO | ALL | YES | |
-| `minScale` | minimum zoom scale | `number` | NO | ALL | YES | |
-| maxScale | maximum zoom scale | number | NO | | YES | |
+| `minScale` | minimum zoom scale | `number` | NO | ALL | NO | 此值在双指缩放功能正常后可生效 |
+| maxScale | maximum zoom scale | number | NO | ALL | NO | 此值在双指缩放功能正常后可生效 |
| useNativeDriver | Whether to animate using [`useNativeDriver`](https://reactnative.dev/docs/animations#using-the-native-driver) | boolean | NO | ALL | YES | |
| onStartShouldSetPanResponder | Override onStartShouldSetPanResponder behavior | () => boolean | NO | ALL | YES | |
| onMoveShouldSetPanResponder | Override onMoveShouldSetPanResponder behavior | () => boolean | NO | ALL | YES | |
-| onPanResponderTerminationRequest | Override onMoveShouldSetPanResponder behavior | () => boolean | NO | ALL | YES | |
-| useHardwareTextureAndroid | for disabling rendering to hardware texture on Android | boolean | NO | Android | YES | |
+| onPanResponderTerminationRequest | Override onMoveShouldSetPanResponder behavior | () => boolean | NO | ALL | NO | 详见遗留问题 |
+| useHardwareTextureAndroid | for disabling rendering to hardware texture on Android | boolean | NO | Android | NO | 此属性仅Android生效 |
## 遗留问题
--
Gitee