feat: so on
This commit is contained in:
parent
622860aa0c
commit
92db732e7e
|
|
@ -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 {
|
export interface RegisterRequest {
|
||||||
username: string
|
username: string
|
||||||
password: string
|
password: string
|
||||||
|
email: string
|
||||||
|
phone?: string
|
||||||
lang_pref: 'jp' | 'fr' | 'private'
|
lang_pref: 'jp' | 'fr' | 'private'
|
||||||
portrait?: string
|
portrait?: string
|
||||||
|
code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoginResponse {
|
export interface LoginResponse {
|
||||||
|
|
@ -27,6 +30,34 @@ export interface RegisterResponse {
|
||||||
message: string
|
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> => {
|
export const login = async (credentials: LoginRequest): Promise<LoginResponse> => {
|
||||||
const response = await apiClient.post('/users/login', credentials)
|
const response = await apiClient.post('/users/login', credentials)
|
||||||
|
|
@ -44,6 +75,40 @@ export const register = async (userData: RegisterRequest): Promise<RegisterRespo
|
||||||
return response.data
|
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> => {
|
export const logout = async (): Promise<void> => {
|
||||||
await apiClient.post('/users/logout')
|
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 {
|
export interface SearchParams {
|
||||||
lang_pref: string
|
language: string
|
||||||
query_word: string
|
query: string
|
||||||
|
sort?: string
|
||||||
|
order?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchSuggestParams {
|
export interface SearchSuggestParams {
|
||||||
query: string
|
query: string
|
||||||
language: string
|
language: string
|
||||||
|
sort?: string
|
||||||
|
order?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchSuggestResponse {
|
export interface SearchSuggestResponse {
|
||||||
|
|
@ -28,21 +32,97 @@ export interface SearchSuggestResponse {
|
||||||
// 搜索单词
|
// 搜索单词
|
||||||
export const searchWord = async (params: SearchParams): Promise<WordDefinition> => {
|
export const searchWord = async (params: SearchParams): Promise<WordDefinition> => {
|
||||||
const packet = {
|
const packet = {
|
||||||
language: params.lang_pref,
|
language: params.language,
|
||||||
query: params.query_word,
|
query: params.query,
|
||||||
sort: "relevance",
|
sort: params.sort || "relevance",
|
||||||
order: "asc"
|
order: params.order || "des"
|
||||||
}
|
}
|
||||||
const response = await apiClient.post('/search', packet)
|
const response = await apiClient.post('/search/word', packet)
|
||||||
console.log(response)
|
console.log(response)
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索推荐
|
// 搜索推荐
|
||||||
export const searchSuggest = async (params: SearchSuggestParams): Promise<SearchSuggestResponse> => {
|
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,
|
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
|
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 {
|
try {
|
||||||
const result = await searchWord({
|
const result = await searchWord({
|
||||||
lang_pref: lang,
|
language: lang,
|
||||||
query_word: query.trim()
|
query: query.trim()
|
||||||
})
|
})
|
||||||
searchResult.value = result
|
searchResult.value = result
|
||||||
} catch (err: any) {
|
} 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"
|
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">
|
<template v-if="!isLogin">
|
||||||
<label class="block mt-8 mb-6 text-gray-500 text-xl">确认密码</label>
|
<label class="block mt-8 mb-6 text-gray-500 text-xl">确认密码</label>
|
||||||
<input
|
<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"
|
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>
|
<label class="block mt-8 mb-6 text-gray-500 text-xl">语言偏好</label>
|
||||||
<select
|
<select
|
||||||
v-model="registerForm.lang_pref"
|
v-model="registerForm.lang_pref"
|
||||||
|
|
@ -104,10 +140,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useAuth } from '../composables/useAuth'
|
import { useAuth } from '../composables/useAuth'
|
||||||
import { useModal } from '../composables/useModal'
|
import { useModal } from '../composables/useModal'
|
||||||
|
import { sendEmailVerification } from '../api/auth'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { login, register } = useAuth()
|
const { login, register } = useAuth()
|
||||||
|
|
@ -123,11 +160,17 @@ const loginForm = ref({
|
||||||
const registerForm = ref({
|
const registerForm = ref({
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
lang_pref: 'fr' as 'jp' | 'fr' | 'private',
|
lang_pref: 'fr' as 'jp' | 'fr' | 'private',
|
||||||
portrait: ''
|
portrait: '',
|
||||||
|
code: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const confirmPassword = ref('')
|
const confirmPassword = ref('')
|
||||||
|
const sendingCode = ref(false)
|
||||||
|
const codeSent = ref(false)
|
||||||
|
const countdown = ref(0)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
const successMessage = ref('')
|
const successMessage = ref('')
|
||||||
|
|
@ -225,10 +268,15 @@ const handleRegister = async () => {
|
||||||
registerForm.value = {
|
registerForm.value = {
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
lang_pref: 'fr',
|
lang_pref: 'fr',
|
||||||
portrait: ''
|
portrait: '',
|
||||||
|
code: ''
|
||||||
}
|
}
|
||||||
confirmPassword.value = ''
|
confirmPassword.value = ''
|
||||||
|
codeSent.value = false
|
||||||
|
countdown.value = 0
|
||||||
}, 2000)
|
}, 2000)
|
||||||
} else {
|
} 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 = () => {
|
const clearMessages = () => {
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
|
@ -252,7 +340,6 @@ const clearMessages = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听模式切换
|
// 监听模式切换
|
||||||
import { watch } from 'vue'
|
|
||||||
watch(isLogin, () => {
|
watch(isLogin, () => {
|
||||||
clearMessages()
|
clearMessages()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@
|
||||||
<h3 class="mb-4 font-semibold text-gray-700">AI写作指导</h3>
|
<h3 class="mb-4 font-semibold text-gray-700">AI写作指导</h3>
|
||||||
<div class="h-[300px] overflow-y-auto">
|
<div class="h-[300px] overflow-y-auto">
|
||||||
<div v-if="loading" class="flex justify-center items-center h-full text-gray-500">
|
<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>
|
<span class="ml-2">AI正在分析您的作文...</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="writingFeedback" class="space-y-4">
|
<div v-else-if="writingFeedback" class="space-y-4">
|
||||||
|
|
@ -124,9 +124,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, onUnmounted } from 'vue'
|
||||||
import AppHeader from '../components/AppHeader.vue'
|
import AppHeader from '../components/AppHeader.vue'
|
||||||
import AppFooter from '../components/AppFooter.vue'
|
import AppFooter from '../components/AppFooter.vue'
|
||||||
|
import { reviewArticle, resetArticleSession } from '../api/writing'
|
||||||
|
|
||||||
const writingTypes = [
|
const writingTypes = [
|
||||||
{ value: 'essay', label: '议论文' },
|
{ value: 'essay', label: '议论文' },
|
||||||
|
|
@ -189,42 +190,38 @@ const getWritingHelp = async () => {
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 模拟AI分析过程
|
// 调用真实的作文批改API
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
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 = {
|
writingFeedback.value = {
|
||||||
overall: `您的作文整体结构清晰,主题明确。建议在以下方面进行改进:`,
|
overall: result.reply,
|
||||||
grammar: [
|
tokens: `使用Token数: ${result.tokens}`,
|
||||||
'注意动词变位的准确性',
|
conversation_length: `对话长度: ${result.conversation_length}`
|
||||||
'检查形容词与名词的性数一致',
|
|
||||||
'确保时态使用的连贯性'
|
|
||||||
],
|
|
||||||
vocabulary: [
|
|
||||||
'可以使用更多高级词汇来丰富表达',
|
|
||||||
'避免重复使用相同的词汇',
|
|
||||||
'尝试使用同义词增加文章的多样性'
|
|
||||||
],
|
|
||||||
structure: [
|
|
||||||
'段落之间的过渡可以更加自然',
|
|
||||||
'可以增加更多的连接词',
|
|
||||||
'结论部分可以更加有力'
|
|
||||||
],
|
|
||||||
style: [
|
|
||||||
'保持正式的写作风格',
|
|
||||||
'句子长度可以适当变化',
|
|
||||||
'增加一些修辞手法会更好'
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('Writing help error:', err)
|
console.error('Writing help error:', err)
|
||||||
error.value = '获取写作指导失败,请稍后重试'
|
error.value = err.message || '获取写作指导失败,请稍后重试'
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 组件卸载时重置会话
|
||||||
|
onUnmounted(async () => {
|
||||||
|
try {
|
||||||
|
await resetArticleSession()
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to reset article session:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const useTemplate = (template: string) => {
|
const useTemplate = (template: string) => {
|
||||||
if (userText.value && !confirm('使用模板将替换当前内容,确定继续吗?')) {
|
if (userText.value && !confirm('使用模板将替换当前内容,确定继续吗?')) {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue