2026-05-01 20:39:09 +08:00
|
|
|
type ClientErrorTone = 'error' | 'warning'
|
|
|
|
|
type ClientActionKind = 'bind-account' | 'open-console' | 'probe-account' | 'unbind-account'
|
2026-04-19 14:18:20 +08:00
|
|
|
|
|
|
|
|
interface ClientErrorPresentation {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: ClientErrorTone
|
|
|
|
|
title: string
|
|
|
|
|
content: string
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
const REMOTE_METHOD_PREFIX = /^Error invoking remote method '[^']+':\s*/i
|
|
|
|
|
const ERROR_PREFIX = /^Error:\s*/i
|
2026-04-19 14:18:20 +08:00
|
|
|
|
|
|
|
|
function rawErrorMessage(error: unknown): string {
|
|
|
|
|
if (error instanceof Error) {
|
2026-05-01 20:39:09 +08:00
|
|
|
return error.message
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
if (typeof error === 'string') {
|
|
|
|
|
return error
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
return ''
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function unwrapClientErrorMessage(error: unknown, fallback: string): string {
|
2026-05-01 20:39:09 +08:00
|
|
|
let message = rawErrorMessage(error).trim()
|
|
|
|
|
let previous = ''
|
2026-04-19 14:18:20 +08:00
|
|
|
|
|
|
|
|
while (message && message !== previous) {
|
2026-05-01 20:39:09 +08:00
|
|
|
previous = message
|
|
|
|
|
message = message.replace(REMOTE_METHOD_PREFIX, '').replace(ERROR_PREFIX, '').trim()
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
return message || fallback
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function presentClientError(kind: ClientActionKind, error: unknown): ClientErrorPresentation {
|
|
|
|
|
const message = unwrapClientErrorMessage(
|
|
|
|
|
error,
|
2026-05-01 20:39:09 +08:00
|
|
|
kind === 'bind-account'
|
|
|
|
|
? '账号绑定失败'
|
|
|
|
|
: kind === 'unbind-account'
|
|
|
|
|
? '账号解绑失败'
|
|
|
|
|
: kind === 'probe-account'
|
|
|
|
|
? '账号校验失败'
|
|
|
|
|
: '打开平台失败',
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if (message === 'desktop_account_bind_window_closed') {
|
2026-04-19 14:18:20 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'warning',
|
|
|
|
|
title: '授权已中断',
|
|
|
|
|
content: '授权窗口已关闭,本次绑定没有完成。重新点击“绑定账号”即可继续。',
|
|
|
|
|
}
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (message === 'desktop_account_bind_limit_reached') {
|
2026-04-20 11:11:21 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'warning',
|
|
|
|
|
title: '授权窗口已达上限',
|
|
|
|
|
content: '当前最多同时打开 2 个授权窗口。请先完成或关闭已有授权窗口,再继续新的绑定。',
|
|
|
|
|
}
|
2026-04-20 11:11:21 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (message === 'desktop_account_upsert_failed') {
|
2026-04-19 14:18:20 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'error',
|
|
|
|
|
title: '保存授权账号失败',
|
|
|
|
|
content: '平台侧授权已经完成,但客户端回写账号信息失败。请稍后重试。',
|
|
|
|
|
}
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (message === 'desktop_account_bind_failed') {
|
2026-04-19 14:18:20 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'error',
|
|
|
|
|
title: '账号绑定失败',
|
|
|
|
|
content: '授权流程执行失败,请稍后重新发起绑定。',
|
|
|
|
|
}
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (message.startsWith('desktop_platform_not_supported:')) {
|
|
|
|
|
const platformId = message.split(':')[1] || '当前平台'
|
2026-04-19 14:18:20 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'error',
|
|
|
|
|
title: '平台暂不支持',
|
2026-04-19 14:18:20 +08:00
|
|
|
content: `${platformId} 暂时还不支持这个操作。`,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (message === 'desktop_account_sync_conflict') {
|
2026-04-20 09:52:48 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'warning',
|
|
|
|
|
title: kind === 'unbind-account' ? '账号状态已变化' : '账号状态已更新',
|
|
|
|
|
content: '当前账号状态刚刚发生了变化。请先刷新列表,再重试这次操作。',
|
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (message.startsWith('desktop_account_session_expired:')) {
|
|
|
|
|
const platformId = message.split(':')[1] || '当前平台'
|
|
|
|
|
const platformLabel = platformId === 'qiehao' ? '企鹅号' : platformId
|
2026-04-30 01:28:41 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'warning',
|
|
|
|
|
title: '授权已过期',
|
2026-04-30 01:28:41 +08:00
|
|
|
content: `${platformLabel} 登录态已失效,请重新授权后再打开工作台。`,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-30 01:28:41 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (message === 'desktop_account_delete_failed') {
|
2026-04-20 09:52:48 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'error',
|
|
|
|
|
title: '账号解绑失败',
|
|
|
|
|
content: '服务端解绑没有成功完成,请稍后重试。',
|
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (kind === 'open-console') {
|
2026-04-19 14:18:20 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'error',
|
|
|
|
|
title: '打开平台失败',
|
2026-04-19 14:18:20 +08:00
|
|
|
content: message,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (kind === 'probe-account') {
|
2026-04-27 21:33:55 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'error',
|
|
|
|
|
title: '账号校验失败',
|
2026-04-27 21:33:55 +08:00
|
|
|
content: message,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-27 21:33:55 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (kind === 'unbind-account') {
|
2026-04-20 09:52:48 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'error',
|
|
|
|
|
title: '账号解绑失败',
|
2026-04-20 09:52:48 +08:00
|
|
|
content: message,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-19 14:18:20 +08:00
|
|
|
return {
|
2026-05-01 20:39:09 +08:00
|
|
|
tone: 'error',
|
|
|
|
|
title: '账号绑定失败',
|
2026-04-19 14:18:20 +08:00
|
|
|
content: message,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-20 11:08:09 +08:00
|
|
|
export async function showClientActionError(kind: ClientActionKind, error: unknown): Promise<void> {
|
2026-05-01 20:39:09 +08:00
|
|
|
const presentation = presentClientError(kind, error)
|
|
|
|
|
const { Modal } = await import('ant-design-vue')
|
|
|
|
|
const showModal = presentation.tone === 'warning' ? Modal.warning : Modal.error
|
2026-04-19 14:18:20 +08:00
|
|
|
|
|
|
|
|
showModal({
|
|
|
|
|
title: presentation.title,
|
|
|
|
|
content: presentation.content,
|
2026-05-01 20:39:09 +08:00
|
|
|
okText: '知道了',
|
2026-04-19 14:18:20 +08:00
|
|
|
centered: true,
|
|
|
|
|
maskClosable: true,
|
2026-05-01 20:39:09 +08:00
|
|
|
})
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|