更新:增加数据迁移功能

This commit is contained in:
2025-12-02 11:46:19 +08:00
parent d653575d27
commit 35e27753b8
7 changed files with 296 additions and 41 deletions

View File

@@ -299,4 +299,19 @@ export async function getPointsData(current : number, limit : number, userId : s
data: { current, limit, userId, }
})
return res
}
/**
* 迁移用户数据
* @param tel 旧账号
* @param code 迁移验证码
* @return
*/
export async function migrateUserData(data: { tel: string, code: string }) {
const res = await mainClient.request<IApiResponse>({
url: 'common/user/migrationWumenData',
method: 'POST',
data
})
return res
}

View File

@@ -194,10 +194,6 @@
"pleaseInputOrderSn": "Please enter order number",
"uploadImageFailed": "Image upload failed",
"maxImagesCount": "Maximum 4 images",
"permissionDenied": "Permission Denied",
"cameraPermission": "Camera permission required",
"storagePermission": "Storage permission required",
"phonePermission": "Phone permission required",
"sendCodeSuccess": "Verification code sent",
"sendCodeFailed": "Failed to send code",
"countdown": "s to resend",
@@ -214,7 +210,23 @@
"selectPackage": "Please select a package",
"consumptionRecord": "Consumption record",
"returnMine": "I'm about to return to my page",
"pointsRecord": "Points consumption record"
"dataMigrate": "Data Migration",
"pointsRecord": "Points consumption record",
"migrateSubtitle": "Migrate data from Chinese account to current account",
"oldAccount": "Chinese Account",
"migrateCode": "Migration Code",
"confirmMigrate": "Confirm Migration",
"migrateSuccess": "Migration Successful",
"migrateFailed": "Migration failed, please check if the information is correct",
"pleaseCompleteInfo": "Please complete all information",
"oldAccountPlaceholder": "The chinese account to migrate",
"migrateCodePlaceholder": "The code obtained from chinese account",
"migrateWarning": "Migration is irreversible, please proceed with caution!",
"migrateInstructions": "Migration Instructions",
"instruction1": "Please obtain the migration code from your chinese account, get it by going to 【我的】-【数据迁移】-【获取迁移验证码】.",
"instruction2": "After data migration is complete, the chinese account data will be cleared, and all purchased Tianyi coins, points, courses, VIP and other data will be transferred to the current account",
"instruction3": "The migration process may take a few minutes, please be patient.",
"instruction4": "If you encounter any issues, please contact customer service for assistance."
},
"book": {

View File

@@ -195,10 +195,6 @@
"pleaseInputOrderSn": "请输入订单编号",
"uploadImageFailed": "图片上传失败",
"maxImagesCount": "最多上传4张图片",
"permissionDenied": "权限被拒绝",
"cameraPermission": "需要相机权限",
"storagePermission": "需要存储权限",
"phonePermission": "需要电话权限",
"sendCodeSuccess": "验证码发送成功",
"sendCodeFailed": "验证码发送失败",
"countdown": "秒后重新发送",
@@ -214,7 +210,24 @@
"days": "天",
"selectPackage": "请选择套餐",
"consumptionRecord": "消费记录",
"returnMine": "即将返回我的页面"
"returnMine": "即将返回我的页面",
"pointsRecord": "积分记录",
"dataMigrate": "数据迁移",
"migrateSubtitle": "迁移国内账号数据到本账号",
"oldAccount": "国内版账号",
"migrateCode": "迁移验证码",
"confirmMigrate": "确定迁移",
"migrateSuccess": "迁移成功",
"migrateFailed": "迁移失败,请检查信息是否正确",
"pleaseCompleteInfo": "请填写完整信息",
"oldAccountPlaceholder": "需要迁移的国内版账号",
"migrateCodePlaceholder": "国内版账号获取的迁移验证码",
"migrateWarning": "迁移后不可恢复,请谨慎操作!",
"migrateInstructions": "迁移说明",
"instruction1": "请在吴门医述APP中获取迁移验证码获取方式【我的】-【数据迁移】-【获取迁移验证码】。",
"instruction2": "数据迁移完成后旧账号数据将被清空已购买的天医币、积分、课程、VIP等数据将转移到当前账号。",
"instruction3": "迁移过程可能需要几分钟时间,请耐心等待。",
"instruction4": "如遇到问题,请联系客服获取帮助。"
},
"book": {
"title": "我的书单",

View File

@@ -199,6 +199,12 @@
"navigationStyle": "custom",
"navigationBarTitleText": "新闻详情"
}
}, {
"path": "pages/user/migrate/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "数据迁移"
}
}, {
"path": "uni_modules/uni-upgrade-center-app/pages/upgrade-popup",
"style": {

View File

@@ -64,12 +64,9 @@
<!-- 功能菜单列表 -->
<view class="menu-section">
<view class="menu-list">
<view v-for="item in menuItems" :key="item.id" class="menu-item" @click="handleMenuClick(item)">
<text class="menu-text">{{ item.name }}</text>
<wd-icon name="arrow-right" size="16px" color="#aaa" />
</view>
</view>
<wd-cell-group border class="menu-list">
<wd-cell v-for="item in menuItems" :key="item.id" :title="item.name" :label="item.desc" is-link @click="handleMenuClick(item)" />
</wd-cell-group>
</view>
</view>
</template>
@@ -135,6 +132,13 @@
name: t('user.feedback'),
url: '/pages/user/feedback/index',
type: 'pageJump'
},
{
id: 6,
name: t('user.dataMigrate'),
url: '/pages/user/migrate/index',
desc: t('user.migrateSubtitle'),
type: 'pageJump'
}
])
@@ -382,7 +386,7 @@
}
.menu-section {
padding: 20rpx 20rpx 0;
padding: 20rpx 20rpx;
}
.menu-list {
@@ -392,30 +396,6 @@
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.menu-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1px solid #e0e0e0;
&:last-child {
border-bottom: none;
}
&:active {
background-color: #f5f5f5;
}
.menu-text {
font-size: 30rpx;
color: #333;
line-height: 40rpx;
}
}
.chong_btn {
font-size: 26rpx;
display: block;

View File

@@ -0,0 +1,184 @@
<template>
<view class="page-wrap">
<!-- 自定义导航栏 -->
<nav-bar :title="$t('user.dataMigrate')"></nav-bar>
<view class="text-red-500 text-center mb-[20rpx]! font-bold">{{ $t('user.migrateWarning') }}</view>
<!-- 主要内容区域 -->
<wd-form ref="migrateForm" :model="formData" :rules="rules" :label-width="120" class="migrate-card p-[10rpx]">
<wd-cell-group border>
<wd-input
v-model="formData.tel"
prop="tel"
clearable
:label="$t('user.oldAccount')"
:placeholder="$t('user.oldAccountPlaceholder')"
/>
<wd-input
v-model="formData.code"
prop="code"
clearable
:label="$t('user.migrateCode')"
:placeholder="$t('user.migrateCodePlaceholder')"
/>
<view class="pb-[20rpx] pt-[10rpx]">
<wd-button type="primary" size="medium" @click="handleSubmit" block>{{ $t('user.confirmMigrate') }}</wd-button>
</view>
</wd-cell-group>
</wd-form>
<!-- 迁移说明 -->
<view class="migrate-card p-[30rpx]">
<view class="instructions-title">{{ $t('user.migrateInstructions') }}</view>
<view class="instructions-content">
<view class="instruction-item">
<text class="instruction-number">1.</text>
<text class="instruction-text">{{ $t('user.instruction1') }}</text>
</view>
<view class="instruction-item">
<text class="instruction-number">2.</text>
<text class="instruction-text">{{ $t('user.instruction2') }}</text>
</view>
<view class="instruction-item">
<text class="instruction-number">3.</text>
<text class="instruction-text">{{ $t('user.instruction3') }}</text>
</view>
<view class="instruction-item">
<text class="instruction-number">4.</text>
<text class="instruction-text">{{ $t('user.instruction4') }}</text>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { t } from '@/utils/i18n'
import { migrateUserData } from '@/api/modules/user'
// 表单引用
const migrateForm = ref()
// 表单数据
const formData = ref({
tel: '',
code: ''
})
// 表单验证规则
const rules = ref({
tel: [
{ required: true, message: t('common.pleaseInput') + t('user.oldAccountPlaceholder'), trigger: 'blur' }
],
code: [
{ required: true, message: t('common.pleaseInput') + t('user.migrateCodePlaceholder'), trigger: 'blur' }
]
})
// 提交表单
const handleSubmit = async () => {
migrateForm.value.validate().then(({ valid, errors }: any) => {
if (valid) {
uni.showModal({
title: t('global.tips'),
content: t('user.migrateWarning'),
success: (res: any) => {
if (res.confirm) {
submitMigrate()
}
}
})
}
})
.catch((error: any) => {
console.log(error)
})
}
// 处理迁移
const submitMigrate = async () => {
await migrateUserData(formData.value)
uni.showToast({
title: t('user.migrateSuccess'),
icon: 'success'
})
// 清空表单
formData.value.tel = ''
formData.value.code = ''
}
</script>
<style lang="scss" scoped>
.page-wrap {
padding: 20rpx;
background-color: #F8F9FA;
min-height: 100vh;
}
.migrate-card {
background: #ffffff;
border-radius: 16rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.migrate-btn {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
border-radius: 12rpx;
color: #ffffff;
font-size: 32rpx;
font-weight: 500;
margin-top: 40rpx;
&:not([disabled]):active {
opacity: 0.8;
}
&[disabled] {
background: #cccccc;
color: #ffffff;
}
}
.instructions-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
margin-bottom: 30rpx;
}
.instructions-content {
.instruction-item {
display: flex;
align-items: flex-start;
margin-bottom: 20rpx;
line-height: 1.6;
&:last-child {
margin-bottom: 0;
}
}
.instruction-number {
font-size: 28rpx;
color: #007aff;
font-weight: 600;
margin-right: 16rpx;
min-width: 40rpx;
}
.instruction-text {
font-size: 28rpx;
color: #666666;
flex: 1;
}
}
</style>

View File

@@ -211,12 +211,27 @@
max-width: 96rem;
}
}
.mb-2 {
margin-bottom: calc(var(--spacing) * 2);
}
.mb-2\! {
margin-bottom: calc(var(--spacing) * 2) !important;
}
.mb-\[20rpx\] {
margin-bottom: 20rpx;
}
.mb-\[20rpx\]\! {
margin-bottom: 20rpx !important;
}
.ml-1 {
margin-left: calc(var(--spacing) * 1);
}
.ml-1\! {
margin-left: calc(var(--spacing) * 1) !important;
}
.ml-2 {
margin-left: calc(var(--spacing) * 2);
}
.ml-2\.5\! {
margin-left: calc(var(--spacing) * 2.5) !important;
}
@@ -253,6 +268,9 @@
.flex-shrink {
flex-shrink: 1;
}
.border-collapse {
border-collapse: collapse;
}
.transform {
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
}
@@ -266,18 +284,39 @@
border-style: var(--tw-border-style);
border-width: 1px;
}
.p-0 {
padding: calc(var(--spacing) * 0);
}
.p-0\! {
padding: calc(var(--spacing) * 0) !important;
}
.p-\[10rpx\] {
padding: 10rpx;
}
.p-\[30rpx\] {
padding: 30rpx;
}
.pt-1 {
padding-top: calc(var(--spacing) * 1);
}
.pt-10 {
padding-top: calc(var(--spacing) * 10);
}
.pt-\[10rpx\] {
padding-top: 10rpx;
}
.pt-\[20rpx\] {
padding-top: 20rpx;
}
.pb-0 {
padding-bottom: calc(var(--spacing) * 0);
}
.pb-0\! {
padding-bottom: calc(var(--spacing) * 0) !important;
}
.pb-\[20rpx\] {
padding-bottom: 20rpx;
}
.text-center {
text-align: center;
}
@@ -303,6 +342,9 @@
.text-\[\#7dc1f0\] {
color: #7dc1f0;
}
.text-\[14rpx\] {
color: 14rpx;
}
.text-\[cadetblue\] {
color: cadetblue;
}
@@ -322,6 +364,9 @@
--tw-ordinal: ordinal;
font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
}
.underline {
text-decoration-line: underline;
}
.ring {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);