This commit is contained in:
Zyronon
2025-11-26 01:33:04 +08:00
parent da0f69f085
commit d61a63c9ce
129 changed files with 1527 additions and 582 deletions

View File

@@ -7,6 +7,7 @@ import {useRouter} from "vue-router";
import useTheme from "@/hooks/theme.ts";
import BaseIcon from "@/components/BaseIcon.vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
import { jump2Feedback } from "@/utils";
const settingStore = useSettingStore()
const runtimeStore = useRuntimeStore()
@@ -17,9 +18,6 @@ const {toggleTheme, getTheme} = useTheme()
function goHome() {
window.location.href = '/';
}
function goFeedback() {
window.open('https://v.wjx.cn/vm/ev0W7fv.aspx#', '_blank');
}
</script>
<template>
@@ -47,14 +45,14 @@ function goFeedback() {
<span v-if="settingStore.sideExpand">设置</span>
<div class="red-point" :class="!settingStore.sideExpand && 'top-1 right-0'" v-if="runtimeStore.isNew"></div>
</div>
<div class="row" @click="goFeedback">
<div class="row" @click="jump2Feedback">
<IconFluentCommentEdit20Regular/>
<span v-if="settingStore.sideExpand">建议反馈</span>
</div>
<!-- <div class="row" @click="router.push('/user')">-->
<!-- <IconFluentPerson20Regular/>-->
<!-- <span v-if="settingStore.sideExpand">用户</span>-->
<!-- </div>-->
<div class="row" @click="router.push('/user')">
<IconFluentPerson20Regular/>
<span v-if="settingStore.sideExpand">用户</span>
</div>
</div>
<div class="bottom flex justify-evenly ">
<BaseIcon

View File

@@ -1,26 +1,26 @@
<script setup lang="tsx">
import {onBeforeUnmount, onMounted} from 'vue'
import {useRoute} from 'vue-router'
import { onBeforeUnmount, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import BaseInput from "@/components/base/BaseInput.vue";
import BaseButton from "@/components/BaseButton.vue";
import {APP_NAME} from "@/config/env.ts";
import {useUserStore} from "@/stores/user.ts";
import {loginApi, LoginParams, registerApi, resetPasswordApi} from "@/apis/user.ts";
import {accountRules, codeRules, passwordRules, phoneRules} from "@/utils/validation.ts";
import { APP_NAME } from "@/config/env.ts";
import { useUserStore } from "@/stores/user.ts";
import { loginApi, LoginParams, registerApi, resetPasswordApi } from "@/apis/user.ts";
import { accountRules, codeRules, passwordRules, phoneRules } from "@/utils/validation.ts";
import Toast from "@/components/base/toast/Toast.ts";
import FormItem from "@/components/base/form/FormItem.vue";
import Form from "@/components/base/form/Form.vue";
import Notice from "@/pages/user/Notice.vue";
import {FormInstance} from "@/components/base/form/types.ts";
import {PASSWORD_CONFIG, PHONE_CONFIG} from "@/config/auth.ts";
import {CodeType, ImportStatus} from "@/types/types.ts";
import { FormInstance } from "@/components/base/form/types.ts";
import { PASSWORD_CONFIG, PHONE_CONFIG } from "@/config/auth.ts";
import { CodeType, ImportStatus } from "@/types/types.ts";
import Code from "@/pages/user/Code.vue";
import {isNewUser, sleep, useNav} from "@/utils";
import { isNewUser, jump2Feedback, sleep, useNav } from "@/utils";
import Header from "@/components/Header.vue";
import PopConfirm from "@/components/PopConfirm.vue";
import {useExport} from "@/hooks/export.ts";
import {getProgress, upload, uploadImportData} from "@/apis";
import {Exception} from "sass";
import { useExport } from "@/hooks/export.ts";
import { getProgress, upload, uploadImportData } from "@/apis";
import { Exception } from "sass";
// 状态管理
const userStore = useUserStore()
@@ -295,7 +295,7 @@ enum ImportStep {
}
const {exportData} = useExport()
let importStep = $ref<ImportStep>(ImportStep.CONFIRMATION)
let importStep = $ref<ImportStep>(ImportStep.SUCCESS)
let isImporting = $ref(false)
let reason = $ref('')
let timer = $ref(-1)
@@ -358,6 +358,10 @@ function logout() {
function forgetData() {
}
function goHome(){
}
</script>
@@ -375,28 +379,28 @@ function forgetData() {
<!-- Tab切换 -->
<div class="center gap-8 mb-6">
<div
class="center cp transition-colors"
:class="loginType === 'code' ? 'link font-medium' : 'text-gray-600'"
@click="loginType = 'code'"
class="center cp transition-colors"
:class="loginType === 'code' ? 'link font-medium' : 'text-gray-600'"
@click="loginType = 'code'"
>
<div>
<span>验证码登录</span>
<div
v-opacity="loginType === 'code'"
class="mt-1 h-0.5 bg-blue-600"
v-opacity="loginType === 'code'"
class="mt-1 h-0.5 bg-blue-600"
></div>
</div>
</div>
<div
class="center cp transition-colors"
:class="loginType === 'password' ? 'link font-medium' : 'text-gray-600'"
@click="loginType = 'password'"
class="center cp transition-colors"
:class="loginType === 'password' ? 'link font-medium' : 'text-gray-600'"
@click="loginType = 'password'"
>
<div>
<span>密码登录</span>
<div
v-opacity="loginType === 'password'"
class="mt-1 h-0.5 bg-blue-600"
v-opacity="loginType === 'password'"
class="mt-1 h-0.5 bg-blue-600"
></div>
</div>
</div>
@@ -404,10 +408,10 @@ function forgetData() {
<!-- 验证码登录表单 -->
<Form
v-if="loginType === 'code'"
ref="phoneLoginFormRef"
:rules="phoneLoginFormRules"
:model="phoneLoginForm">
v-if="loginType === 'code'"
ref="phoneLoginFormRef"
:rules="phoneLoginFormRules"
:model="phoneLoginForm">
<FormItem prop="phone">
<BaseInput v-model="phoneLoginForm.phone"
type="tel"
@@ -420,11 +424,11 @@ function forgetData() {
<FormItem prop="code">
<div class="flex gap-2">
<BaseInput
v-model="phoneLoginForm.code"
type="code"
size="large"
:max-length="PHONE_CONFIG.codeLength"
placeholder="请输入验证码"
v-model="phoneLoginForm.code"
type="code"
size="large"
:max-length="PHONE_CONFIG.codeLength"
placeholder="请输入验证码"
/>
<Code :validate-field="() => phoneLoginFormRef.validateField('phone')"
:type="CodeType.Login"
@@ -435,10 +439,10 @@ function forgetData() {
<!-- 密码登录表单 -->
<Form
v-else
ref="loginForm2Ref"
:rules="loginForm2Rules"
:model="loginForm2">
v-else
ref="loginForm2Ref"
:rules="loginForm2Rules"
:model="loginForm2">
<FormItem prop="account">
<BaseInput v-model="loginForm2.account"
type="email"
@@ -451,12 +455,12 @@ function forgetData() {
<FormItem prop="password">
<div class="flex gap-2">
<BaseInput
v-model="loginForm2.password"
type="password"
name="password"
autocomplete="current-password"
size="large"
placeholder="请输入密码"
v-model="loginForm2.password"
type="password"
name="password"
autocomplete="current-password"
size="large"
placeholder="请输入密码"
/>
</div>
</FormItem>
@@ -467,10 +471,10 @@ function forgetData() {
</Notice>
<BaseButton
class="w-full"
size="large"
:loading="loading"
@click="handleLogin"
class="w-full"
size="large"
:loading="loading"
@click="handleLogin"
>
登录
</BaseButton>
@@ -487,27 +491,27 @@ function forgetData() {
<Header @click="switchMode('login')" title="注册新账号"/>
<Form
ref="registerFormRef"
:rules="registerFormRules"
:model="registerForm">
ref="registerFormRef"
:rules="registerFormRules"
:model="registerForm">
<FormItem prop="account">
<BaseInput
v-model="registerForm.account"
type="tel"
name="username"
autocomplete="username"
size="large"
placeholder="请输入手机号/邮箱地址"
v-model="registerForm.account"
type="tel"
name="username"
autocomplete="username"
size="large"
placeholder="请输入手机号/邮箱地址"
/>
</FormItem>
<FormItem prop="code">
<div class="flex gap-2">
<BaseInput
v-model="registerForm.code"
type="code"
size="large"
placeholder="请输入验证码"
:max-length="PHONE_CONFIG.codeLength"
v-model="registerForm.code"
type="code"
size="large"
placeholder="请输入验证码"
:max-length="PHONE_CONFIG.codeLength"
/>
<Code :validate-field="() => registerFormRef.validateField('account')"
:type="CodeType.Register"
@@ -516,22 +520,22 @@ function forgetData() {
</FormItem>
<FormItem prop="password">
<BaseInput
v-model="registerForm.password"
type="password"
name="password"
autocomplete="current-password"
size="large"
:placeholder="`请设置密码(${PASSWORD_CONFIG.minLength}-${PASSWORD_CONFIG.maxLength} 位)`"
v-model="registerForm.password"
type="password"
name="password"
autocomplete="current-password"
size="large"
:placeholder="`请设置密码(${PASSWORD_CONFIG.minLength}-${PASSWORD_CONFIG.maxLength} 位)`"
/>
</FormItem>
<FormItem prop="confirmPassword">
<BaseInput
v-model="registerForm.confirmPassword"
type="password"
name="password"
autocomplete="new-password"
size="large"
placeholder="请再次输入密码"
v-model="registerForm.confirmPassword"
type="password"
name="password"
autocomplete="new-password"
size="large"
placeholder="请再次输入密码"
/>
</FormItem>
</Form>
@@ -539,10 +543,10 @@ function forgetData() {
<Notice/>
<BaseButton
class="w-full"
size="large"
:loading="loading"
@click="handleRegister"
class="w-full"
size="large"
:loading="loading"
@click="handleRegister"
>
注册
</BaseButton>
@@ -554,27 +558,27 @@ function forgetData() {
<Header @click="switchMode('login')" title="重置密码"/>
<Form
ref="forgotFormRef"
:rules="forgotFormRules"
:model="forgotForm">
ref="forgotFormRef"
:rules="forgotFormRules"
:model="forgotForm">
<FormItem prop="account">
<BaseInput
v-model="forgotForm.account"
type="tel"
name="username"
autocomplete="username"
size="large"
placeholder="请输入手机号/邮箱地址"
v-model="forgotForm.account"
type="tel"
name="username"
autocomplete="username"
size="large"
placeholder="请输入手机号/邮箱地址"
/>
</FormItem>
<FormItem prop="code">
<div class="flex gap-2">
<BaseInput
v-model="forgotForm.code"
type="code"
size="large"
placeholder="请输入验证码"
:max-length="PHONE_CONFIG.codeLength"
v-model="forgotForm.code"
type="code"
size="large"
placeholder="请输入验证码"
:max-length="PHONE_CONFIG.codeLength"
/>
<Code :validate-field="() => forgotFormRef.validateField('account')"
:type="CodeType.ResetPwd"
@@ -583,31 +587,31 @@ function forgetData() {
</FormItem>
<FormItem prop="newPassword">
<BaseInput
v-model="forgotForm.newPassword"
type="password"
name="password"
autocomplete="new-password"
size="large"
:placeholder="`请输入新密码(${PASSWORD_CONFIG.minLength}-${PASSWORD_CONFIG.maxLength} 位)`"
v-model="forgotForm.newPassword"
type="password"
name="password"
autocomplete="new-password"
size="large"
:placeholder="`请输入新密码(${PASSWORD_CONFIG.minLength}-${PASSWORD_CONFIG.maxLength} 位)`"
/>
</FormItem>
<FormItem prop="confirmPassword">
<BaseInput
v-model="forgotForm.confirmPassword"
type="password"
name="password"
autocomplete="new-password"
size="large"
placeholder="请再次输入新密码"
v-model="forgotForm.confirmPassword"
type="password"
name="password"
autocomplete="new-password"
size="large"
placeholder="请再次输入新密码"
/>
</FormItem>
</Form>
<BaseButton
class="w-full mt-2"
size="large"
:loading="loading"
@click="handleForgotPassword"
class="w-full mt-2"
size="large"
:loading="loading"
@click="handleForgotPassword"
>
重置密码
</BaseButton>
@@ -618,16 +622,16 @@ function forgetData() {
<div v-if="currentMode === 'login'" class="center flex-col bg-gray-100 rounded-xl px-12">
<div class="relative w-40 h-40 bg-white rounded-xl overflow-hidden shadow-xl">
<img
v-if="showWechatQR"
:src="wechatQRUrl"
alt="微信登录二维码"
class="w-full h-full"
:class="{ 'opacity-30': qrStatus === 'expired' }"
v-if="showWechatQR"
:src="wechatQRUrl"
alt="微信登录二维码"
class="w-full h-full"
:class="{ 'opacity-30': qrStatus === 'expired' }"
/>
<!-- 扫描成功蒙层 -->
<div
v-if="qrStatus === 'scanned'"
class="absolute left-0 top-0 w-full h-full center flex-col gap-space bg-white"
v-if="qrStatus === 'scanned'"
class="absolute left-0 top-0 w-full h-full center flex-col gap-space bg-white"
>
<IconFluentCheckmarkCircle20Filled class="color-green text-4xl"/>
<div class="text-base text-gray-700 font-medium">扫描成功</div>
@@ -635,8 +639,8 @@ function forgetData() {
</div>
<!-- 取消登录蒙层 -->
<div
v-if="qrStatus === 'cancelled'"
class="absolute left-0 top-0 w-full h-full center flex-col gap-space bg-white"
v-if="qrStatus === 'cancelled'"
class="absolute left-0 top-0 w-full h-full center flex-col gap-space bg-white"
>
<IconFluentErrorCircle20Regular class="color-red text-4xl"/>
<div class="text-base text-gray-700 font-medium">你已取消此次登录</div>
@@ -645,12 +649,12 @@ function forgetData() {
</div>
<!-- 过期蒙层 -->
<div
v-if=" qrStatus === 'expired'"
class="absolute top-0 left-0 right-0 bottom-0 bg-opacity-95 center backdrop-blur-sm"
v-if=" qrStatus === 'expired'"
class="absolute top-0 left-0 right-0 bottom-0 bg-opacity-95 center backdrop-blur-sm"
>
<IconFluentArrowClockwise20Regular
@click="refreshQRCode"
class="cp text-4xl"/>
@click="refreshQRCode"
class="cp text-4xl"/>
</div>
</div>
<p class="mt-4 center gap-space">
@@ -662,7 +666,7 @@ function forgetData() {
</div>
<div v-else class="card-white p-6 w-100">
<div class="title">同步数据确认</div>
<Header @click="logout" title="同步数据"></Header>
<div class="flex flex-col justify-between h-60">
<template v-if="importStep === ImportStep.CONFIRMATION">
<div>
@@ -671,8 +675,6 @@ function forgetData() {
</div>
<div class="flex gap-space justify-end">
<template v-if="importStep === ImportStep.CONFIRMATION">
<BaseButton type="info" @click="logout">退出登录</BaseButton>
<PopConfirm :title="[
{text:'您的用户数据将以压缩包自动下载到您的电脑中,以便您随时恢复',type:'normal'},
{text:'随后网站的用户数据将被删除',type:'redBold'},
@@ -680,15 +682,15 @@ function forgetData() {
]"
@confirm="forgetData"
>
<BaseButton type="info">放弃数据</BaseButton>
<BaseButton type="info">不同步</BaseButton>
</PopConfirm>
</template>
<BaseButton @click="startSync">确认同步</BaseButton>
<BaseButton @click="startSync">同步</BaseButton>
</div>
</template>
<template v-if="importStep === ImportStep.PROCESSING">
<div>
<h3 class="text-align-center">正在导入中</h3>
<div class="title text-align-center">正在导入中</div>
<ol class="pl-4">
<li>
您的用户数据已自动下载到您的电脑中,以便随时恢复
@@ -711,31 +713,28 @@ function forgetData() {
</template>
<template v-if="importStep === ImportStep.FAIL">
<div>
<h3 class="text-align-center">同步失败</h3>
<div class="title text-align-center">同步失败</div>
<div class="mt-10">
<span>{{ reason }}</span>
</div>
</div>
<div class="flex gap-space justify-end">
<BaseButton type="info" @click="logout">退出登录</BaseButton>
<PopConfirm :title="[
{text:'您的用户数据将以压缩包自动下载到您的电脑中,以便您随时恢复',type:'normal'},
{text:'随后网站的用户数据将被删除',type:'redBold'},
{text:'是否确认继续?',type:'normal'},
]"
@confirm="forgetData"
>
<BaseButton type="info">放弃数据</BaseButton>
</PopConfirm>
<BaseButton @click="startSync">再次同步</BaseButton>
<div class="flex justify-end">
<BaseButton type="info" @click="jump2Feedback">反馈</BaseButton>
<BaseButton @click="goHome">进入网站</BaseButton>
</div>
</template>
<template v-if="importStep === ImportStep.SUCCESS">
<div>
<div class="title text-align-center">同步成功</div>
<div class="mt-10">
<span>稍后将自动进入网站...</span>
</div>
</div>
<div class="flex justify-end">
<BaseButton @click="goHome">进入网站</BaseButton>
</div>
</template>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
</style>
</template>]

View File

@@ -458,4 +458,8 @@ export async function isNewUser() {
console.log(JSON.stringify(base.$state))
console.log(JSON.stringify(getDefaultBaseState()))
return JSON.stringify(base.$state) === JSON.stringify({...getDefaultBaseState(), ...{load: true}})
}
export function jump2Feedback(){
window.open('https://v.wjx.cn/vm/ev0W7fv.aspx#', '_blank');
}