87d7b854 by 华明祺

feat(personal): 完善个人会员申请及支付流程

- 协议勾选区域固定在页面底部,优化用户体验
- 联系方式改为非必填,但填写时验证手机号格式
- 支付逻辑使用 await-to-js 重构,统一错误处理
- 支付流程添加 loading 状态,防止重复提交
- 支付成功后传递 orderId,跳转时获取并展示订单详情
- 支付成功页面优化:标签不换行、值支持自动换行
- 新增获取订单详情接口 /common/order/{orderId}
1 parent 7d54d38d
......@@ -195,11 +195,11 @@ export function regionsList(params) {
export function carUrl(data, type) {
return uni.uploadFile({
url: `${config.baseUrl_api}/person/info/getPersonInfoFromCert/${type}`,
header: {
'Authorization': uni.getStorageSync('token'),
'Content-Language': 'zh_CN',
'Accept-Language': 'zh-CN,zh',
},
// header: {
// 'Authorization': uni.getStorageSync('token'),
// 'Content-Language': 'zh_CN',
// 'Accept-Language': 'zh-CN,zh',
// },
name: 'pic',
filePath: data
}).then(res => {
......@@ -237,10 +237,10 @@ export function addPersonCommit(data) {
}
export function getZtxFeeConfig() {
return request({
url: '/system/config/getZtxFeeConfig',
method: 'get'
})
return request({
url: '/system/config/getZtxFeeConfig',
method: 'get'
})
}
// 图片上传
export function uploadImg(e) {
......@@ -747,10 +747,10 @@ export function personalCommit(id) {
})
}
export function getNewCountByRangeId(rangeId) {
return request({
url: `/person/paymentNew/getNewCountByRangeId/${rangeId}`,
method: 'get',
})
return request({
url: `/person/paymentNew/getNewCountByRangeId/${rangeId}`,
method: 'get',
})
}
export function delPayment(payIds) {
......@@ -774,11 +774,11 @@ export function delcertified(ids) {
}
export function editYear(data) {
return request({
url: `/person/paymentNew/editYear/${data.payId}?payId=${data.payId}&year=${data.year}`,
method: 'post',
params: data
})
return request({
url: `/person/paymentNew/editYear/${data.payId}?payId=${data.payId}&year=${data.year}`,
method: 'post',
params: data
})
}
export function editGroupYear(data) {
......@@ -1283,135 +1283,143 @@ export function checkPersonByPersonId(perId) {
}
// 获取团体会员优惠政策
export function canUseDiscount(params) {
return request({
url: `/system/certifiedNew/canUseDiscount`,
method: 'get',
params
})
return request({
url: `/system/certifiedNew/canUseDiscount`,
method: 'get',
params
})
}
// 获取团体会员一年缴费价格
export function getMyMemberCertUnitFee(params) {
return request({
url: `/system/certifiedNew/getMyMemberCertUnitFee`,
method: 'get',
params
})
return request({
url: `/system/certifiedNew/getMyMemberCertUnitFee`,
method: 'get',
params
})
}
export function checkBusinessLicense(data) {
return request({
url: `/member/info/checkBusinessLicense`,
method: 'post',
params: data
})
return request({
url: `/member/info/checkBusinessLicense`,
method: 'post',
params: data
})
}
// 生成团体订单renewYear
export function certifiedNew(params) {
return request({
url: `/system/certifiedNew/commit`,
method: 'post',
params
})
return request({
url: `/system/certifiedNew/commit`,
method: 'post',
params
})
}
// 模拟回调
export function callBack2(orderId) {
return request({
url: `/system/certifiedNew/callBack2/${orderId}`,
method: 'get',
})
return request({
url: `/system/certifiedNew/callBack2/${orderId}`,
method: 'get',
})
}
export function pcallBack2(orderId) {
return request({
url: `/person/paymentRangeNew/callBack2/${orderId}`,
method: 'get',
})
return request({
url: `/person/paymentRangeNew/callBack2/${orderId}`,
method: 'get',
})
}
// 优惠政策回显
export function getZtxDiscountPolicy(params) {
return request({
url: '/system/config/getZtxDiscountPolicy',
method: 'get',
params
})
return request({
url: '/system/config/getZtxDiscountPolicy',
method: 'get',
params
})
}
// 考官列表
export function listApi(params) {
return request({
url: `/member/examiner/list`,
method: 'get',
params
})
return request({
url: `/member/examiner/list`,
method: 'get',
params
})
}
// 考官列表
export function examinerDel(id) {
return request({
url: `/member/examiner/${id}`,
method: 'delete'
})
return request({
url: `/member/examiner/${id}`,
method: 'delete'
})
}
// 添加考官
export function otherAdd(memId, ids) {
return request({
url: `/member/examiner/otherAdd/${memId}/${ids}`,
method: 'post'
})
return request({
url: `/member/examiner/otherAdd/${memId}/${ids}`,
method: 'post'
})
}
export function commitExamPointApply(params) {
return request({
url: `/member/examPointApply/commit?selfSelect=${params.selfSelect}`,
method: 'post',
params
})
return request({
url: `/member/examPointApply/commit?selfSelect=${params.selfSelect}`,
method: 'post',
params
})
}
export function getMyStatus() {
return request({
url: `/member/examPointApply/getMyStatus`
})
return request({
url: `/member/examPointApply/getMyStatus`
})
}
// 个人会员缴费支付
export function goPay(id) {
return request({
url: `/person/paymentRangeNew/pay/${id}`,
method: 'post'
})
return request({
url: `/person/paymentRangeNew/pay/${id}`,
method: 'post'
})
}
// 缴费单列表学员
export function listAPI(params) {
return request({
url: `/person/paymentNew/list`,
method: 'get',
params
})
return request({
url: `/person/paymentNew/list`,
method: 'get',
params
})
}
// 删除学员
export function paymentNewDel(id) {
return request({
url: `/person/paymentNew/${id}`,
method: 'delete'
})
return request({
url: `/person/paymentNew/${id}`,
method: 'delete'
})
}
// 缴费单列表
export function memberInsertPersons(data) {
return request({
url: `/person/paymentNew/memberInsertPersons/${data.rangeId}/${data.year}/${data.idcCode}`,
method: 'post',
data
})
return request({
url: `/person/paymentNew/memberInsertPersons/${data.rangeId}/${data.year}/${data.idcCode}`,
method: 'post',
data
})
}
export function createMemberPayRange(data) {
return request({
url: `/person/paymentRangeNew/createMemberPayRange`,
method: 'post',
data
})
return request({
url: `/person/paymentRangeNew/createMemberPayRange`,
method: 'post',
data
})
}
// 获取订单详情
export function getOrderInfo(orderId) {
return request({
url: `/common/order/${orderId}`,
method: 'get'
})
}
\ No newline at end of file
......
{
"dependencies": {
"await-to-js": "^3.0.0",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.6",
"lodash": "^4.17.21",
......
<template>
<view class="success-container">
<!-- 成功图标(渐变圆形+动效) -->
<view class="success-icon">
<view class="icon-circle">
<text class="check-icon"></text>
</view>
</view>
<!-- 支付成功标题(动画) -->
<view class="success-title">支付成功</view>
<view class="success-subtitle">支付成功,请等待审核</view>
<!-- 订单信息卡片(带阴影) -->
<view class="info-card">
<view class="info-item">
<text class="label">付款账户</text>
<text class="value">(5437)</text>
</view>
<view class="info-item">
<text class="label">交易流水号</text>
<text class="value">2205051351076117833</text>
</view>
<view class="info-item">
<text class="label">商户名称</text>
<text class="value">中国跆拳道协会</text>
</view>
<view class="info-item">
<text class="label">订单金额</text>
<text class="value amount">1500.00元</text>
</view>
<view class="info-item">
<text class="label">会员编号</text>
<text class="value">CTA00004</text>
</view>
<view class="info-item">
<text class="label">会员有效期</text>
<text class="value">2028年1月25日</text>
</view>
</view>
<!-- 确定按钮(渐变+动效) -->
<view class="confirm-btn-area">
<button class="confirm-btn" @click="goBack">确定</button>
</view>
</view>
</template>
<script setup>
import { onLoad } from '@dcloudio/uni-app'
const goBack = () => {
uni.navigateTo({
url: `/personal/home`
})
}
onLoad((option) => {
})
</script>
<style scoped>
/* 全局容器 */
.success-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 100rpx 40rpx 60rpx;
min-height: 100vh;
background-color: #f8f9fa;
box-sizing: border-box;
}
/* 成功图标容器 */
.success-icon {
margin-bottom: 40rpx;
animation: fadeIn 0.6s ease-out;
}
/* 渐变圆形背景 */
.icon-circle {
width: 180rpx;
height: 180rpx;
border-radius: 50%;
/* 青绿色渐变 */
background: linear-gradient(135deg, #06c1ae, #04a896);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 30rpx rgba(6, 193, 174, 0.3);
/* 轻微上浮动效 */
animation: scaleIn 0.8s ease-out;
}
/* 对勾图标 */
.check-icon {
font-size: 90rpx;
color: #ffffff;
font-weight: bold;
}
/* 支付成功标题 */
.success-title {
font-size: 48rpx;
font-weight: 700;
color: #333333;
margin-bottom: 12rpx;
animation: slideUp 0.6s ease-out;
}
/* 副标题 */
.success-subtitle {
font-size: 28rpx;
color: #666666;
margin-bottom: 60rpx;
animation: slideUp 0.8s ease-out;
}
/* 订单信息卡片 */
.info-card {
width: 100%;
background: #ffffff;
border-radius: 20rpx;
padding: 40rpx 30rpx;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.05);
margin-bottom: 80rpx;
animation: fadeIn 1s ease-out;
}
/* 单个信息项 */
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
/* 最后一项去掉下划线 */
.info-item:last-child {
border-bottom: none;
}
/* 标签样式 */
.label {
font-size: 32rpx;
color: #666666;
}
/* 值样式 */
.value {
font-size: 32rpx;
color: #333333;
text-align: right;
}
/* 金额特殊样式 */
.amount {
color: #cd1e27;
font-weight: 600;
}
/* 确定按钮区域 */
.confirm-btn-area {
width: 100%;
padding: 0 20rpx;
box-sizing: border-box;
}
/* 确定按钮(渐变+动效) */
.confirm-btn {
width: 100%;
height: 90rpx;
line-height: 90rpx;
/* 按钮渐变背景 */
background: #fff;
color: #C4121B;
font-size: 36rpx;
font-weight: 600;
border-radius: 45rpx;
border: 1px solid #C4121B;
animation: slideUp 1s ease-out;
/* 禁止默认样式 */
position: relative;
overflow: hidden;
}
/* 按钮点击反馈 */
.confirm-btn::after {
border: none;
}
.confirm-btn:active {
transform: scale(0.98);
box-shadow: 0 4rpx 10rpx rgba(6, 193, 174, 0.2);
}
/* 动画定义 */
@keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes scaleIn {
0% { transform: scale(0); }
70% { transform: scale(1.1); }
100% { transform: scale(1); }
}
@keyframes slideUp {
0% { opacity: 0; transform: translateY(30rpx); }
100% { opacity: 1; transform: translateY(0); }
}
<template>
<view class="success-container">
<!-- 成功图标(渐变圆形+动效) -->
<view class="success-icon">
<view class="icon-circle">
<text class="check-icon"></text>
</view>
</view>
<!-- 支付成功标题(动画) -->
<view class="success-title">支付成功</view>
<view class="success-subtitle">支付成功,请等待审核</view>
<!-- 订单信息卡片(带阴影) -->
<view class="info-card">
<view class="info-item">
<text class="label">交易流水号</text>
<text class="value">{{ orderInfo.tradeNo }}</text>
</view>
<view class="info-item">
<text class="label">商户名称</text>
<text class="value">{{ orderInfo.merchantName || '中国跆拳道协会' }}</text>
</view>
<view class="info-item">
<text class="label">订单金额</text>
<text class="value amount">{{ orderInfo.price ? orderInfo.price + '元' : '--' }}</text>
</view>
</view>
<!-- 确定按钮(渐变+动效) -->
<view class="confirm-btn-area">
<button class="confirm-btn" @click="goBack">确定</button>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
import to from 'await-to-js'
import * as api from '@/common/api.js'
const orderInfo = ref({
id: '',
tradeNo: '',
merchantName: '中国跆拳道协会',
price: ''
})
const goBack = () => {
uni.reLaunch({
url: '/login/login'
})
}
onLoad(async (option) => {
if (option.orderId) {
const [err, res] = await to(api.getOrderInfo(option.orderId))
if (!err && res.data) {
orderInfo.value = res.data
} else {
orderInfo.value.id = option.orderId
}
}
})
</script>
<style scoped>
/* 全局容器 */
.success-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 100rpx 40rpx 60rpx;
min-height: 100vh;
background-color: #f8f9fa;
box-sizing: border-box;
}
/* 成功图标容器 */
.success-icon {
margin-bottom: 40rpx;
animation: fadeIn 0.6s ease-out;
}
/* 渐变圆形背景 */
.icon-circle {
width: 180rpx;
height: 180rpx;
border-radius: 50%;
/* 青绿色渐变 */
background: linear-gradient(135deg, #06c1ae, #04a896);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 30rpx rgba(6, 193, 174, 0.3);
/* 轻微上浮动效 */
animation: scaleIn 0.8s ease-out;
}
/* 对勾图标 */
.check-icon {
font-size: 90rpx;
color: #ffffff;
font-weight: bold;
}
/* 支付成功标题 */
.success-title {
font-size: 48rpx;
font-weight: 700;
color: #333333;
margin-bottom: 12rpx;
animation: slideUp 0.6s ease-out;
}
/* 副标题 */
.success-subtitle {
font-size: 28rpx;
color: #666666;
margin-bottom: 60rpx;
animation: slideUp 0.8s ease-out;
}
/* 订单信息卡片 */
.info-card {
width: 100%;
background: #ffffff;
border-radius: 20rpx;
padding: 40rpx 30rpx;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.05);
margin-bottom: 80rpx;
animation: fadeIn 1s ease-out;
}
/* 单个信息项 */
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
/* 最后一项去掉下划线 */
.info-item:last-child {
border-bottom: none;
}
/* 标签样式 */
.label {
font-size: 32rpx;
color: #666666;
white-space: nowrap;
margin-right: 20rpx;
flex-shrink: 0;
}
/* 值样式 */
.value {
font-size: 32rpx;
color: #333333;
text-align: right;
word-break: break-all;
word-wrap: break-word;
}
/* 金额特殊样式 */
.amount {
color: #cd1e27;
font-weight: 600;
}
/* 确定按钮区域 */
.confirm-btn-area {
width: 100%;
padding: 0 20rpx;
box-sizing: border-box;
}
/* 确定按钮(渐变+动效) */
.confirm-btn {
width: 100%;
height: 90rpx;
line-height: 90rpx;
/* 按钮渐变背景 */
background: #fff;
color: #C4121B;
font-size: 36rpx;
font-weight: 600;
border-radius: 45rpx;
border: 1px solid #C4121B;
animation: slideUp 1s ease-out;
/* 禁止默认样式 */
position: relative;
overflow: hidden;
}
/* 按钮点击反馈 */
.confirm-btn::after {
border: none;
}
.confirm-btn:active {
transform: scale(0.98);
box-shadow: 0 4rpx 10rpx rgba(6, 193, 174, 0.2);
}
/* 动画定义 */
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes scaleIn {
0% {
transform: scale(0);
}
70% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
@keyframes slideUp {
0% {
opacity: 0;
transform: translateY(30rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
</style>
\ No newline at end of file
......
## 2.1.6(2023-04-16)
* 修复 组件使用 v-show 指令会导致选择图片后初始位置严重偏位的问题
## 2.1.5(2023-04-16)
* 新增 兼容APP平台
## 2.1.4(2023-03-13)
* 新增 fileType 属性,用于指定生成文件的类型,只支持 'jpg' 或 'png',默认为 'png'
* 新增 delay 属性,微信小程序平台使用 `Canvas 2D` 绘制时控制图片从绘制到生成所需时间
* 优化 当生成图片的尺寸宽/高超过 Canvas 2D 最大限制(1365*1365)则将画布尺寸缩放在限制范围内绘制完成后输出目标尺寸
* 优化 旋转图标指示方向与实际旋转方向不符
## 2.1.3(2023-02-06)
* 优化 vue3支持
## 2.1.2(2023-02-03)
* 新增 navigation 属性,H5平台当 showAngle 为 true 时,使用插件的页面在 `page.json` 中配置了 "navigationStyle": "custom" 时,必须将此值设为 false ,否则四个可拉伸角的触发位置会有偏差
* 修复 H5平台部分设备(已知iPhone11以下机型)拍照的图片缩放时会闪动的问题
## 2.1.1(2022-12-06)
* 修复 横屏适配问题
## 2.1.0(2022-12-06)
* 新增 兼容H5平台,使用 renderjs 响应手势事件
## 2.0.0(2022-12-05)
* 重构 插件,使用 WXS 响应手势事件
* 新增 图片翻转
* 新增 拉伸裁剪框放大图片
* 新增 监听PC鼠标滚轮触发缩放
* 新增 圆形、圆角矩形的图片裁剪
* 优化 图片缩放,移动端以双指触摸中心点为缩放中心点,PC端以鼠标所在点为缩放中心点
* 优化 裁剪框样式
* 优化 图片位置拖动 支持边界回弹效果(滑动时可滑出边界,释放时回弹到边界)
* 优化 生成图片使用新版 Canvas 2D 接口
{
"id": "qf-image-cropper",
"displayName": "图片裁剪插件",
"version": "2.1.6",
"description": "图片裁剪插件,支持自定义尺寸、定点等比例缩放、拖动、图片翻转、剪切圆形/圆角图片、定制样式,功能多性能高体验好注释全。",
"keywords": [
"qf-image-cropper",
"图片裁剪",
"图片编辑",
"头像裁剪",
"小程序"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "u",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}
\ No newline at end of file
# qf-image-cropper
## 图片裁剪插件
uniapp微信小程序图片裁剪插件,支持自定义尺寸、定点等比例缩放、拖动、图片翻转、剪切圆形/圆角图片、定制样式,功能多性能高体验好注释全。
### 平台支持:
1. 支持微信小程序:移动端、PC端、开发者工具
2. 支持H5平台(2.1.0版本起)
3. 支持APP平台(2.1.5版本起):Android、IOS
4. 其他平台暂未测试兼容性未知
### 支持功能:
1. 自定义裁剪尺寸
2. 定点等比例缩放:移动端以双指触摸中心点为缩放中心点,PC端以鼠标所在点为缩放中心点
3. 自由拖动:支持限制滑出边界,也支持回弹效果(滑动时可滑出边界,释放时回弹到边界)
4. 图片翻转:在裁剪尺寸非 1:1 的情况下,翻转时宽高无法铺满裁剪区域时,图片会自动放大到合适尺寸
5. 裁剪生成新图片
6. 本地选择图片
7. 可定制样式:可自由选择是否渲染裁剪边框、可伸缩裁剪顶角、参考线
8. 裁剪圆角图片:圆形、圆角矩形
### 属性说明
| 属性名 | 类型 | 默认值 | 说明 |
|:---|:---|:---|:---|
| src | String | | 图片资源地址 |
| width | Number | 300 | 裁剪宽度 |
| height | Number | 300 | 裁剪高度 |
| showBorder | Boolean | true | 是否绘制裁剪区域边框 |
| showGrid | Boolean | true | 是否绘制裁剪区域网格参考线 |
| showAngle | Boolean | true | 是否展示四个支持伸缩的角 |
| areaScale | Number | 0.3 | 裁剪区域最小缩放倍数 |
| maxScale | Number | 5 | 图片最大缩放倍数 |
| bounce | Boolean | true | 是否有回弹效果:拖动时可以拖出边界,释放时会弹回边界 |
| rotatable | Boolean | true | 是否支持翻转 |
| choosable | Boolean | true | 是否支持从本地选择素材 |
| angleSize | Number | 20 | 四个角尺寸,单位px |
| angleBorderWidth | Number | 2 | 四个角边框宽度,单位px |
| radius | Number | | 裁剪图片圆角半径,单位px |
| fileType | String | png | 生成文件的类型,只支持 'jpg' 或 'png'。默认为 'png' |
| delay | Number | 1000 | 图片从绘制到生成所需时间,单位ms<br>微信小程序平台使用 `Canvas 2D` 绘制时有效<br>如绘制大图或出现裁剪图片空白等情况应适当调大该值,因 `Canvas 2d` 采用同步绘制,需自己把控绘制完成时间 |
| navigation | Boolean | true | 页面是否是原生标题栏:<br>H5平台当 showAngle 为 true 时,使用插件的页面在 `page.json` 中配置了 `"navigationStyle": "custom"` 时,必须将此值设为 false ,否则四个可拉伸角的触发位置会有偏差。<br>注:因H5平台的窗口高度是包含标题栏的,而屏幕触摸点的坐标是不包含的 |
| @crop | EventHandle | | 剪裁完成后触发,event = { tempFilePath }。在H5平台下,tempFilePath 为 base64 |
### 基本用法
```
<template>
<div>
<qf-image-cropper :width="500" :height="500" :radius="30" @crop="handleCrop"></qf-image-cropper>
</div>
</template>
<script>
import QfImageCropper from '@/components/qf-image-cropper/qf-image-cropper.vue';
export default {
components: {
QfImageCropper
},
methods: {
handleCrop(e) {
uni.previewImage({
urls: [e.tempFilePath],
current: 0
});
}
}
}
</script>
```
### 使用说明
1.建议在`pages.json`中将引用插件的页面添加一下配置禁止下拉刷新和禁止页面滑动,防止出现性能或页面抖动等问题。
```
{
"enablePullDownRefresh": false,
"disableScroll": true
}
```
2.建议使用本插件不要设置过大宽高的目标图片尺寸,建议1365x1365以内,否则可能会导致如下问题:
```
1.界面卡顿,内存占用过高
2.生成图片失真(模糊)
3.确定裁剪后一直显示 `裁剪中...`,该问题是由 `uni.canvasToTempFilePath` 无法回调导致,不同平台不同设备限制可能有所不同。
```
3.如裁剪后的图片存在偏移的问题,请检查是否受自己项目中父组件或全局样式影响。
4.src属性设置网络图片时,图片资源必须是能触发 `getImageInfo` API 的 success 回调才可用于插件裁剪。因此小程序平台获取网络图片信息需先配置download域名白名单才能生效。
\ No newline at end of file
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!