feat(admin-web): show centered modal in Chinese when AI points are insufficient
- Add `ai_points_insufficient` / `ai_points_unavailable` Chinese mappings in errors.ts so the toast copy is localized. - Add `showAiPointsInsufficientModal()` helper that renders a centered `Modal.confirm` with localized title/body and a "查看点数明细" action that routes to `/account/ai-points`. The helper is debounced so concurrent failed requests do not stack multiple modals. - Trigger the modal globally from the admin-web response interceptor when the backend returns `ai_points_insufficient`, so every AI-generating endpoint surfaces the same prominent prompt without per-callsite changes. - Drop the English `detail` for ai_points_insufficient in `formatError` so the secondary toast stays pure Chinese. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -315,6 +315,12 @@ apiClient.raw.interceptors.response.use(
|
|||||||
markStoredMembershipBlocked(error.message)
|
markStoredMembershipBlocked(error.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (error instanceof ApiClientError && error.message === 'ai_points_insufficient') {
|
||||||
|
void import('./errors').then(({ showAiPointsInsufficientModal }) => {
|
||||||
|
showAiPointsInsufficientModal()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
import { Modal } from 'ant-design-vue'
|
||||||
import { ApiClientError } from '@geo/http-client'
|
import { ApiClientError } from '@geo/http-client'
|
||||||
|
|
||||||
|
import { router } from '@/router'
|
||||||
|
|
||||||
const authSessionErrorMessages = new Set([
|
const authSessionErrorMessages = new Set([
|
||||||
'not_authenticated',
|
'not_authenticated',
|
||||||
'missing_bearer_token',
|
'missing_bearer_token',
|
||||||
@@ -28,6 +31,8 @@ const errorMessageMap: Record<string, string> = {
|
|||||||
tenant_scope_missing: '租户上下文缺失',
|
tenant_scope_missing: '租户上下文缺失',
|
||||||
query_failed: '数据读取失败',
|
query_failed: '数据读取失败',
|
||||||
quota_insufficient: '生成额度不足',
|
quota_insufficient: '生成额度不足',
|
||||||
|
ai_points_insufficient: 'AI 点数余额不足',
|
||||||
|
ai_points_unavailable: 'AI 点数服务暂不可用,请稍后重试',
|
||||||
trial_plan_expired: '免费试用已到期,请联系管理员开通',
|
trial_plan_expired: '免费试用已到期,请联系管理员开通',
|
||||||
subscription_required: '当前租户尚未开通有效套餐,请联系管理员',
|
subscription_required: '当前租户尚未开通有效套餐,请联系管理员',
|
||||||
subscription_inactive: '当前租户套餐已失效,请联系管理员',
|
subscription_inactive: '当前租户套餐已失效,请联系管理员',
|
||||||
@@ -89,6 +94,34 @@ const errorMessageMap: Record<string, string> = {
|
|||||||
unknown_error: '发生未知错误',
|
unknown_error: '发生未知错误',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isAiPointsInsufficient(error: unknown): boolean {
|
||||||
|
return error instanceof ApiClientError && error.message === 'ai_points_insufficient'
|
||||||
|
}
|
||||||
|
|
||||||
|
let aiPointsModalVisible = false
|
||||||
|
|
||||||
|
export function showAiPointsInsufficientModal(): void {
|
||||||
|
if (aiPointsModalVisible) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
aiPointsModalVisible = true
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: 'AI 点数不足',
|
||||||
|
content: '当前账号的 AI 点数余额已耗尽,无法继续生成内容。请升级套餐或等待下月点数重置。',
|
||||||
|
okText: '查看点数明细',
|
||||||
|
cancelText: '我知道了',
|
||||||
|
centered: true,
|
||||||
|
okButtonProps: { type: 'primary' },
|
||||||
|
onOk: () => {
|
||||||
|
void router.push('/account/ai-points')
|
||||||
|
},
|
||||||
|
afterClose: () => {
|
||||||
|
aiPointsModalVisible = false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function isHandledAuthError(error: unknown): boolean {
|
export function isHandledAuthError(error: unknown): boolean {
|
||||||
return (
|
return (
|
||||||
error instanceof ApiClientError &&
|
error instanceof ApiClientError &&
|
||||||
@@ -104,6 +137,9 @@ export function formatError(error: unknown): string {
|
|||||||
|
|
||||||
if (error instanceof ApiClientError) {
|
if (error instanceof ApiClientError) {
|
||||||
const message = errorMessageMap[error.message] ?? error.message.replaceAll('_', ' ')
|
const message = errorMessageMap[error.message] ?? error.message.replaceAll('_', ' ')
|
||||||
|
if (isAiPointsInsufficient(error)) {
|
||||||
|
return message
|
||||||
|
}
|
||||||
return error.detail ? `${message}: ${error.detail}` : message
|
return error.detail ? `${message}: ${error.detail}` : message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user