mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-05-27 19:19:56 +08:00
* feat(i18n): enhance locale handling and routing - Implemented dynamic locale aliases in router configuration. - Added support for preferred locale storage in global state. - Improved locale resolution logic in router beforeEach guard. - Created utility functions for locale management and path manipulation. - Added tests for locale matching and message extraction. - Updated Header component to allow language selection. - Refactored getRouterPathWithLang to utilize new locale utilities. - Updated Vite configuration to support aliasing for vue-i18n. - Bumped version numbers across various packages to 1.9.0. * feat(i18n): update version to 1.8.0 and enhance locale handling - Updated version numbers across all package.json files to 1.8.0. - Enhanced locale handling in App.vue by centralizing locale configurations. - Improved Turnstile component to support dynamic language rendering. - Refactored i18n utilities to include initial locale setup and empty locale messages. - Updated i18n.ts to utilize the new locale management structure. - Added naive-locale.ts for better integration with Naive UI's locale handling. - Adjusted Header.vue to streamline language selection and locale changes. - Fixed translations in multiple locale files for consistency and accuracy. * fix(i18n): address review feedback * feat(i18n): update default locale to English and enhance language handling in components * fix(i18n): switch locale selector to dropdown * docs: add topbar language and github order design spec * fix(i18n): 修复 Header 语言切换器相关问题,恢复为独立控件并调整样式 * Refactor locale handling in router and add locale-guard utility functions - Improved locale resolution logic in router by introducing utility functions for better readability and maintainability. - Added `locale-guard.js` to encapsulate locale-related functions such as getting route locale, resolving locale for navigation, and applying locale navigation state. - Updated JWT synchronization logic to streamline the handling of JWT from query parameters. - Modified i18n messages test to check for coverage of registered locale message keys instead of extracting English source messages. * 删除顶部栏语言和GitHub顺序设计文档 * fix: 修复前端设置初始化时未返回 domains 数组导致的 undefined 错误 * refactor(i18n): consolidate locale infrastructure * fix(i18n): stabilize locale route switching * fix(i18n): persist default locale selection * fix(i18n): 修复前端设置初始化时未返回 domains 数组导致的 undefined 错误,统一按空数组兜底处理 feat(i18n): 添加 locale 别名处理,支持默认语言的重定向 test(i18n): 增加对默认语言别名重定向的测试用例 * refactor: replace useAppI18n with useScopedI18n in multiple components for improved localization management * fix(tests): 移除不必要的 URL 断言以简化 Passkey 测试 * fix(i18n): 更新语言切换逻辑,确保使用当前语言设置进行路由导航 * fix(i18n): 强制路由切换以确保语言切换后正确导航 * refactor(i18n): 优化消息注册和路由本地化逻辑,移除冗余代码 * refactor(i18n): 拆分 API 文件以优化路由管理,更新语言处理逻辑 * fix: align i18n release notes and frontend test script
154 lines
4.0 KiB
Vue
154 lines
4.0 KiB
Vue
<script setup>
|
|
import { computed } from 'vue';
|
|
import { useScopedI18n } from '@/i18n/app';
|
|
import { ContentCopyOutlined, LinkRound, CodeRound } from '@vicons/material';
|
|
import { useMessage } from 'naive-ui';
|
|
import { useGlobalState } from '../store';
|
|
|
|
const message = useMessage();
|
|
const { isDark } = useGlobalState();
|
|
|
|
// Dark mode: use Gmail's softer blue (#A8C7FA) for better readability
|
|
const alertThemeOverrides = computed(() => {
|
|
if (isDark.value) {
|
|
return {
|
|
colorSuccess: 'rgba(168, 199, 250, 0.15)',
|
|
borderSuccess: '1px solid rgba(168, 199, 250, 0.3)',
|
|
iconColorSuccess: '#A8C7FA',
|
|
titleTextColorSuccess: '#A8C7FA',
|
|
}
|
|
}
|
|
return {}
|
|
});
|
|
|
|
const tagThemeOverrides = computed(() => {
|
|
if (isDark.value) {
|
|
return {
|
|
colorSuccess: 'rgba(168, 199, 250, 0.15)',
|
|
borderSuccess: '1px solid rgba(168, 199, 250, 0.3)',
|
|
textColorSuccess: '#A8C7FA',
|
|
}
|
|
}
|
|
return {}
|
|
});
|
|
|
|
const { t } = useScopedI18n('components.AiExtractInfo')
|
|
|
|
const props = defineProps({
|
|
metadata: {
|
|
type: String,
|
|
default: null
|
|
},
|
|
compact: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
});
|
|
|
|
const aiExtract = computed(() => {
|
|
if (!props.metadata) return null;
|
|
try {
|
|
const data = JSON.parse(props.metadata);
|
|
return data.ai_extract || null;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
});
|
|
|
|
const typeLabel = computed(() => {
|
|
if (!aiExtract.value) return '';
|
|
const typeMap = {
|
|
auth_code: t('authCode'),
|
|
auth_link: t('authLink'),
|
|
service_link: t('serviceLink'),
|
|
subscription_link: t('subscriptionLink'),
|
|
other_link: t('otherLink'),
|
|
};
|
|
return typeMap[aiExtract.value.type] || '';
|
|
});
|
|
|
|
const typeIcon = computed(() => {
|
|
if (!aiExtract.value) return null;
|
|
const iconMap = {
|
|
auth_code: CodeRound,
|
|
auth_link: LinkRound,
|
|
service_link: LinkRound,
|
|
subscription_link: LinkRound,
|
|
other_link: LinkRound,
|
|
};
|
|
return iconMap[aiExtract.value.type] || null;
|
|
});
|
|
|
|
const isLink = computed(() => {
|
|
return aiExtract.value && aiExtract.value.type !== 'auth_code';
|
|
});
|
|
|
|
const displayText = computed(() => {
|
|
if (!aiExtract.value) return '';
|
|
// For auth_code, always show the raw result (verification code)
|
|
if (aiExtract.value.type === 'auth_code') {
|
|
return aiExtract.value.result;
|
|
}
|
|
// For links, prefer result_text as display label
|
|
return aiExtract.value.result_text || aiExtract.value.result;
|
|
});
|
|
|
|
const copyToClipboard = async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(aiExtract.value.result);
|
|
message.success(t('copySuccess'));
|
|
} catch (e) {
|
|
message.error(t('copyFailed'));
|
|
}
|
|
};
|
|
|
|
const openLink = () => {
|
|
if (isLink.value && aiExtract.value.result) {
|
|
window.open(aiExtract.value.result, '_blank');
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="aiExtract && aiExtract.result" class="ai-extract-info">
|
|
<n-alert v-if="!compact" type="success" closable :theme-overrides="alertThemeOverrides">
|
|
<template #icon>
|
|
<n-icon :component="typeIcon" />
|
|
</template>
|
|
<template #header>
|
|
{{ typeLabel }}
|
|
</template>
|
|
<n-space align="center">
|
|
<n-text v-if="aiExtract.type === 'auth_code'" strong style="font-size: 18px; font-family: monospace;">
|
|
{{ aiExtract.result }}
|
|
</n-text>
|
|
<n-ellipsis v-else style="max-width: 400px;">
|
|
{{ displayText }}
|
|
</n-ellipsis>
|
|
<n-button size="small" @click="copyToClipboard" tertiary>
|
|
<template #icon>
|
|
<n-icon :component="ContentCopyOutlined" />
|
|
</template>
|
|
</n-button>
|
|
<n-button v-if="isLink" size="small" @click="openLink" tertiary type="primary">
|
|
{{ t('open') }}
|
|
</n-button>
|
|
</n-space>
|
|
</n-alert>
|
|
<n-tag v-else type="success" @click="copyToClipboard" style="cursor: pointer;" size="small" :theme-overrides="tagThemeOverrides">
|
|
<template #icon>
|
|
<n-icon :component="typeIcon" />
|
|
</template>
|
|
<n-ellipsis style="max-width: 150px;">
|
|
{{ typeLabel }}: {{ displayText }}
|
|
</n-ellipsis>
|
|
</n-tag>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.ai-extract-info {
|
|
margin-bottom: 10px;
|
|
}
|
|
</style>
|