Compare commits
2 Commits
622860aa0c
...
632caa28ac
| Author | SHA1 | Date |
|---|---|---|
|
|
632caa28ac | |
|
|
92db732e7e |
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Dictionay</title>
|
||||
<title>Lexiverse</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
import apiClient from './client'
|
||||
|
||||
/**
|
||||
* AI助手问答请求参数
|
||||
*/
|
||||
export interface AiQuestionRequest {
|
||||
word: string
|
||||
question: string
|
||||
}
|
||||
|
||||
/**
|
||||
* AI助手问答响应
|
||||
*/
|
||||
export interface AiQuestionResponse {
|
||||
word: string
|
||||
answer: string
|
||||
model: string
|
||||
tokens_used: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 词语智能问答
|
||||
* @param data 问答参数
|
||||
* @returns AI回答
|
||||
*/
|
||||
export const askAiAssistant = async (data: AiQuestionRequest): Promise<AiQuestionResponse> => {
|
||||
try {
|
||||
const response = await apiClient.post<AiQuestionResponse>('/ai_assist/word/exp', data)
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
console.error('AI Assistant API error:', error)
|
||||
|
||||
if (error.response) {
|
||||
const status = error.response.status
|
||||
const message = error.response.data?.message || 'AI助手请求失败'
|
||||
|
||||
switch (status) {
|
||||
case 400:
|
||||
throw new Error('本月API使用量已超')
|
||||
case 401:
|
||||
throw new Error('请先登录后再使用AI助手')
|
||||
case 500:
|
||||
throw new Error('AI服务暂时不可用,请稍后重试')
|
||||
default:
|
||||
throw new Error(`AI助手请求失败:${message}`)
|
||||
}
|
||||
} else if (error.request) {
|
||||
throw new Error('网络连接失败,请检查网络连接')
|
||||
} else {
|
||||
throw new Error('AI助手请求失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除词语聊天记录
|
||||
* @param word 词语
|
||||
* @returns 清除结果
|
||||
*/
|
||||
export const clearAiHistory = async (word: string): Promise<{ msg: string }> => {
|
||||
try {
|
||||
const response = await apiClient.post<{ msg: string }>('/ai_assist/clear', null, {
|
||||
params: { word }
|
||||
})
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
console.error('Clear AI history error:', error)
|
||||
|
||||
if (error.response) {
|
||||
const message = error.response.data?.message || '清除聊天记录失败'
|
||||
throw new Error(message)
|
||||
} else {
|
||||
throw new Error('清除聊天记录失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用AI对话(预留接口)
|
||||
* @returns 空响应
|
||||
*/
|
||||
export const universalAiChat = async (): Promise<void> => {
|
||||
try {
|
||||
await apiClient.post('/ai_assist/univer')
|
||||
} catch (error: any) {
|
||||
console.error('Universal AI chat error:', error)
|
||||
throw new Error('通用AI对话功能暂未开放')
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,11 @@ export interface LoginRequest {
|
|||
export interface RegisterRequest {
|
||||
username: string
|
||||
password: string
|
||||
email: string
|
||||
phone?: string
|
||||
lang_pref: 'jp' | 'fr' | 'private'
|
||||
portrait?: string
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
|
|
@ -27,6 +30,34 @@ export interface RegisterResponse {
|
|||
message: string
|
||||
}
|
||||
|
||||
export interface EmailVerifyRequest {
|
||||
email: string
|
||||
}
|
||||
|
||||
export interface EmailVerifyResponse {
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface VerifyCodeRequest {
|
||||
email: string
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface VerifyCodeResponse {
|
||||
reset_token: string
|
||||
}
|
||||
|
||||
export interface ResetPasswordRequest {
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
current_password: string
|
||||
new_username?: string
|
||||
new_password?: string
|
||||
new_language?: 'jp' | 'fr' | 'private'
|
||||
}
|
||||
|
||||
// 用户登录
|
||||
export const login = async (credentials: LoginRequest): Promise<LoginResponse> => {
|
||||
const response = await apiClient.post('/users/login', credentials)
|
||||
|
|
@ -44,6 +75,40 @@ export const register = async (userData: RegisterRequest): Promise<RegisterRespo
|
|||
return response.data
|
||||
}
|
||||
|
||||
// 邮箱验证 - 注册时
|
||||
export const sendEmailVerification = async (data: EmailVerifyRequest): Promise<EmailVerifyResponse> => {
|
||||
const response = await apiClient.post('/users/register/email_verify', data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 邮箱找回密码(发送验证码)
|
||||
export const sendPasswordResetEmail = async (data: EmailVerifyRequest): Promise<EmailVerifyResponse> => {
|
||||
const response = await apiClient.post('/users/auth/forget-password/email', data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 邮箱验证码验证
|
||||
export const verifyEmailCode = async (data: VerifyCodeRequest): Promise<VerifyCodeResponse> => {
|
||||
const response = await apiClient.post('/users/auth/varify_code/email', data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
export const resetPassword = async (data: ResetPasswordRequest, resetToken: string): Promise<{ message: string }> => {
|
||||
const response = await apiClient.post('/users/auth/reset-password', data, {
|
||||
headers: {
|
||||
'x-reset-token': resetToken
|
||||
}
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
export const updateUser = async (data: UpdateUserRequest): Promise<{ message: string }> => {
|
||||
const response = await apiClient.put('/users/update', data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 用户登出
|
||||
export const logout = async (): Promise<void> => {
|
||||
await apiClient.post('/users/logout')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
import apiClient from './client'
|
||||
|
||||
/**
|
||||
* 词条评论请求参数
|
||||
*/
|
||||
export interface WordCommentRequest {
|
||||
comment_word: string
|
||||
comment_content: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加词条评论
|
||||
* @param lang 语言类型 (fr 或 jp)
|
||||
* @param data 评论内容
|
||||
* @returns 创建成功
|
||||
*/
|
||||
export const addWordComment = async (lang: 'fr' | 'jp', data: WordCommentRequest): Promise<void> => {
|
||||
try {
|
||||
await apiClient.post(`/comment/word/${lang}`, data)
|
||||
} catch (error: any) {
|
||||
console.error('Add word comment error:', error)
|
||||
|
||||
if (error.response) {
|
||||
const status = error.response.status
|
||||
const message = error.response.data?.message || '添加评论失败'
|
||||
|
||||
switch (status) {
|
||||
case 401:
|
||||
throw new Error('请先登录后再添加评论')
|
||||
case 422:
|
||||
throw new Error('评论内容格式不正确')
|
||||
default:
|
||||
throw new Error(`添加评论失败:${message}`)
|
||||
}
|
||||
} else if (error.request) {
|
||||
throw new Error('网络连接失败,请检查网络连接')
|
||||
} else {
|
||||
throw new Error('添加评论失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,13 +12,17 @@ export interface WordDefinition {
|
|||
}
|
||||
|
||||
export interface SearchParams {
|
||||
lang_pref: string
|
||||
query_word: string
|
||||
language: string
|
||||
query: string
|
||||
sort?: string
|
||||
order?: string
|
||||
}
|
||||
|
||||
export interface SearchSuggestParams {
|
||||
query: string
|
||||
language: string
|
||||
sort?: string
|
||||
order?: string
|
||||
}
|
||||
|
||||
export interface SearchSuggestResponse {
|
||||
|
|
@ -28,21 +32,97 @@ export interface SearchSuggestResponse {
|
|||
// 搜索单词
|
||||
export const searchWord = async (params: SearchParams): Promise<WordDefinition> => {
|
||||
const packet = {
|
||||
language: params.lang_pref,
|
||||
query: params.query_word,
|
||||
sort: "relevance",
|
||||
order: "asc"
|
||||
language: params.language,
|
||||
query: params.query,
|
||||
sort: params.sort || "relevance",
|
||||
order: params.order || "des"
|
||||
}
|
||||
const response = await apiClient.post('/search', packet)
|
||||
const response = await apiClient.post('/search/word', packet)
|
||||
console.log(response)
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 搜索推荐
|
||||
export const searchSuggest = async (params: SearchSuggestParams): Promise<SearchSuggestResponse> => {
|
||||
const response = await apiClient.post('/search/list', {
|
||||
const response = await apiClient.post('/search/list/word', {
|
||||
query: params.query,
|
||||
language: params.language
|
||||
language: params.language,
|
||||
sort: params.sort || "relevance",
|
||||
order: params.order || "des"
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 谚语相关接口
|
||||
*/
|
||||
|
||||
export interface ProverbDetail {
|
||||
text: string
|
||||
chi_exp: string
|
||||
freq: number
|
||||
}
|
||||
|
||||
export interface ProverbListItem {
|
||||
id: number
|
||||
proverb: string
|
||||
chi_exp: string
|
||||
}
|
||||
|
||||
// 获取法语谚语详情
|
||||
export const getProverbDetail = async (proverbId: number): Promise<{ result: ProverbDetail }> => {
|
||||
const formData = new FormData()
|
||||
formData.append('proverb_id', proverbId.toString())
|
||||
|
||||
const response = await apiClient.post('/search/proverb', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 获取谚语联想建议
|
||||
export const searchProverbSuggest = async (query: string, dictLanguage: string): Promise<{ list: ProverbListItem[] }> => {
|
||||
const response = await apiClient.post('/search/list/proverb', {
|
||||
query,
|
||||
dict_language: dictLanguage
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 日语惯用语相关接口
|
||||
*/
|
||||
|
||||
export interface IdiomDetail {
|
||||
id: number
|
||||
text: string
|
||||
search_text: string
|
||||
chi_exp: string
|
||||
example: string
|
||||
freq: number
|
||||
}
|
||||
|
||||
export interface IdiomListItem {
|
||||
id: number
|
||||
proverb: string
|
||||
chi_exp: string
|
||||
}
|
||||
|
||||
// 获取日语惯用语详情
|
||||
export const getIdiomDetail = async (queryId: number): Promise<{ result: IdiomDetail }> => {
|
||||
const response = await apiClient.post('/search/idiom', null, {
|
||||
params: { query_id: queryId }
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 获取日语惯用语联想建议
|
||||
export const searchIdiomSuggest = async (query: string, dictLanguage: string = 'jp'): Promise<{ list: IdiomListItem[] }> => {
|
||||
const response = await apiClient.post('/search/list/idiom', {
|
||||
query,
|
||||
dict_language: dictLanguage
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
import apiClient from './client'
|
||||
|
||||
/**
|
||||
* 用户反馈类型
|
||||
*/
|
||||
export type FeedbackType =
|
||||
| 'ui_design'
|
||||
| 'dict_fr'
|
||||
| 'dict_jp'
|
||||
| 'user'
|
||||
| 'translate'
|
||||
| 'writting'
|
||||
| 'ai_assist'
|
||||
| 'pronounce'
|
||||
|
||||
/**
|
||||
* 用户反馈请求参数
|
||||
*/
|
||||
export interface FeedbackRequest {
|
||||
report_part: FeedbackType
|
||||
text: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户反馈响应
|
||||
*/
|
||||
export interface FeedbackResponse {
|
||||
massages: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交用户反馈
|
||||
* @param data 反馈内容
|
||||
* @returns 提交结果
|
||||
*/
|
||||
export const submitFeedback = async (data: FeedbackRequest): Promise<FeedbackResponse> => {
|
||||
try {
|
||||
const response = await apiClient.post<FeedbackResponse>('/improvements', data)
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
console.error('Submit feedback error:', error)
|
||||
|
||||
if (error.response) {
|
||||
const status = error.response.status
|
||||
const message = error.response.data?.message || '提交反馈失败'
|
||||
|
||||
switch (status) {
|
||||
case 401:
|
||||
throw new Error('请先登录后再提交反馈')
|
||||
case 422:
|
||||
throw new Error('反馈内容格式不正确,请检查后重试')
|
||||
default:
|
||||
throw new Error(`提交反馈失败:${message}`)
|
||||
}
|
||||
} else if (error.request) {
|
||||
throw new Error('网络连接失败,请检查网络连接')
|
||||
} else {
|
||||
throw new Error('提交反馈失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
import apiClient from './client'
|
||||
|
||||
/**
|
||||
* 作文批改请求参数
|
||||
*/
|
||||
export interface ArticleReviewRequest {
|
||||
title_content: string
|
||||
article_type: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 作文批改响应
|
||||
*/
|
||||
export interface ArticleReviewResponse {
|
||||
reply: string
|
||||
tokens: number
|
||||
conversation_length: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 作文追问请求参数
|
||||
*/
|
||||
export interface ArticleQuestionRequest {
|
||||
query: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 作文追问响应
|
||||
*/
|
||||
export interface ArticleQuestionResponse {
|
||||
reply: string
|
||||
tokens: number
|
||||
conversation_length: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置会话响应
|
||||
*/
|
||||
export interface ResetSessionResponse {
|
||||
message: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 作文批改会话
|
||||
* @param data 作文内容和类型
|
||||
* @param lang 作文语种,默认 fr-FR
|
||||
* @returns AI批改结果
|
||||
*/
|
||||
export const reviewArticle = async (
|
||||
data: ArticleReviewRequest,
|
||||
lang: string = 'fr-FR'
|
||||
): Promise<ArticleReviewResponse> => {
|
||||
try {
|
||||
const response = await apiClient.post<ArticleReviewResponse>(
|
||||
'/article-director/article',
|
||||
data,
|
||||
{
|
||||
params: { lang }
|
||||
}
|
||||
)
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
console.error('Article review API error:', error)
|
||||
|
||||
if (error.response) {
|
||||
const status = error.response.status
|
||||
const message = error.response.data?.message || '作文批改失败'
|
||||
|
||||
switch (status) {
|
||||
case 401:
|
||||
throw new Error('请先登录后再使用作文批改功能')
|
||||
case 500:
|
||||
throw new Error('作文批改服务暂时不可用,请稍后重试')
|
||||
default:
|
||||
throw new Error(`作文批改失败:${message}`)
|
||||
}
|
||||
} else if (error.request) {
|
||||
throw new Error('网络连接失败,请检查网络连接')
|
||||
} else {
|
||||
throw new Error('作文批改请求失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 作文追问
|
||||
* @param data 追问内容
|
||||
* @returns AI回答
|
||||
*/
|
||||
export const askArticleQuestion = async (
|
||||
data: ArticleQuestionRequest
|
||||
): Promise<ArticleQuestionResponse> => {
|
||||
try {
|
||||
const response = await apiClient.post<ArticleQuestionResponse>(
|
||||
'/article-director/question',
|
||||
data
|
||||
)
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
console.error('Article question API error:', error)
|
||||
|
||||
if (error.response) {
|
||||
const status = error.response.status
|
||||
const message = error.response.data?.message || '追问失败'
|
||||
|
||||
switch (status) {
|
||||
case 401:
|
||||
throw new Error('请先登录后再使用追问功能')
|
||||
case 500:
|
||||
throw new Error('追问服务暂时不可用,请稍后重试')
|
||||
default:
|
||||
throw new Error(`追问失败:${message}`)
|
||||
}
|
||||
} else if (error.request) {
|
||||
throw new Error('网络连接失败,请检查网络连接')
|
||||
} else {
|
||||
throw new Error('追问请求失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置作文会话
|
||||
* @returns 重置结果
|
||||
*/
|
||||
export const resetArticleSession = async (): Promise<ResetSessionResponse> => {
|
||||
try {
|
||||
const response = await apiClient.post<ResetSessionResponse>(
|
||||
'/article-director/reset'
|
||||
)
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
console.error('Reset article session error:', error)
|
||||
|
||||
if (error.response) {
|
||||
const message = error.response.data?.message || '重置会话失败'
|
||||
throw new Error(message)
|
||||
} else {
|
||||
throw new Error('重置会话失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -90,8 +90,8 @@ const handleSearch = async (query: string, lang: string) => {
|
|||
|
||||
try {
|
||||
const result = await searchWord({
|
||||
lang_pref: lang,
|
||||
query_word: query.trim()
|
||||
language: lang,
|
||||
query: query.trim()
|
||||
})
|
||||
searchResult.value = result
|
||||
} catch (err: any) {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
class="px-8 border border-blue-700 focus:border-blue-500 rounded-full outline-none w-full h-[63px] text-2xl"
|
||||
/>
|
||||
|
||||
<!-- 注册时显示确认密码 -->
|
||||
<!-- 注册时显示额外字段 -->
|
||||
<template v-if="!isLogin">
|
||||
<label class="block mt-8 mb-6 text-gray-500 text-xl">确认密码</label>
|
||||
<input
|
||||
|
|
@ -74,6 +74,42 @@
|
|||
class="px-8 border border-blue-700 focus:border-blue-500 rounded-full outline-none w-full h-[63px] text-2xl"
|
||||
/>
|
||||
|
||||
<label class="block mt-8 mb-6 text-gray-500 text-xl">邮箱</label>
|
||||
<input
|
||||
v-model="registerForm.email"
|
||||
type="email"
|
||||
required
|
||||
placeholder="请输入邮箱地址"
|
||||
class="px-8 border border-blue-700 focus:border-blue-500 rounded-full outline-none w-full h-[63px] text-2xl"
|
||||
/>
|
||||
|
||||
<label class="block mt-8 mb-6 text-gray-500 text-xl">邮箱验证码</label>
|
||||
<div class="flex gap-4">
|
||||
<input
|
||||
v-model="registerForm.code"
|
||||
type="text"
|
||||
required
|
||||
placeholder="请输入邮箱验证码"
|
||||
class="flex-1 px-8 border border-blue-700 focus:border-blue-500 rounded-full outline-none h-[63px] text-2xl"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@click="sendVerificationCode"
|
||||
:disabled="sendingCode || countdown > 0 || !registerForm.email"
|
||||
class="bg-blue-700 hover:bg-blue-600 disabled:opacity-50 px-6 rounded-full text-white text-lg whitespace-nowrap disabled:cursor-not-allowed"
|
||||
>
|
||||
{{ countdown > 0 ? `${countdown}秒后重试` : (codeSent ? '重新发送' : '发送验证码') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<label class="block mt-8 mb-6 text-gray-500 text-xl">手机号(可选)</label>
|
||||
<input
|
||||
v-model="registerForm.phone"
|
||||
type="tel"
|
||||
placeholder="请输入手机号(可选)"
|
||||
class="px-8 border border-blue-700 focus:border-blue-500 rounded-full outline-none w-full h-[63px] text-2xl"
|
||||
/>
|
||||
|
||||
<label class="block mt-8 mb-6 text-gray-500 text-xl">语言偏好</label>
|
||||
<select
|
||||
v-model="registerForm.lang_pref"
|
||||
|
|
@ -104,10 +140,11 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuth } from '../composables/useAuth'
|
||||
import { useModal } from '../composables/useModal'
|
||||
import { sendEmailVerification } from '../api/auth'
|
||||
|
||||
const router = useRouter()
|
||||
const { login, register } = useAuth()
|
||||
|
|
@ -123,11 +160,17 @@ const loginForm = ref({
|
|||
const registerForm = ref({
|
||||
username: '',
|
||||
password: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
lang_pref: 'fr' as 'jp' | 'fr' | 'private',
|
||||
portrait: ''
|
||||
portrait: '',
|
||||
code: ''
|
||||
})
|
||||
|
||||
const confirmPassword = ref('')
|
||||
const sendingCode = ref(false)
|
||||
const codeSent = ref(false)
|
||||
const countdown = ref(0)
|
||||
const loading = ref(false)
|
||||
const error = ref('')
|
||||
const successMessage = ref('')
|
||||
|
|
@ -225,10 +268,15 @@ const handleRegister = async () => {
|
|||
registerForm.value = {
|
||||
username: '',
|
||||
password: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
lang_pref: 'fr',
|
||||
portrait: ''
|
||||
portrait: '',
|
||||
code: ''
|
||||
}
|
||||
confirmPassword.value = ''
|
||||
codeSent.value = false
|
||||
countdown.value = 0
|
||||
}, 2000)
|
||||
} else {
|
||||
// 注册失败
|
||||
|
|
@ -245,6 +293,46 @@ const handleRegister = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
const sendVerificationCode = async () => {
|
||||
if (!registerForm.value.email) {
|
||||
error.value = '请先输入邮箱地址'
|
||||
return
|
||||
}
|
||||
|
||||
// 简单的邮箱格式验证
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
if (!emailRegex.test(registerForm.value.email)) {
|
||||
error.value = '请输入有效的邮箱地址'
|
||||
return
|
||||
}
|
||||
|
||||
sendingCode.value = true
|
||||
error.value = ''
|
||||
|
||||
try {
|
||||
await sendEmailVerification({ email: registerForm.value.email })
|
||||
showSuccess('验证码已发送到您的邮箱,请查收')
|
||||
codeSent.value = true
|
||||
|
||||
// 开始倒计时
|
||||
countdown.value = 60
|
||||
const timer = setInterval(() => {
|
||||
countdown.value--
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
}, 1000)
|
||||
} catch (err: any) {
|
||||
console.error('Send verification code error:', err)
|
||||
const errorMessage = err.response?.data?.detail || err.response?.data?.message || '发送验证码失败'
|
||||
showError(errorMessage)
|
||||
error.value = errorMessage
|
||||
} finally {
|
||||
sendingCode.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 切换模式时清空错误和成功信息
|
||||
const clearMessages = () => {
|
||||
error.value = ''
|
||||
|
|
@ -252,7 +340,6 @@ const clearMessages = () => {
|
|||
}
|
||||
|
||||
// 监听模式切换
|
||||
import { watch } from 'vue'
|
||||
watch(isLogin, () => {
|
||||
clearMessages()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
<h3 class="mb-4 font-semibold text-gray-700">AI写作指导</h3>
|
||||
<div class="h-[300px] overflow-y-auto">
|
||||
<div v-if="loading" class="flex justify-center items-center h-full text-gray-500">
|
||||
<div class="border-b-2 border-blue-700 rounded-full w-8 h-8 animate-spin"></div>
|
||||
<div class="border-blue-700 border-b-2 rounded-full w-8 h-8 animate-spin"></div>
|
||||
<span class="ml-2">AI正在分析您的作文...</span>
|
||||
</div>
|
||||
<div v-else-if="writingFeedback" class="space-y-4">
|
||||
|
|
@ -124,9 +124,10 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
import AppHeader from '../components/AppHeader.vue'
|
||||
import AppFooter from '../components/AppFooter.vue'
|
||||
import { reviewArticle, resetArticleSession } from '../api/writing'
|
||||
|
||||
const writingTypes = [
|
||||
{ value: 'essay', label: '议论文' },
|
||||
|
|
@ -189,42 +190,38 @@ const getWritingHelp = async () => {
|
|||
error.value = ''
|
||||
|
||||
try {
|
||||
// 模拟AI分析过程
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
// 调用真实的作文批改API
|
||||
const result = await reviewArticle({
|
||||
title_content: userText.value.trim(),
|
||||
article_type: writingTypes.find(t => t.value === selectedType.value)?.label || '议论文'
|
||||
}, 'fr-FR')
|
||||
|
||||
// 模拟AI反馈
|
||||
// 将API返回的reply文本解析为结构化反馈
|
||||
// 这里简单地将整个回复作为overall显示
|
||||
// 如果API返回的是结构化的JSON,可以直接使用
|
||||
writingFeedback.value = {
|
||||
overall: `您的作文整体结构清晰,主题明确。建议在以下方面进行改进:`,
|
||||
grammar: [
|
||||
'注意动词变位的准确性',
|
||||
'检查形容词与名词的性数一致',
|
||||
'确保时态使用的连贯性'
|
||||
],
|
||||
vocabulary: [
|
||||
'可以使用更多高级词汇来丰富表达',
|
||||
'避免重复使用相同的词汇',
|
||||
'尝试使用同义词增加文章的多样性'
|
||||
],
|
||||
structure: [
|
||||
'段落之间的过渡可以更加自然',
|
||||
'可以增加更多的连接词',
|
||||
'结论部分可以更加有力'
|
||||
],
|
||||
style: [
|
||||
'保持正式的写作风格',
|
||||
'句子长度可以适当变化',
|
||||
'增加一些修辞手法会更好'
|
||||
]
|
||||
overall: result.reply,
|
||||
tokens: `使用Token数: ${result.tokens}`,
|
||||
conversation_length: `对话长度: ${result.conversation_length}`
|
||||
}
|
||||
|
||||
} catch (err: any) {
|
||||
console.error('Writing help error:', err)
|
||||
error.value = '获取写作指导失败,请稍后重试'
|
||||
error.value = err.message || '获取写作指导失败,请稍后重试'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 组件卸载时重置会话
|
||||
onUnmounted(async () => {
|
||||
try {
|
||||
await resetArticleSession()
|
||||
} catch (err) {
|
||||
console.error('Failed to reset article session:', err)
|
||||
}
|
||||
})
|
||||
|
||||
const useTemplate = (template: string) => {
|
||||
if (userText.value && !confirm('使用模板将替换当前内容,确定继续吗?')) {
|
||||
return
|
||||
|
|
|
|||
Loading…
Reference in New Issue