diff --git a/components.d.ts b/components.d.ts index a673d1c1..13129be7 100644 --- a/components.d.ts +++ b/components.d.ts @@ -39,8 +39,8 @@ declare module 'vue' { IconFluentAddSquare20Regular: typeof import('~icons/fluent/add-square20-regular')['default'] IconFluentArrowBounce20Regular: typeof import('~icons/fluent/arrow-bounce20-regular')['default'] IconFluentArrowCircleRight16Regular: typeof import('~icons/fluent/arrow-circle-right16-regular')['default'] + IconFluentArrowClockwise20Regular: typeof import('~icons/fluent/arrow-clockwise20-regular')['default'] IconFluentArrowLeft16Regular: typeof import('~icons/fluent/arrow-left16-regular')['default'] - IconFluentArrowMove20Regular: typeof import('~icons/fluent/arrow-move20-regular')['default'] IconFluentArrowRight16Regular: typeof import('~icons/fluent/arrow-right16-regular')['default'] IconFluentArrowShuffle16Regular: typeof import('~icons/fluent/arrow-shuffle16-regular')['default'] IconFluentArrowShuffle20Filled: typeof import('~icons/fluent/arrow-shuffle20-filled')['default'] @@ -56,11 +56,11 @@ declare module 'vue' { IconFluentChevronLeft28Filled: typeof import('~icons/fluent/chevron-left28-filled')['default'] IconFluentDatabasePerson20Regular: typeof import('~icons/fluent/database-person20-regular')['default'] IconFluentDelete20Regular: typeof import('~icons/fluent/delete20-regular')['default'] - IconFluentDismiss12Regular: typeof import('~icons/fluent/dismiss12-regular')['default'] IconFluentDismiss20Regular: typeof import('~icons/fluent/dismiss20-regular')['default'] IconFluentDismissCircle16Regular: typeof import('~icons/fluent/dismiss-circle16-regular')['default'] IconFluentDismissCircle20Filled: typeof import('~icons/fluent/dismiss-circle20-filled')['default'] IconFluentErrorCircle20Filled: typeof import('~icons/fluent/error-circle20-filled')['default'] + IconFluentErrorCircle20Regular: typeof import('~icons/fluent/error-circle20-regular')['default'] IconFluentEye16Regular: typeof import('~icons/fluent/eye16-regular')['default'] IconFluentEyeOff16Regular: typeof import('~icons/fluent/eye-off16-regular')['default'] IconFluentHome20Regular: typeof import('~icons/fluent/home20-regular')['default'] @@ -91,6 +91,7 @@ declare module 'vue' { IconFluentWeatherMoon16Regular: typeof import('~icons/fluent/weather-moon16-regular')['default'] IconFluentWeatherSunny16Regular: typeof import('~icons/fluent/weather-sunny16-regular')['default'] IconIconParkOutlineAddMusic: typeof import('~icons/icon-park-outline/add-music')['default'] + IconIxWechatLogo: typeof import('~icons/ix/wechat-logo')['default'] IconPhExportLight: typeof import('~icons/ph/export-light')['default'] IconSystemUiconsImport: typeof import('~icons/system-uicons/import')['default'] InputNumber: typeof import('./src/components/base/InputNumber.vue')['default'] diff --git a/package.json b/package.json index ba6bd345..13438bf0 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@iconify-json/fluent": "^1.2.28", "@iconify-json/icon-park-outline": "^1.2.4", "@iconify-json/icon-park-solid": "^1.2.4", + "@iconify-json/ix": "^1.2.10", "@iconify-json/material-symbols": "^1.2.33", "@iconify-json/oui": "^1.2.6", "@iconify-json/ph": "^1.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fc6dd6c..1b46c643 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,6 +75,9 @@ importers: '@iconify-json/icon-park-solid': specifier: ^1.2.4 version: 1.2.4 + '@iconify-json/ix': + specifier: ^1.2.10 + version: 1.2.10 '@iconify-json/material-symbols': specifier: ^1.2.33 version: 1.2.33 @@ -171,9 +174,6 @@ importers: vite-plugin-externals: specifier: ^0.6.2 version: 0.6.2(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(sass@1.90.0)) - vite-plugin-mpa: - specifier: ^1.2.0 - version: 1.2.0 vue-tsc: specifier: ^3.0.1 version: 3.0.5(typescript@5.9.2) @@ -517,6 +517,9 @@ packages: '@iconify-json/icon-park-solid@1.2.4': resolution: {integrity: sha512-030MChSP6lCY7N+U5J5R7YguHTGcm7qQEI/ivBjk77El/i8yJatoj568cwwXGM8c6HEU/kIxEE4m3O/6w0WBGg==} + '@iconify-json/ix@1.2.10': + resolution: {integrity: sha512-2NMqsW+sMyH+cpRnRW6mVqJM/q3Mbb7UVY9NWJJEJfHGn1SbzZde/jpgEmTZe5jMJMPQGWhaCzbGsTMrFim+3Q==} + '@iconify-json/material-symbols@1.2.33': resolution: {integrity: sha512-Bs0X1+/vpJydW63olrGh60zkR8/Y70sI14AIWaP7Z6YQXukzWANH4q3I0sIPklbIn1oL6uwLvh0zQyd6Vh79LQ==} @@ -1547,9 +1550,6 @@ packages: cliui@3.2.0: resolution: {integrity: sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==} - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1637,10 +1637,6 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} - connect-history-api-fallback@1.6.0: - resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==} - engines: {node: '>=0.8'} - consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -3176,11 +3172,6 @@ packages: resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} engines: {node: '>=0.10.0'} - shelljs@0.8.5: - resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} - engines: {node: '>=4'} - hasBin: true - side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -3646,9 +3637,6 @@ packages: peerDependencies: vite: '>=2.0.0' - vite-plugin-mpa@1.2.0: - resolution: {integrity: sha512-A1G+CnnUkDuff2i+Z/RWeQMb8yj3FH9n7+KTEXxkOSeMRQ7v3Xy/tKtaMjPxW6n8zSOE/BbyzQAAX0RAoUd2AA==} - vite@7.1.2: resolution: {integrity: sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3800,10 +3788,6 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -3811,10 +3795,6 @@ packages: yargs-parser@5.0.1: resolution: {integrity: sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==} - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -4171,6 +4151,10 @@ snapshots: dependencies: '@iconify/types': 2.0.0 + '@iconify-json/ix@1.2.10': + dependencies: + '@iconify/types': 2.0.0 + '@iconify-json/material-symbols@1.2.33': dependencies: '@iconify/types': 2.0.0 @@ -5415,12 +5399,6 @@ snapshots: strip-ansi: 3.0.1 wrap-ansi: 2.1.0 - cliui@7.0.4: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -5517,8 +5495,6 @@ snapshots: confbox@0.2.2: {} - connect-history-api-fallback@1.6.0: {} - consola@3.4.2: {} content-type@1.0.5: {} @@ -7159,12 +7135,6 @@ snapshots: is-plain-object: 2.0.4 split-string: 3.1.0 - shelljs@0.8.5: - dependencies: - glob: 7.2.3 - interpret: 1.4.0 - rechoir: 0.6.2 - side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -7720,12 +7690,6 @@ snapshots: magic-string: 0.25.9 vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(sass@1.90.0) - vite-plugin-mpa@1.2.0: - dependencies: - connect-history-api-fallback: 1.6.0 - shelljs: 0.8.5 - yargs: 16.2.0 - vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(sass@1.90.0): dependencies: esbuild: 0.25.9 @@ -7845,8 +7809,6 @@ snapshots: yallist@3.1.1: {} - yargs-parser@20.2.9: {} - yargs-parser@21.1.1: {} yargs-parser@5.0.1: @@ -7854,16 +7816,6 @@ snapshots: camelcase: 3.0.0 object.assign: 4.1.7 - yargs@16.2.0: - dependencies: - cliui: 7.0.4 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - yargs@17.7.2: dependencies: cliui: 8.0.1 diff --git a/public/privacy-policy.html b/public/privacy-policy.html new file mode 100644 index 00000000..78c80663 --- /dev/null +++ b/public/privacy-policy.html @@ -0,0 +1,100 @@ + + + + + + 隐私政策 + + +
+

隐私政策

+
+
+

一、引言

+

+ 我们非常重视您的隐私保护。本隐私政策说明了我们如何收集、使用、存储和保护您的个人信息。在使用本应用之前,请您仔细阅读本隐私政策。

+
+ +
+

二、信息收集

+

我们可能收集以下信息:

+

1. 账户信息:当您注册账户时,我们会收集您的手机号、邮箱地址、密码等信息。

+

2. 学习数据:我们会记录您的学习进度、学习记录、练习数据等信息,以便为您提供个性化的学习服务。 +

+

3. 设备信息:我们可能收集您的设备型号、操作系统版本、唯一设备标识符等信息,用于改善服务质量和安全性。 +

+

4. 日志信息:当您使用本应用时,我们可能自动收集某些信息,包括IP地址、访问时间、访问页面等。 +

+
+ +
+

三、信息使用

+

我们使用收集的信息用于以下目的:

+

1. 提供、维护和改进我们的服务;

+

2. 处理您的注册、登录、学习记录等请求;

+

3. 向您发送服务通知、更新和安全提醒;

+

4. 进行数据分析,以改善用户体验和服务质量;

+

5. 检测、预防和解决技术问题;

+

6. 遵守法律法规要求。

+
+ +
+

四、信息存储

+

1. 我们采用行业标准的安全措施来保护您的个人信息,防止未经授权的访问、使用或泄露。

+

2. 您的个人信息将存储在安全的服务器上,我们会对数据进行加密处理。

+

3. 我们仅在为实现本隐私政策所述目的所必需的期间内保留您的个人信息。

+
+ +
+

五、信息共享

+

我们不会向第三方出售、交易或转让您的个人信息,除非:

+

1. 获得您的明确同意;

+

2. 法律法规要求或司法机关、行政机关依法要求提供;

+

3. 为履行我们的服务协议或本隐私政策,我们可能需要与我们的服务提供商共享某些信息。

+
+ +
+

六、Cookie和类似技术

+

+ 我们可能使用Cookie和类似技术来收集信息、改善用户体验和分析服务使用情况。您可以通过浏览器设置管理Cookie,但这可能影响某些功能的正常使用。

+
+ +
+

七、您的权利

+

根据相关法律法规,您对自己的个人信息享有以下权利:

+

1. 访问权:您有权访问我们持有的关于您的个人信息;

+

2. 更正权:您有权要求更正不准确的个人信息;

+

3. 删除权:在特定情况下,您有权要求删除您的个人信息;

+

4. 撤回同意:您有权随时撤回您之前给予的同意;

+

5. 投诉权:如果您认为我们对您个人信息的处理违反了相关法律法规,您有权向相关监管部门投诉。 +

+
+ +
+

八、未成年人保护

+

+ 我们非常重视未成年人的个人信息保护。如果您是未成年人,建议您请您的父母或监护人仔细阅读本隐私政策,并在征得您的父母或监护人同意的前提下使用我们的服务。

+
+ +
+

九、隐私政策更新

+

+ 我们可能会不时更新本隐私政策。我们会在本页面上发布新的隐私政策,并通过适当方式通知您。如果您不同意更新后的隐私政策,您可以选择停止使用我们的服务。

+
+ +
+

十、联系我们

+

如果您对本隐私政策有任何疑问、意见或建议,或需要行使您的相关权利,请通过以下方式联系我们:

+

邮箱:zyronon@163.com

+
+ +
+

最后更新时间:2025年11月11日

+
+
+
+ + \ No newline at end of file diff --git a/public/user-agreement.html b/public/user-agreement.html new file mode 100644 index 00000000..20fd737a --- /dev/null +++ b/public/user-agreement.html @@ -0,0 +1,83 @@ + + + + + + 用户协议 + + +
+

用户协议

+
+
+

一、总则

+

欢迎使用本应用!在使用本应用之前,请您仔细阅读本用户协议(以下简称"本协议")。当您注册、登录、使用(以下统称"使用")本应用时,即表示您已阅读、理解并同意接受本协议的全部内容。

+
+ +
+

二、服务内容

+

本应用为用户提供单词学习、文章阅读等在线教育服务。我们保留随时修改或中断服务而不需通知用户的权利,我们行使修改或中断服务的权利,不需对用户或第三方负责。

+
+ +
+

三、用户账户

+

1. 用户在使用本应用前需要注册一个账户。用户应当使用真实、准确、完整的信息注册账户。

+

2. 用户有责任维护账户信息的安全,对账户下的所有活动负责。

+

3. 用户不得将账户转让、出售或以其他方式提供给第三方使用。

+
+ +
+

四、用户行为规范

+

用户在使用本应用时,应当遵守相关法律法规,不得从事以下行为:

+

1. 发布、传播违法、有害、威胁、辱骂、骚扰、侵权、诽谤、淫秽、暴力或其他不当内容;

+

2. 侵犯他人知识产权、隐私权或其他合法权益;

+

3. 干扰或破坏本应用的正常运行;

+

4. 使用自动化工具或脚本进行数据采集、批量操作等;

+

5. 其他违反法律法规或本协议的行为。

+
+ +
+

五、知识产权

+

1. 本应用的所有内容,包括但不限于文字、图片、音频、视频、软件、程序、版面设计等,均受知识产权法保护。

+

2. 未经我们书面许可,用户不得复制、传播、展示、镜像、上传、下载本应用的任何内容。

+
+ +
+

六、隐私保护

+

我们重视用户的隐私保护。关于我们如何收集、使用、存储和保护您的个人信息,请详见《隐私政策》。

+
+ +
+

七、免责声明

+

1. 用户明确同意使用本应用的风险由用户个人承担。

+

2. 我们不对因不可抗力或非我们原因造成的服务中断或终止承担责任。

+

3. 我们不对用户在使用本应用过程中产生的任何直接、间接、偶然、特殊及后续的损害承担责任。

+
+ +
+

八、协议修改

+

我们有权随时修改本协议的任何条款。一旦本协议的内容发生变动,我们将会通过适当方式向用户提示修改内容。如果用户不同意我们对本协议相关条款所做的修改,用户有权停止使用本应用。如果用户继续使用本应用,则视为用户接受我们对本协议相关条款所做的修改。

+
+ +
+

九、法律适用与争议解决

+

1. 本协议的订立、执行和解释及争议的解决均应适用中华人民共和国法律。

+

2. 如双方就本协议内容或其执行发生任何争议,双方应尽量友好协商解决;协商不成时,任何一方均可向我们所在地的人民法院提起诉讼。

+
+ +
+

十、其他

+

1. 本协议构成双方对本协议之约定事项及其他有关事宜的完整协议,除本协议规定的之外,未赋予本协议各方其他权利。

+

2. 如本协议中的任何条款无论因何种原因完全或部分无效或不具有执行力,本协议的其余条款仍应有效并且有约束力。

+
+ +
+

最后更新时间:2025年11月11日

+
+
+
+ + \ No newline at end of file diff --git a/src/apis/index.ts b/src/apis/index.ts index 6b61335d..265216f8 100644 --- a/src/apis/index.ts +++ b/src/apis/index.ts @@ -48,7 +48,7 @@ export function addDict(params?, data?) { return http('dict/addDict', remove(data), remove(params), 'post') } -export function uploadImportData(data,onUploadProgress) { +export function uploadImportData(data, onUploadProgress) { return axiosInstance({ url: 'dict/uploadImportData', method: 'post', @@ -60,5 +60,7 @@ export function uploadImportData(data,onUploadProgress) { }) } -// 导出认证相关API -export * from './auth' +// 查询导入进度,status: 0=导入中, 1=完成, 2=失败 +export function getProgress(params?) { + return http<{ status: number; reason: string }>('dict/getProgress', null, params, 'get') +} diff --git a/src/apis/auth.ts b/src/apis/user.ts similarity index 55% rename from src/apis/auth.ts rename to src/apis/user.ts index 4a4891e3..1332fb4d 100644 --- a/src/apis/auth.ts +++ b/src/apis/user.ts @@ -1,11 +1,12 @@ import http from '@/utils/http.ts' + // 用户登录接口 export interface LoginParams { - email?: string - phone?: string + account?: string password?: string + phone?: string code?: string - type: 'email' | 'phone' | 'wechat' + type: 'code' | 'pwd' } export interface LoginResponse { @@ -61,28 +62,28 @@ export interface WechatLoginParams { } // API 函数定义 -export function login(params: LoginParams) { +export function loginApi(params: LoginParams) { // 暂时直接返回成功响应,等待后端接入 - return Promise.resolve({ - success: true, - code: 200, - msg: '登录成功', - data: { - token: 'mock_token_' + Date.now(), - user: { - id: '1', - email: params.email, - phone: params.phone, - nickname: '测试用户', - avatar: '' - } - } - }) - // return http('auth/login', params, null, 'post') + // return Promise.resolve({ + // success: true, + // code: 200, + // msg: '登录成功', + // data: { + // token: 'mock_token_' + Date.now(), + // user: { + // id: '1', + // account: params.account ?? 'account', + // phone: params.phone ?? 'phone', + // nickname: '测试用户', + // avatar: '' + // } + // } + // }) + return http('user/login', params, null, 'post') } -export function register(params: RegisterParams) { - return http('auth/register', params, null, 'post') +export function registerApi(params: RegisterParams) { + return http('user/register', params, null, 'post') } export function sendCode(params: SendCodeParams) { @@ -91,26 +92,26 @@ export function sendCode(params: SendCodeParams) { code: 200, msg: '登录成功', }) - return http('auth/sendCode', params, null, 'post') + return http('user/sendCode', params, null, 'post') } -export function resetPassword(params: ResetPasswordParams) { - return http('auth/resetPassword', params, null, 'post') +export function resetPasswordApi(params: ResetPasswordParams) { + return http('user/resetPassword', params, null, 'post') } export function wechatLogin(params: WechatLoginParams) { - return http('auth/wechatLogin', params, null, 'post') + return http('user/wechatLogin', params, null, 'post') } -export function logout() { - return http('auth/logout', null, null, 'post') +export function logoutApi() { + return http('user/logout', null, null, 'post') } export function refreshToken() { - return http<{ token: string }>('auth/refreshToken', null, null, 'post') + return http<{ token: string }>('user/refreshToken', null, null, 'post') } // 获取用户信息 export function getUserInfo() { - return http('auth/userInfo', null, null, 'get') + return http('user/userInfo', null, null, 'get') } diff --git a/src/pages/user/Notice.vue b/src/pages/user/Notice.vue index 9c602505..6364751e 100644 --- a/src/pages/user/Notice.vue +++ b/src/pages/user/Notice.vue @@ -6,9 +6,9 @@
继续操作即表示你阅读并同意我们的 - 用户协议 + 用户协议 与 - 隐私政策 + 隐私政策
diff --git a/src/pages/user/PrivacyPolicy.vue b/src/pages/user/PrivacyPolicy.vue deleted file mode 100644 index 561d048e..00000000 --- a/src/pages/user/PrivacyPolicy.vue +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - diff --git a/src/pages/user/UserAgreement.vue b/src/pages/user/UserAgreement.vue deleted file mode 100644 index 5d2f55d0..00000000 --- a/src/pages/user/UserAgreement.vue +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - diff --git a/src/pages/user/index.vue b/src/pages/user/index.vue index e3453d29..d9f661d5 100644 --- a/src/pages/user/index.vue +++ b/src/pages/user/index.vue @@ -4,94 +4,219 @@ import { useAuthStore } from "@/stores/auth.ts"; import { useRouter } from "vue-router"; import BaseButton from "@/components/BaseButton.vue"; import Toast from "@/components/base/toast/Toast.ts"; +import { uploadImportData, getProgress } from "@/apis/index.ts"; -const authStore = useAuthStore() -const router = useRouter() +const authStore = useAuthStore(); +const router = useRouter(); // 页面状态 -const isLoading = ref(false) +const isLoading = ref(false); + +// 同步数据状态 +const isSyncing = ref(false); +const uploadPercent = ref(0); +const progressText = ref("等待上传..."); +const syncStatus = ref(null); // 0=导入中,1=完成,2=失败 +const syncReason = ref(""); +const fileInputRef = ref(null); // 退出登录 const handleLogout = async () => { - await authStore.logout() -} + isLoading.value = true; + try { + await authStore.logout(); + } finally { + isLoading.value = false; + } +}; // 跳转到设置页面 const goToSettings = () => { - router.push('/setting') -} + router.push("/setting"); +}; onMounted(() => { - // 如果用户未登录,跳转到登录页 if (!authStore.isLoggedIn) { - router.push({path: "/login"}); - return + return; } - - // 获取用户信息 if (!authStore.user) { - authStore.fetchUserInfo() + authStore.fetchUserInfo(); } -}) +}); + +const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); + +const resetSync = () => { + isSyncing.value = false; + uploadPercent.value = 0; + progressText.value = "等待上传..."; + syncStatus.value = null; + syncReason.value = ""; +}; + +const handleSyncClick = () => { + fileInputRef.value?.click(); +}; + +const onFileSelected = async (e: Event) => { + const input = e.target as HTMLInputElement; + const file = input.files?.[0]; + input.value = ""; // 重置,便于重复选择同一文件 + if (!file) return; + + const ext = file.name.split(".").pop()?.toLowerCase(); + if (!ext || (ext !== "zip" && ext !== "json")) { + Toast.warning("仅支持上传 zip 或 json 文件"); + return; + } + + try { + isSyncing.value = true; + progressText.value = "上传中..."; + + const formData = new FormData(); + formData.append("file", file); + + await uploadImportData(formData, (event: ProgressEvent) => { + if (event.total) { + uploadPercent.value = Math.round((event.loaded / event.total) * 100); + } + }); + + progressText.value = "导入中..."; + + // 轮询导入进度,直到 status != 0 + while (true) { + const res = await getProgress(); + const { status, reason } = res as any; // http 封装返回结构按实际为准 + syncStatus.value = status; + syncReason.value = reason || ""; + + if (status !== 0) break; + await sleep(1000); + } + + if (syncStatus.value === 1) { + uploadPercent.value = 100; + progressText.value = "导入完成"; + Toast.success("数据同步成功"); + } else if (syncStatus.value === 2) { + progressText.value = "导入失败"; + Toast.error(syncReason.value || "导入失败"); + } + } catch (err: any) { + progressText.value = "上传或导入失败"; + Toast.error(err?.message || "上传失败"); + } finally { + // 保留结果展示片刻,再复位 + setTimeout(() => resetSync(), 1500); + } +}; diff --git a/src/pages/user/login.vue b/src/pages/user/login.vue index c7ba312b..4c5f2cb6 100644 --- a/src/pages/user/login.vue +++ b/src/pages/user/login.vue @@ -1,18 +1,18 @@