去掉多语言

This commit is contained in:
zhx 2026-05-17 14:27:13 +08:00
parent 0788ddfb8a
commit 02cbe30117
74 changed files with 343 additions and 6410 deletions

View File

@ -23,14 +23,14 @@ async function initSetupVbenForm() {
}, },
}, },
defineRules: { defineRules: {
// 输入项目必填国际化适配 // 输入项目必填提示
required: (value, _params, ctx) => { required: (value, _params, ctx) => {
if (value === undefined || value === null || value.length === 0) { if (value === undefined || value === null || value.length === 0) {
return $t('ui.formRules.required', [ctx.label]); return $t('ui.formRules.required', [ctx.label]);
} }
return true; return true;
}, },
// 选择项目必填国际化适配 // 选择项目必填提示
selectRequired: (value, _params, ctx) => { selectRequired: (value, _params, ctx) => {
if (value === undefined || value === null) { if (value === undefined || value === null) {
return $t('ui.formRules.selectRequired', [ctx.label]); return $t('ui.formRules.selectRequired', [ctx.label]);

View File

@ -145,12 +145,6 @@ export async function approveReportedNotPass(data: Record<string, any>[]) {
return requestClient.post('/approval/reported/not/pass', data); return requestClient.post('/approval/reported/not/pass', data);
} }
export async function getDynamicReportPage(params: Record<string, any>) {
return requestClient.get<LegacyPageResult>('/dynamic/report/page', {
params,
});
}
export async function pageUserIntegralOriginStream( export async function pageUserIntegralOriginStream(
params: Record<string, any>, params: Record<string, any>,
) { ) {
@ -162,26 +156,6 @@ export async function pageUserIntegralOriginStream(
); );
} }
export async function processDynamicReport(data: Record<string, any>) {
return requestClient.post('/dynamic/report', data);
}
export async function getDynamicContentPage(params: Record<string, any>) {
return requestClient.get<LegacyPageResult>('/approval/dynamic/content/page', {
params,
});
}
export async function approveDynamicContentPass(data: Array<number | string>) {
return requestClient.post('/approval/dynamic/content/pass', data);
}
export async function approveDynamicContentNotPass(
data: Array<number | string>,
) {
return requestClient.post('/approval/dynamic/content/not/pass', data);
}
export async function getViolationHistoryPage(params: Record<string, any>) { export async function getViolationHistoryPage(params: Record<string, any>) {
return requestClient.get<LegacyPageResult>('/approval/history/page', { return requestClient.get<LegacyPageResult>('/approval/history/page', {
params, params,

View File

@ -1,65 +0,0 @@
import { requestClient } from '#/api/request';
export interface LegacyPageResult<T = Record<string, any>> {
records: T[];
total: number;
}
export async function dynamicBlacklistPage(params: Record<string, any>) {
return requestClient.get<LegacyPageResult<Record<string, any>>>(
'/dynamic/blacklist/page',
{
params,
},
);
}
export async function addDynamicBlacklist(data: Record<string, any>) {
return requestClient.post('/dynamic/blacklist/add', data);
}
export async function deleteDynamicBlacklist(userId: number | string) {
return requestClient.get(`/dynamic/blacklist/delete/${userId}`);
}
export async function dynamicTagTable(params: Record<string, any>) {
return requestClient.get<LegacyPageResult<Record<string, any>>>(
'/dynamic/tag/page',
{
params,
},
);
}
export async function saveDynamicTag(data: Record<string, any>) {
return requestClient.post('/dynamic/tag/add-or-update', data);
}
export async function getDynamicPopularConfig() {
return requestClient.get<Record<string, any>>('/dynamic/popular/config');
}
export async function saveDynamicPopularConfig(data: Record<string, any>) {
return requestClient.post('/dynamic/popular/config', data);
}
export async function userDynamicTable(params: Record<string, any>) {
return requestClient.get<LegacyPageResult<Record<string, any>>>(
'/user/dynamic/page',
{
params,
},
);
}
export async function deleteUserDynamic(data: Array<number | string>) {
return requestClient.post('/user/dynamic/delete', data);
}
export async function setUserDynamicTop(data: Record<string, any>) {
return requestClient.post('/user/dynamic/setUpTop', data);
}
export async function closeUserDynamicTop(dynamicId: number | string) {
return requestClient.get(`/user/dynamic/closeTop/${dynamicId}`);
}

View File

@ -60,7 +60,7 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
} else { } else {
delete config.headers.Authorization; delete config.headers.Authorization;
} }
config.headers['Accept-Language'] = preferences.app.locale; config.headers['Accept-Language'] = 'zh-CN';
config.headers['Req-Client'] = 'Ops'; config.headers['Req-Client'] = 'Ops';
config.headers['Req-Version'] = 'v2'; config.headers['Req-Version'] = 'v2';
return config; return config;

View File

@ -9,14 +9,14 @@ import '@vben/styles/antdv-next';
import { useTitle } from '@vueuse/core'; import { useTitle } from '@vueuse/core';
import { $t, setupI18n } from '#/locales';
import SysOriginSelect from '#/components/sys-origin-select.vue'; import SysOriginSelect from '#/components/sys-origin-select.vue';
import { $t, setupLocale } from '#/locales';
import { initComponentAdapter } from './adapter/component'; import { initComponentAdapter } from './adapter/component';
import { initSetupVbenForm } from './adapter/form'; import { initSetupVbenForm } from './adapter/form';
import App from './app.vue'; import App from './app.vue';
import { setupRuntimeLocaleSync } from './locales/runtime';
import { router } from './router'; import { router } from './router';
import './styles/table-filters.css'; import './styles/table-filters.css';
async function bootstrap(namespace: string) { async function bootstrap(namespace: string) {
@ -44,8 +44,8 @@ async function bootstrap(namespace: string) {
spinning: 'spinning', spinning: 'spinning',
}); });
// 国际化 i18n 配置 // 固定中文基础文案
await setupI18n(app); await setupLocale(app);
// 配置 pinia-tore // 配置 pinia-tore
await initStores(app, { namespace }); await initStores(app, { namespace });
@ -75,7 +75,6 @@ async function bootstrap(namespace: string) {
}); });
app.mount('#app'); app.mount('#app');
setupRuntimeLocaleSync();
} }
export { bootstrap }; export { bootstrap };

View File

@ -1,3 +1,3 @@
# locale # locale
每个app使用的国际化可能不同这里用于扩展国际化的功能例如扩展 dayjs、antd组件库的多语言切换以及app本身的国际化文件 后台固定使用中文,这里只保留 dayjs、antd 组件库和应用基础文案的中文配置

View File

@ -2,117 +2,71 @@ import type { Locale } from 'antdv-next/dist/locale/index';
import type { App } from 'vue'; import type { App } from 'vue';
import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
import { ref } from 'vue'; import { ref } from 'vue';
import { import {
$t, $t,
setupI18n as coreSetup, setupLocale as coreSetup,
loadLocalesMapFromDir, mergeLocaleMessages,
} from '@vben/locales'; } from '@vben/locales';
import { preferences } from '@vben/preferences';
import antdEnLocale from 'antdv-next/dist/locale/en_US';
import antdDefaultLocale from 'antdv-next/dist/locale/zh_CN'; import antdDefaultLocale from 'antdv-next/dist/locale/zh_CN';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
const antdLocale = ref<Locale>(antdDefaultLocale); const antdLocale = ref<Locale>(antdDefaultLocale);
const modules = import.meta.glob('./langs/**/*.json'); const modules = import.meta.glob('./langs/zh-CN/*.json', { eager: true });
const runtimeModules = import.meta.glob('./langs/*/runtime.json');
const localesMap = loadLocalesMapFromDir( function normalizeModule(module: unknown) {
/\.\/langs\/([^/]+)\/(.*)\.json$/, return module && typeof module === 'object' && 'default' in module
modules, ? (module as { default: Record<string, any> }).default
); : module;
/**
*
*
* @param lang
*/
async function loadMessages(lang: SupportedLanguagesType) {
const [appLocaleMessages, runtimeMessages] = await Promise.all([
localesMap[lang]?.(),
loadRuntimeMessages(lang),
loadThirdPartyMessage(lang),
]);
const appMessages = appLocaleMessages?.default ?? {};
const { runtime: _runtime, ...rest } = appMessages;
return {
...(runtimeMessages ?? {}),
...rest,
};
} }
async function loadRuntimeMessages(lang: SupportedLanguagesType) { function loadAppMessages() {
const runtimeLoader = runtimeModules[`./langs/${lang}/runtime.json`]; const messages: Record<string, any> = {};
const runtimeMessages = runtimeLoader for (const [path, module] of Object.entries(modules)) {
? ((await runtimeLoader()) as { default?: Record<string, string> }) const fileName = path.match(/\.\/langs\/zh-CN\/(.*)\.json$/)?.[1];
: undefined; if (!fileName) {
return runtimeMessages?.default; continue;
}
const value = normalizeModule(module);
if (fileName === 'runtime') {
Object.assign(messages, value);
} else {
messages[fileName] = value;
}
}
return messages;
} }
mergeLocaleMessages(loadAppMessages());
/** /**
* *
* @param lang
*/ */
async function loadThirdPartyMessage(lang: SupportedLanguagesType) { async function loadThirdPartyMessage() {
await Promise.all([loadAntdLocale(lang), loadDayjsLocale(lang)]); await Promise.all([loadAntdLocale(), loadDayjsLocale()]);
} }
/** /**
* dayjs的语言包 * dayjs的语言包
* @param lang
*/ */
async function loadDayjsLocale(lang: SupportedLanguagesType) { async function loadDayjsLocale() {
let locale; await import('dayjs/locale/zh-cn');
switch (lang) { dayjs.locale('zh-cn');
case 'en-US': {
locale = await import('dayjs/locale/en');
break;
}
case 'zh-CN': {
locale = await import('dayjs/locale/zh-cn');
break;
}
// 默认使用英语
default: {
locale = await import('dayjs/locale/en');
}
}
if (locale) {
dayjs.locale(locale);
} else {
console.error(`Failed to load dayjs locale for ${lang}`);
}
} }
/** /**
* antd的语言包 * antd的语言包
* @param lang
*/ */
async function loadAntdLocale(lang: SupportedLanguagesType) { async function loadAntdLocale() {
switch (lang) { antdLocale.value = antdDefaultLocale;
case 'en-US': {
antdLocale.value = antdEnLocale;
break;
}
case 'zh-CN': {
antdLocale.value = antdDefaultLocale;
break;
}
}
} }
async function setupI18n(app: App, options: LocaleSetupOptions = {}) { async function setupLocale(app: App) {
await coreSetup(app, { await coreSetup(app);
defaultLocale: preferences.app.locale, await loadThirdPartyMessage();
loadMessages,
missingWarn: !import.meta.env.PROD,
...options,
});
} }
export { $t, antdLocale, setupI18n }; export { $t, antdLocale, setupLocale };

View File

@ -1,14 +0,0 @@
{
"auth": {
"login": "Login",
"register": "Register",
"codeLogin": "Code Login",
"qrcodeLogin": "Qr Code Login",
"forgetPassword": "Forget Password",
"profile": "Profile"
},
"dashboard": {
"title": "Data Platform",
"workspace": "Data Platform"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -107,7 +107,6 @@
"编辑应用": "编辑应用", "编辑应用": "编辑应用",
"编辑用户": "编辑用户", "编辑用户": "编辑用户",
"编辑游戏": "编辑游戏", "编辑游戏": "编辑游戏",
"编辑置顶动态": "编辑置顶动态",
"编辑资料": "编辑资料", "编辑资料": "编辑资料",
"编辑资源": "编辑资源", "编辑资源": "编辑资源",
"编码": "编码", "编码": "编码",
@ -161,7 +160,6 @@
"不允许跨年查询": "不允许跨年查询", "不允许跨年查询": "不允许跨年查询",
"不在预计状态内": "不在预计状态内", "不在预计状态内": "不在预计状态内",
"财富": "财富", "财富": "财富",
"财富、魅力等级多少级才能发布动态": "财富、魅力等级多少级才能发布动态",
"财富等级": "财富等级", "财富等级": "财富等级",
"菜单": "菜单", "菜单": "菜单",
"菜单编辑": "菜单编辑", "菜单编辑": "菜单编辑",
@ -223,7 +221,6 @@
"超级货运": "超级货运", "超级货运": "超级货运",
"超级经销商": "超级经销商", "超级经销商": "超级经销商",
"超商条码": "超商条码", "超商条码": "超商条码",
"超限制发动态支付金币": "超限制发动态支付金币",
"朝拜": "朝拜", "朝拜": "朝拜",
"朝拜描述": "朝拜描述", "朝拜描述": "朝拜描述",
"车辆": "车辆", "车辆": "车辆",
@ -443,13 +440,6 @@
"动画": "动画", "动画": "动画",
"动画资源": "动画资源", "动画资源": "动画资源",
"动画资源图": "动画资源图", "动画资源图": "动画资源图",
"动态-禁止用户发动态": "动态-禁止用户发动态",
"动态管理": "动态管理",
"动态举报审批": "动态举报审批",
"动态内容": "动态内容",
"动态内容审批": "动态内容审批",
"动态数量": "动态数量",
"动态图片": "动态图片",
"动效": "动效", "动效": "动效",
"冻结": "冻结", "冻结": "冻结",
"豆子": "豆子", "豆子": "豆子",
@ -856,7 +846,6 @@
"几时": "几时", "几时": "几时",
"记录数": "记录数", "记录数": "记录数",
"记录ID": "记录ID", "记录ID": "记录ID",
"继续发送每条动态支付多少金币": "继续发送每条动态支付多少金币",
"加入": "加入", "加入": "加入",
"加入成员金币": "加入成员金币", "加入成员金币": "加入成员金币",
"加入房间": "加入房间", "加入房间": "加入房间",
@ -1198,7 +1187,6 @@
"美元": "美元", "美元": "美元",
"美元汇率": "美元汇率", "美元汇率": "美元汇率",
"魅力等级": "魅力等级", "魅力等级": "魅力等级",
"魅力等级多少级才能发布动态": "魅力等级多少级才能发布动态",
"门票": "门票", "门票": "门票",
"密码": "密码", "密码": "密码",
"密码不能少于4位": "密码不能少于4位", "密码不能少于4位": "密码不能少于4位",
@ -1700,7 +1688,6 @@
"确认审核选中记录吗": "确认审核选中记录吗", "确认审核选中记录吗": "确认审核选中记录吗",
"确认选择": "确认选择", "确认选择": "确认选择",
"热门": "热门", "热门": "热门",
"热门动态权重计分设置": "热门动态权重计分设置",
"热门房间": "热门房间", "热门房间": "热门房间",
"人": "人", "人": "人",
"人身攻击": "人身攻击", "人身攻击": "人身攻击",
@ -2347,10 +2334,7 @@
"用户:数据分配图": "用户:数据分配图", "用户:数据分配图": "用户:数据分配图",
"用户1V1": "用户1V1", "用户1V1": "用户1V1",
"用户充值抽奖活动奖励": "用户充值抽奖活动奖励", "用户充值抽奖活动奖励": "用户充值抽奖活动奖励",
"用户当天发送动态数量达到限制后": "用户当天发送动态数量达到限制后",
"用户当天发送动态数量达到限制后,继续发送每条动态支付多少金币": "用户当天发送动态数量达到限制后,继续发送每条动态支付多少金币",
"用户道具流水": "用户道具流水", "用户道具流水": "用户道具流水",
"用户动态": "用户动态",
"用户房间区域变动": "用户房间区域变动", "用户房间区域变动": "用户房间区域变动",
"用户个性签名审批": "用户个性签名审批", "用户个性签名审批": "用户个性签名审批",
"用户观看广告配置": "用户观看广告配置", "用户观看广告配置": "用户观看广告配置",
@ -2486,7 +2470,6 @@
"暂无备注": "暂无备注", "暂无备注": "暂无备注",
"暂无查看权限": "暂无查看权限", "暂无查看权限": "暂无查看权限",
"暂无道具配置": "暂无道具配置", "暂无道具配置": "暂无道具配置",
"暂无动态": "暂无动态",
"暂无购买记录": "暂无购买记录", "暂无购买记录": "暂无购买记录",
"暂无可复制内容": "暂无可复制内容", "暂无可复制内容": "暂无可复制内容",
"暂无可选资源": "暂无可选资源", "暂无可选资源": "暂无可选资源",

View File

@ -1,267 +0,0 @@
import { nextTick, watch } from 'vue';
import { i18n } from '@vben/locales';
const CHINESE_RE = /[\u3400-\u9fff]/;
const SKIP_ATTRIBUTE_SELECTOR =
'[data-no-runtime-locale], .ace_editor, .monaco-editor, noscript, script, style';
const SKIP_TEXT_SELECTOR = `${SKIP_ATTRIBUTE_SELECTOR}, [contenteditable="true"], code, pre`;
const TRANSLATABLE_ATTRIBUTES = [
'alt',
'aria-label',
'placeholder',
'title',
] as const;
const elementOriginals = new WeakMap<Element, Map<string, string>>();
const textOriginals = new WeakMap<Text, string>();
let initialized = false;
let observer: MutationObserver | undefined;
let runtimeMatcherCache:
| {
dict: Record<string, string>;
locale: string;
pattern?: RegExp;
}
| undefined;
function escapeRegExp(text: string) {
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function getRuntimeDictionary(locale: string) {
const messages = i18n.global.getLocaleMessage(locale) as Record<string, unknown>;
const dictionary: Record<string, string> = {};
for (const [key, value] of Object.entries(messages)) {
if (typeof value === 'string' && CHINESE_RE.test(key)) {
dictionary[key] = value;
}
}
return dictionary;
}
function getRuntimeMatcher(locale: string) {
if (runtimeMatcherCache?.locale === locale) {
return runtimeMatcherCache;
}
const dict = locale === 'en-US' ? getRuntimeDictionary(locale) : {};
const keys = Object.keys(dict).sort((left, right) => right.length - left.length);
runtimeMatcherCache = {
dict,
locale,
pattern:
keys.length > 0
? new RegExp(keys.map((key) => escapeRegExp(key)).join('|'), 'g')
: undefined,
};
return runtimeMatcherCache;
}
function getElementOriginalMap(element: Element) {
let originalMap = elementOriginals.get(element);
if (!originalMap) {
originalMap = new Map<string, string>();
elementOriginals.set(element, originalMap);
}
return originalMap;
}
function shouldSkipElement(element: Element) {
return element.matches(SKIP_ATTRIBUTE_SELECTOR) || !!element.closest(SKIP_ATTRIBUTE_SELECTOR);
}
function shouldSkipTextNode(node: Text) {
return !!node.parentElement?.closest(SKIP_TEXT_SELECTOR);
}
function getCurrentLocale() {
return String(i18n.global.locale.value);
}
function translateText(text: string) {
if (!text || !CHINESE_RE.test(text)) {
return text;
}
const locale = getCurrentLocale();
if (locale !== 'en-US') {
return text;
}
const { dict, pattern } = getRuntimeMatcher(locale);
if (!pattern) {
return text;
}
return text.replace(pattern, (matched) => dict[matched] ?? matched);
}
function translateElementAttributes(element: Element) {
if (shouldSkipElement(element)) {
return;
}
const locale = getCurrentLocale();
const originals = getElementOriginalMap(element);
for (const attribute of TRANSLATABLE_ATTRIBUTES) {
const rawValue = element.getAttribute(attribute);
if (rawValue == null) {
continue;
}
if (locale === 'en-US') {
const originalValue = CHINESE_RE.test(rawValue)
? rawValue
: (originals.get(attribute) ?? rawValue);
if (!CHINESE_RE.test(originalValue)) {
continue;
}
const translatedValue = translateText(originalValue);
if (translatedValue !== originalValue) {
originals.set(attribute, originalValue);
if (rawValue !== translatedValue) {
element.setAttribute(attribute, translatedValue);
}
}
continue;
}
const originalValue = originals.get(attribute);
if (originalValue !== undefined && rawValue !== originalValue) {
element.setAttribute(attribute, originalValue);
}
}
}
function translateTextNode(node: Text) {
if (shouldSkipTextNode(node)) {
return;
}
const rawValue = node.nodeValue ?? '';
const locale = getCurrentLocale();
if (locale === 'en-US') {
const originalValue = CHINESE_RE.test(rawValue)
? rawValue
: (textOriginals.get(node) ?? rawValue);
if (!CHINESE_RE.test(originalValue)) {
return;
}
const translatedValue = translateText(originalValue);
if (translatedValue !== originalValue) {
textOriginals.set(node, originalValue);
if (rawValue !== translatedValue) {
node.nodeValue = translatedValue;
}
}
return;
}
const originalValue = textOriginals.get(node);
if (originalValue !== undefined && rawValue !== originalValue) {
node.nodeValue = originalValue;
}
}
function processNode(root: Node) {
const stack: Node[] = [root];
while (stack.length > 0) {
const node = stack.pop();
if (!node) {
continue;
}
if (node.nodeType === Node.TEXT_NODE) {
translateTextNode(node as Text);
continue;
}
if (node.nodeType !== Node.ELEMENT_NODE) {
continue;
}
const element = node as Element;
translateElementAttributes(element);
for (let index = element.childNodes.length - 1; index >= 0; index -= 1) {
const childNode = element.childNodes[index];
if (childNode) {
stack.push(childNode);
}
}
}
}
function refreshRuntimeLocale() {
if (typeof document === 'undefined' || !document.body) {
return;
}
processNode(document.body);
}
function startObserver() {
if (
observer ||
typeof document === 'undefined' ||
typeof MutationObserver === 'undefined' ||
!document.body
) {
return;
}
observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'attributes' || mutation.type === 'characterData') {
processNode(mutation.target);
continue;
}
mutation.addedNodes.forEach((node) => processNode(node));
}
});
observer.observe(document.body, {
attributeFilter: [...TRANSLATABLE_ATTRIBUTES],
attributes: true,
characterData: true,
childList: true,
subtree: true,
});
}
function setupRuntimeLocaleSync() {
if (initialized) {
return;
}
initialized = true;
watch(
() => getCurrentLocale(),
async () => {
runtimeMatcherCache = undefined;
await nextTick();
refreshRuntimeLocale();
startObserver();
},
{
flush: 'post',
immediate: true,
},
);
}
export { setupRuntimeLocaleSync, translateText as translateLocaleText };

View File

@ -1,8 +1,9 @@
import type { RouteRecordRaw } from 'vue-router';
import type { import type {
ComponentRecordType, ComponentRecordType,
GenerateMenuAndRoutesOptions, GenerateMenuAndRoutesOptions,
} from '@vben/types'; } from '@vben/types';
import type { RouteRecordRaw } from 'vue-router';
import { generateAccessible } from '@vben/access'; import { generateAccessible } from '@vben/access';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
@ -24,8 +25,6 @@ const ROUTE_MENU_OVERRIDES: Record<
} }
> = { > = {
BdLead: { aliases: ['BdLead'], routers: ['team/bd-lead'] }, BdLead: { aliases: ['BdLead'], routers: ['team/bd-lead'] },
DynamicTagList: { aliases: ['DynamicTag'], routers: ['dynamic/tag'] },
DynamicUserList: { aliases: ['UserDynamicList'], routers: ['user/dynamic/list'] },
OperateBusinessDevelopment: { OperateBusinessDevelopment: {
aliases: ['BusinessDevelopment'], aliases: ['BusinessDevelopment'],
routers: ['room/business-development'], routers: ['room/business-development'],
@ -63,16 +62,16 @@ const ROUTE_MENU_OVERRIDES: Record<
}; };
const LOCAL_RESIDENT_ACTIVITY_FALLBACK_ROUTES = new Set([ const LOCAL_RESIDENT_ACTIVITY_FALLBACK_ROUTES = new Set([
'ResidentWheelConfig',
'ResidentSmashGoldenEggConfig', 'ResidentSmashGoldenEggConfig',
'ResidentVoiceRoomRocket', 'ResidentVoiceRoomRocket',
'ResidentWheelConfig',
]); ]);
const LOCAL_APP_SYSTEM_FALLBACK_ROUTES = new Set([ const LOCAL_APP_SYSTEM_FALLBACK_ROUTES = new Set([
'AppSystemAgoraErrorLog', 'AppSystemAgoraErrorLog',
]); ]);
function normalizeRoutePath(path?: string | null) { function normalizeRoutePath(path?: null | string) {
return String(path || '') return String(path || '')
.trim() .trim()
.replace(/^\/+/, '') .replace(/^\/+/, '')
@ -95,9 +94,9 @@ function collectRoutePaths(route: RouteRecordRaw) {
const values = [normalizeRoutePath(route.path)]; const values = [normalizeRoutePath(route.path)];
const aliases = Array.isArray(route.alias) const aliases = Array.isArray(route.alias)
? route.alias ? route.alias
: route.alias : (route.alias
? [route.alias] ? [route.alias]
: []; : []);
values.push(...aliases.map((item) => normalizeRoutePath(String(item)))); values.push(...aliases.map((item) => normalizeRoutePath(String(item))));
const override = ROUTE_MENU_OVERRIDES[String(route.name || '')]; const override = ROUTE_MENU_OVERRIDES[String(route.name || '')];
if (override?.routers) { if (override?.routers) {

View File

@ -82,18 +82,6 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/approval/index.vue'), component: () => import('#/views/approval/index.vue'),
meta: { title: '家族头像审批' }, meta: { title: '家族头像审批' },
}, },
{
name: 'ApprovalDynamicContent',
path: 'dynamic/content/approval',
component: () => import('#/views/approval/index.vue'),
meta: { title: '动态内容审批' },
},
{
name: 'ApprovalDynamicReport',
path: 'dynamic/report',
component: () => import('#/views/approval/index.vue'),
meta: { title: '动态举报审批' },
},
{ {
name: 'ApprovalUserBankCard', name: 'ApprovalUserBankCard',
path: 'user/bank-card/approval', path: 'user/bank-card/approval',

View File

@ -1,41 +0,0 @@
import type { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
meta: {
icon: 'lucide:messages-square',
order: 14,
title: '动态管理',
},
name: 'DynamicManager',
path: '/dynamic/manager',
children: [
{
name: 'DynamicTagList',
path: 'dynamic/tag',
component: () => import('#/views/dynamic/tag-list.vue'),
meta: { title: '标签列表' },
},
{
name: 'DynamicPopularConfig',
path: 'dynamic/popular/config',
component: () => import('#/views/dynamic/popular-config.vue'),
meta: { title: '权重配置' },
},
{
name: 'DynamicUserList',
path: 'user/dynamic/list',
component: () => import('#/views/dynamic/user-dynamic-list.vue'),
meta: { title: '用户动态' },
},
{
name: 'DynamicBlacklist',
path: 'dynamic/blacklist',
component: () => import('#/views/dynamic/dynamic-blacklist.vue'),
meta: { title: '黑名单' },
},
],
},
];
export default routes;

View File

@ -1,7 +1,5 @@
import { import {
approveData, approveData,
approveDynamicContentNotPass,
approveDynamicContentPass,
approvePhotoWallNotPass, approvePhotoWallNotPass,
approvePhotoWallPass, approvePhotoWallPass,
approveReportedNotPass, approveReportedNotPass,
@ -10,8 +8,6 @@ import {
approveUserBankCardNotPass, approveUserBankCardNotPass,
approveUserBankCardPass, approveUserBankCardPass,
batchProcessFeedback, batchProcessFeedback,
getDynamicContentPage,
getDynamicReportPage,
getFamilyApprovalPage, getFamilyApprovalPage,
getFeedbackPage, getFeedbackPage,
getPhotoWallApprovalPage, getPhotoWallApprovalPage,
@ -23,7 +19,6 @@ import {
getUserProfileDescApprovalPage, getUserProfileDescApprovalPage,
getViolationHistoryPage, getViolationHistoryPage,
markFamilyNotPass, markFamilyNotPass,
processDynamicReport,
} from '#/api/legacy/approval'; } from '#/api/legacy/approval';
type ApprovalActionType = 'danger' | 'primary'; type ApprovalActionType = 'danger' | 'primary';
@ -181,12 +176,6 @@ const REPORT_STATUS_OPTIONS: ApprovalFilterOption[] = [
{ label: '无效', value: 2 }, { label: '无效', value: 2 },
]; ];
const DYNAMIC_REPORT_STATUS_OPTIONS: ApprovalFilterOption[] = [
{ label: '待审核', value: 0 },
{ label: '违规', value: 1 },
{ label: '正常', value: 2 },
];
const REPORT_TYPE_OPTIONS: ApprovalFilterOption[] = [ const REPORT_TYPE_OPTIONS: ApprovalFilterOption[] = [
{ label: '非法信息', value: 0 }, { label: '非法信息', value: 0 },
{ label: '人身攻击', value: 1 }, { label: '人身攻击', value: 1 },
@ -230,7 +219,7 @@ function splitCsv(value: any) {
return []; return [];
} }
if (Array.isArray(value)) { if (Array.isArray(value)) {
return value.filter(Boolean).map((item) => String(item)); return value.filter(Boolean).map(String);
} }
return String(value) return String(value)
.split(',') .split(',')
@ -248,7 +237,7 @@ function buildApprovalDataAction(
await approveData({ await approveData({
approvalStatus, approvalStatus,
approvalType, approvalType,
waitApprovalUser: records.map(mapRecord), waitApprovalUser: records.map((record) => mapRecord(record)),
}); });
}, },
label: approvalStatus === 'PASS' ? '鉴定通过' : '鉴定违规', label: approvalStatus === 'PASS' ? '鉴定通过' : '鉴定违规',
@ -262,7 +251,7 @@ function familyNotPassAction(
): ApprovalBulkAction { ): ApprovalBulkAction {
return { return {
execute: async (records) => { execute: async (records) => {
const approvalRecords = records.map(mapRecord); const approvalRecords = records.map((record) => mapRecord(record));
await approveData({ await approveData({
approvalStatus: 'NOT_PASS', approvalStatus: 'NOT_PASS',
approvalType, approvalType,
@ -867,9 +856,9 @@ const approvalPages: Record<string, ApprovalPage> = {
`年龄:${record.userProfile?.age || '-'}`, `年龄:${record.userProfile?.age || '-'}`,
`创建时间:${record.createTime || '-'}`, `创建时间:${record.createTime || '-'}`,
`修改时间:${record.updateTime || '-'}`, `修改时间:${record.updateTime || '-'}`,
...(query.themeStatus !== 'PENDING' ...(query.themeStatus === 'PENDING'
? [`审核人:${record.optUserNickname || '-'}`] ? []
: []), : [`审核人:${record.optUserNickname || '-'}`]),
], ],
title: record.userProfile?.userNickname || '-', title: record.userProfile?.userNickname || '-',
titleUserId: record.userProfile?.id, titleUserId: record.userProfile?.id,
@ -1183,10 +1172,10 @@ const approvalPages: Record<string, ApprovalPage> = {
record.violationType === 'PHOTO_WALL' || record.violationType === 'PHOTO_WALL' ||
record.violationType === 'ROOM_AVATAR' record.violationType === 'ROOM_AVATAR'
? 'images' ? 'images'
: record.violationType === 'LIVE' || : (record.violationType === 'LIVE' ||
record.violationType === 'SHORT_VIDEO' record.violationType === 'SHORT_VIDEO'
? 'videos' ? 'videos'
: 'text'; : 'text');
return { return {
content: content:
contentType === 'text' ? record.content || '-' : splitCsv(record.content), contentType === 'text' ? record.content || '-' : splitCsv(record.content),
@ -1471,238 +1460,6 @@ const approvalPages: Record<string, ApprovalPage> = {
}, },
title: '家族审批', title: '家族审批',
}, },
'/approval/manager/dynamic/content/approval': {
actions: [
{
execute: async (records) => {
await approveDynamicContentPass(records.map((record) => record.dynamicId));
},
label: '鉴定通过',
show: (query) => query.approveStatus === 'PENDING',
type: 'primary',
},
{
execute: async (records) => {
await approveDynamicContentNotPass(
records.map((record) => record.dynamicId),
);
},
label: '鉴定违规',
show: (query) =>
query.approveStatus === 'PENDING' || query.approveStatus === 'PASS',
type: 'danger',
},
],
buildCard: (record, query) => ({
actions: [{ label: '账号处理', type: 'account', value: record.userId }],
galleryImages: (record.pictures || [])
.map((item: any) => item.resourceUrl)
.filter(Boolean),
leadText: record.dynamicContent || '',
lines: [
`性别:${record.userSexName || record.userBaseInfo?.userSexName || '-'}`,
`年龄:${record.userBaseInfo?.age || '-'}`,
`创建时间:${record.createTime || '-'}`,
...(query.approveStatus !== 'PENDING'
? [`审批时间:${record.updateTime || '-'}`]
: []),
],
overlay:
(record.pictures || [])
.map((item: any) => item.labelNames)
.filter(Boolean)
.join(' / ') || '',
title: record.userBaseInfo?.userNickname || '-',
titleUserId: record.userId,
}),
defaultQuery: {
approveStatus: 'PENDING',
cursor: 1,
endDateTime: '',
limit: 20,
startDateTime: '',
sysOrigin: DEFAULT_SYS_ORIGIN,
userId: '',
},
fetch: getDynamicContentPage,
filters: [
{ field: 'sysOrigin', label: '系统', placeholder: '系统', type: 'sysOrigin' },
{
field: 'approveStatus',
label: '审核',
options: COMMON_APPROVAL_ALL_OPTIONS,
placeholder: '审核',
type: 'select',
},
{
endField: 'endDateTime',
label: '时间范围',
placeholder: '时间范围',
startField: 'startDateTime',
type: 'dateRange',
},
{ field: 'userId', label: '用户ID', placeholder: '用户ID', type: 'input' },
],
kind: 'gallery',
selectable: (query) =>
query.approveStatus === 'PENDING' || query.approveStatus === 'PASS',
title: '动态内容审批',
},
'/approval/manager/dynamic/report': {
actions: [
{
execute: async (records) => {
await processDynamicReport({
approvalStatus: 2,
contentIds: records.map((record) => record.dynamicContentId),
});
},
label: '正常',
show: (query) => query.approvalStatus === 0,
type: 'primary',
},
{
execute: async (records) => {
await processDynamicReport({
approvalStatus: 1,
contentIds: records.map((record) => record.dynamicContentId),
});
},
label: '违规',
show: (query) => query.approvalStatus === 0,
type: 'danger',
},
],
columns: [
{
key: 'sysOrigin',
render: (record) => ({
text: record.sysOrigin || '-',
type: 'origin',
}),
title: '系统',
width: 90,
},
{
key: 'reportUser',
render: (record) => ({
text: record.reportUser?.userNickname || '-',
type: 'link',
userId: record.reportUser?.id,
}),
title: '举报人',
},
{
key: 'reportedUser',
render: (record) => ({
text: record.reportedUser?.userNickname || '-',
type: 'link',
userId: record.reportedUser?.id,
}),
title: '被举报人',
},
{
key: 'reportType',
render: (record) => ({
text:
REPORT_TYPE_OPTIONS.find((item) => item.value === record.reportType)
?.label || '-',
type: 'text',
}),
title: '举报类型',
},
{
key: 'dynamicContent',
render: (record) => ({
text: record.dynamicContent || '-',
type: 'text',
}),
title: '动态内容',
},
{
key: 'time',
render: (record) => ({
text:
record.createTime && record.updateTime
? `${record.createTime} / ${record.updateTime}`
: record.createTime || '-',
type: 'text',
}),
title: '时间',
width: 240,
},
],
defaultQuery: {
approvalStatus: 0,
cursor: 1,
endTime: '',
limit: 20,
reportType: '',
reportUserId: '',
reportedUserId: '',
startTime: '',
sysOrigin: DEFAULT_SYS_ORIGIN,
},
expandable: (record) => [
{ label: '举报图片', type: 'images', value: splitCsv(record.reportedUrls) },
{
label: '动态图片',
type: 'images',
value: (record.dynamicPictures || [])
.map((item: any) => item.resourceUrl)
.filter(Boolean),
},
{ label: '举报内容', value: record.reportedContent || '-' },
],
fetch: getDynamicReportPage,
filters: [
{ field: 'sysOrigin', label: '系统', placeholder: '系统', type: 'sysOrigin' },
{
field: 'approvalStatus',
label: '状态',
options: DYNAMIC_REPORT_STATUS_OPTIONS,
placeholder: '状态',
type: 'select',
},
{
clearable: true,
field: 'reportType',
label: '举报类型',
options: REPORT_TYPE_OPTIONS,
placeholder: '举报类型',
type: 'select',
},
{
field: 'reportUserId',
label: '举报人ID',
placeholder: '举报人ID',
type: 'input',
},
{
field: 'reportedUserId',
label: '被举报人ID',
placeholder: '被举报人ID',
type: 'input',
},
{
endField: 'endTime',
label: '时间范围',
placeholder: '时间范围',
startField: 'startTime',
type: 'dateRange',
},
],
kind: 'table',
rowActions: (record) => [
{
label: '被举报账号处理',
type: 'account',
value: record.reportedUser?.id,
},
],
selectable: (query) => query.approvalStatus === 0,
title: '动态举报审批',
},
'/approval/manager/user/bank-card/approval': { '/approval/manager/user/bank-card/approval': {
actions: [ actions: [
{ {
@ -1760,15 +1517,15 @@ const approvalPages: Record<string, ApprovalPage> = {
color: color:
record.status === 'PENDING' record.status === 'PENDING'
? 'default' ? 'default'
: record.status === 'PASS' : (record.status === 'PASS'
? 'success' ? 'success'
: 'error', : 'error'),
text: text:
record.status === 'PENDING' record.status === 'PENDING'
? '待审核' ? '待审核'
: record.status === 'PASS' : (record.status === 'PASS'
? '通过' ? '通过'
: '驳回', : '驳回'),
type: 'tag', type: 'tag',
}), }),
title: '状态', title: '状态',
@ -1821,12 +1578,11 @@ const approvalPages: Record<string, ApprovalPage> = {
export { export {
APPROVAL_TYPE_OPTIONS, APPROVAL_TYPE_OPTIONS,
approvalPages,
BANK_CARD_OPTIONS, BANK_CARD_OPTIONS,
DYNAMIC_REPORT_STATUS_OPTIONS,
FEEDBACK_STATUS_OPTIONS, FEEDBACK_STATUS_OPTIONS,
PHOTO_WALL_STATUS_OPTIONS, PHOTO_WALL_STATUS_OPTIONS,
REPORT_STATUS_OPTIONS, REPORT_STATUS_OPTIONS,
REPORT_TYPE_OPTIONS, REPORT_TYPE_OPTIONS,
SYS_ORIGIN_OPTIONS, SYS_ORIGIN_OPTIONS,
approvalPages,
}; };

View File

@ -1,256 +0,0 @@
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue';
import {
OSS_FILE_BUCKETS,
getAccessImgUrl,
simpleUploadFile,
} from '#/api/legacy/oss';
import { saveDynamicTag } from '#/api/legacy/dynamic';
import { LANGUAGE_OPTIONS } from '#/views/system/shared';
import {
Button,
Form,
FormItem,
Image,
Input,
InputNumber,
Modal,
Select,
Space,
message,
} from 'antdv-next';
const props = defineProps<{
open: boolean;
record: null | Record<string, any>;
sysOriginOptions: Array<{ label: string; value: string }>;
}>();
const emit = defineEmits<{
close: [];
success: [];
}>();
const fileInputRef = ref<HTMLInputElement | null>(null);
const saving = ref(false);
const uploadLoading = ref(false);
const form = reactive({
del: false as boolean,
description: '',
id: '',
imageUrl: '',
languageType: '',
sort: undefined as number | string | undefined,
sysOrigin: '',
tag: '',
});
watch(
() => props.open,
(open) => {
if (!open) {
return;
}
const record = props.record;
form.id = String(record?.id || '');
form.imageUrl = record?.imageUrl || '';
form.sysOrigin =
record?.sysOrigin || props.sysOriginOptions[0]?.value || 'LIKEI';
form.tag = record?.tag || '';
form.description = record?.description || '';
form.del = Boolean(record?.del);
form.sort = record?.sort ?? undefined;
form.languageType = record?.languageType || '';
},
{ immediate: true },
);
const title = ref('新增标签');
watch(
() => props.record,
(value) => {
title.value = value?.id ? '修改标签' : '新增标签';
},
{ immediate: true },
);
function openFileDialog() {
fileInputRef.value?.click();
}
async function handleFileChange(event: Event) {
const target = event.target as HTMLInputElement;
const file = target.files?.[0];
target.value = '';
if (!file) {
return;
}
uploadLoading.value = true;
try {
const result = await simpleUploadFile(file, OSS_FILE_BUCKETS.other);
form.imageUrl = getAccessImgUrl(result.name);
message.success('上传成功');
} finally {
uploadLoading.value = false;
}
}
function resetImage() {
form.imageUrl = '';
}
async function handleSubmit() {
if (!form.imageUrl) {
message.warning('请上传背景图');
return;
}
if (!form.sysOrigin) {
message.warning('请选择系统');
return;
}
if (!form.tag.trim()) {
message.warning('请输入标签');
return;
}
if (!form.description.trim()) {
message.warning('请输入描述');
return;
}
if (!form.languageType) {
message.warning('请选择语言');
return;
}
if (form.sort === undefined || form.sort === null || form.sort === '') {
message.warning('请输入排序');
return;
}
saving.value = true;
try {
await saveDynamicTag({
del: form.del,
description: form.description.trim(),
id: form.id || undefined,
imageUrl: form.imageUrl,
languageType: form.languageType,
sort: Number(form.sort),
sysOrigin: form.sysOrigin,
tag: form.tag.trim(),
});
message.success('保存成功');
emit('success');
emit('close');
} finally {
saving.value = false;
}
}
</script>
<template>
<Modal
:confirm-loading="saving"
:open="open"
destroy-on-close
:title="title"
width="680"
@cancel="emit('close')"
@ok="handleSubmit"
>
<Form layout="vertical">
<FormItem label="背景">
<input
ref="fileInputRef"
accept="image/*"
class="hidden-input"
type="file"
@change="handleFileChange"
/>
<div class="cover-field">
<div v-if="form.imageUrl" class="cover-preview">
<Image :src="form.imageUrl" class="cover-image" />
</div>
<div v-else class="cover-empty">请选择背景图</div>
<Space>
<Button :loading="uploadLoading" @click="openFileDialog">
{{ form.imageUrl ? '重新上传' : '上传图片' }}
</Button>
<Button v-if="form.imageUrl" danger @click="resetImage">移除</Button>
</Space>
</div>
</FormItem>
<FormItem label="系统">
<SysOriginSelect v-model:value="form.sysOrigin"
:options="sysOriginOptions"
></SysOriginSelect>
</FormItem>
<FormItem label="标签">
<Input v-model:value="form.tag" />
</FormItem>
<FormItem label="描述">
<Input v-model:value="form.description" />
</FormItem>
<FormItem label="语言">
<Select
option-label-prop="label"
v-model:value="form.languageType"
:options="LANGUAGE_OPTIONS.map((item) => ({ label: item.name, value: item.value }))"
/>
</FormItem>
<FormItem label="状态">
<Select
option-label-prop="label"
v-model:value="form.del"
:options="[
{ label: '上架', value: false as any },
{ label: '下架', value: true as any },
]"
/>
</FormItem>
<FormItem label="排序">
<InputNumber
v-model:value="form.sort"
:min="0"
placeholder="数字越大越靠前"
style="width: 100%"
/>
</FormItem>
</Form>
</Modal>
</template>
<style scoped>
.hidden-input {
display: none;
}
.cover-field {
display: grid;
gap: 12px;
}
.cover-preview,
.cover-empty {
align-items: center;
background: #f8fafc;
border: 1px dashed rgb(148 163 184 / 35%);
border-radius: 16px;
display: flex;
justify-content: center;
min-height: 180px;
overflow: hidden;
}
.cover-empty {
color: #94a3b8;
}
.cover-image {
height: 180px;
object-fit: cover;
width: 100%;
}
</style>

View File

@ -1,267 +0,0 @@
<script lang="ts" setup>
import {
reactive,
ref } from 'vue';
import { useRouter } from 'vue-router';
import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores';
import {
addDynamicBlacklist,
deleteDynamicBlacklist,
dynamicBlacklistPage,
} from '#/api/legacy/dynamic';
import AccountInput from '#/components/account-input.vue';
import SysOriginLabel from '#/components/sys-origin-label.vue';
import { formatDate,
getAllowedSysOrigins } from '#/views/system/shared';
import {
Button,
Card,
Form,
FormItem,
Input,
Modal,
Pagination,
Table,
Tag,
message,
} from 'antdv-next';
defineOptions({ name: 'DynamicBlacklist' });
const router = useRouter();
const accessStore = useAccessStore();
const sysOriginOptions = getAllowedSysOrigins(accessStore.accessCodes || []);
const loading = ref(false);
const total = ref(0);
const list = ref<Array<Record<string, any>>>([]);
const modalOpen = ref(false);
const saving = ref(false);
const query = reactive({
cursor: 1,
limit: 20,
sysOrigin: sysOriginOptions[0]?.value ?? 'LIKEI',
userId: '',
});
const form = reactive({
account: '',
});
const columns = [
{ dataIndex: 'originSys', key: 'originSys', title: '系统', width: 120 },
{ dataIndex: 'user', key: 'user', title: '用户', width: 220 },
{ dataIndex: 'nickname', key: 'nickname', title: '昵称', width: 180 },
{ dataIndex: 'userSex', key: 'userSex', title: '性别', width: 100 },
{ dataIndex: 'country', key: 'country', title: '国家/地区', width: 160 },
{ dataIndex: 'createTime', key: 'createTime', title: '拉黑时间', width: 180 },
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 100 },
];
async function loadData(reset = false) {
if (reset) {
query.cursor = 1;
}
loading.value = true;
try {
const result = await dynamicBlacklistPage({ ...query });
list.value = result.records || [];
total.value = result.total || 0;
} finally {
loading.value = false;
}
}
function handleSearch() {
loadData(true);
}
function handlePageChange(page: number, pageSize: number) {
query.cursor = page;
query.limit = pageSize;
loadData();
}
function openCreate() {
form.account = '';
modalOpen.value = true;
}
function openUserDetails(record: Record<string, any>) {
const userId = record.id || record.userId;
if (!userId) {
return;
}
router.push(`/common/user/deatils/${userId}`);
}
async function handleDelete(record: Record<string, any>) {
loading.value = true;
try {
await deleteDynamicBlacklist(record.id);
message.success('删除成功');
await loadData();
} finally {
loading.value = false;
}
}
async function handleSubmit() {
if (!form.account.trim()) {
message.warning('请输入账号');
return;
}
saving.value = true;
try {
await addDynamicBlacklist({
account: form.account.trim(),
sysOrigin: query.sysOrigin,
});
message.success('保存成功');
modalOpen.value = false;
await loadData(true);
} finally {
saving.value = false;
}
}
loadData(true);
</script>
<template>
<Page title="黑名单">
<Card>
<div class="toolbar">
<SysOriginSelect
v-model:value="query.sysOrigin"
style="width: 140px"
@change="handleSearch"
:options="sysOriginOptions"
></SysOriginSelect>
<AccountInput
v-model:value="query.userId"
:sys-origin="query.sysOrigin"
placeholder="用户ID"
style="width: 220px"
/>
<Button :loading="loading" type="primary" @click="handleSearch">
搜索
</Button>
<Button @click="openCreate">加入黑名单</Button>
</div>
<Table
:columns="columns"
:data-source="list"
:loading="loading"
:pagination="false"
row-key="id"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'originSys'">
<SysOriginLabel :value="record.originSys || record.sysOrigin" />
</template>
<template v-else-if="column.key === 'user'">
<div class="user-cell">
<img
:src="record.userAvatar || 'https://dummyimage.com/44x44/e2e8f0/64748b&text=U'"
alt=""
class="user-avatar"
>
<div class="user-info">
<div>{{ record.actualAccount || '-' }}</div>
<Tag v-if="record.countryCode">{{ record.countryCode }}</Tag>
</div>
</div>
</template>
<template v-else-if="column.key === 'nickname'">
<Button type="link" @click="openUserDetails(record)">
{{ record.userNickname || '-' }}
</Button>
</template>
<template v-else-if="column.key === 'userSex'">
{{ Number(record.userSex) === 1 ? '男' : '女' }}
</template>
<template v-else-if="column.key === 'country'">
{{ record.countryName || '-' }}
</template>
<template v-else-if="column.key === 'createTime'">
{{ formatDate(record.createTime) }}
</template>
<template v-else-if="column.key === 'actions'">
<Button danger size="small" type="link" @click="handleDelete(record)">
删除
</Button>
</template>
</template>
</Table>
<div class="pagination">
<Pagination
:current="query.cursor"
:page-size="query.limit"
:total="total"
show-size-changer
@change="handlePageChange"
@showSizeChange="handlePageChange"
/>
</div>
</Card>
<Modal
:confirm-loading="saving"
:open="modalOpen"
destroy-on-close
title="动态-禁止用户发动态"
@cancel="modalOpen = false"
@ok="handleSubmit"
>
<Form layout="vertical">
<FormItem label="平台">
<Input :value="query.sysOrigin" disabled />
</FormItem>
<FormItem label="账号">
<Input v-model:value="form.account" />
</FormItem>
</Form>
</Modal>
</Page>
</template>
<style scoped>
.toolbar {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 16px;
}
.user-cell {
align-items: center;
display: flex;
gap: 10px;
}
.user-avatar {
border-radius: 999px;
height: 44px;
object-fit: cover;
width: 44px;
}
.user-info {
min-width: 0;
}
.pagination {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
</style>

View File

@ -1,139 +0,0 @@
<script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue';
import { Page } from '@vben/common-ui';
import {
getDynamicPopularConfig,
saveDynamicPopularConfig,
} from '#/api/legacy/dynamic';
import {
Button,
Card,
Form,
FormItem,
InputNumber,
Modal,
message,
} from 'antdv-next';
defineOptions({ name: 'DynamicPopularConfig' });
const loading = ref(false);
const saving = ref(false);
const form = reactive({
commentScore: undefined as number | undefined,
dynamicFees: undefined as number | undefined,
levelLimit: undefined as number | undefined,
likeScore: undefined as number | undefined,
quantityLimit: undefined as number | undefined,
});
async function loadData() {
loading.value = true;
try {
const result = await getDynamicPopularConfig();
form.likeScore = result?.likeScore;
form.commentScore = result?.commentScore;
form.levelLimit = result?.levelLimit;
form.quantityLimit = result?.quantityLimit;
form.dynamicFees = result?.dynamicFees;
} finally {
loading.value = false;
}
}
function handleSubmit() {
Modal.confirm({
title: '是否确定修改配置值?',
async onOk() {
saving.value = true;
try {
await saveDynamicPopularConfig({ ...form });
message.success('保存成功');
await loadData();
} finally {
saving.value = false;
}
},
});
}
onMounted(loadData);
</script>
<template>
<Page title="权重配置">
<Card :loading="loading">
<Form layout="vertical">
<div class="section">
<div class="section-title">热门动态权重计分设置</div>
<div class="grid">
<FormItem label="点赞增加分数">
<InputNumber v-model:value="form.likeScore" :min="0" style="width: 100%" />
</FormItem>
<FormItem label="评论增加分数">
<InputNumber v-model:value="form.commentScore" :min="0" style="width: 100%" />
</FormItem>
</div>
</div>
<div class="section">
<div class="section-title">财富魅力等级多少级才能发布动态</div>
<FormItem label="等级限制">
<InputNumber v-model:value="form.levelLimit" :min="0" style="width: 100%" />
</FormItem>
</div>
<div class="section">
<div class="section-title">每个用户当天允许发送多少条动态</div>
<FormItem label="动态数量">
<InputNumber v-model:value="form.quantityLimit" :min="0" style="width: 100%" />
</FormItem>
</div>
<div class="section">
<div class="section-title">
用户当天发送动态数量达到限制后继续发送每条动态支付多少金币
</div>
<FormItem label="金币数">
<InputNumber v-model:value="form.dynamicFees" :min="0" style="width: 100%" />
</FormItem>
</div>
<Button :loading="saving" type="primary" @click="handleSubmit">
修改
</Button>
</Form>
</Card>
</Page>
</template>
<style scoped>
.section + .section {
margin-top: 20px;
}
.section-title {
background: rgb(37 99 235 / 6%);
border-left: 3px solid #2563eb;
border-radius: 10px;
color: #1e293b;
margin-bottom: 16px;
padding: 12px 14px;
}
.grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (width <= 768px) {
.grid {
grid-template-columns: 1fr;
}
}
</style>

View File

@ -1,216 +0,0 @@
<script lang="ts" setup>
import {
computed,
reactive,
ref } from 'vue';
import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores';
import { dynamicTagTable } from '#/api/legacy/dynamic';
import SysOriginLabel from '#/components/sys-origin-label.vue';
import {
LANGUAGE_OPTIONS,
formatDate,
getAllowedSysOrigins,
} from '#/views/system/shared';
import {
Button,
Card,
Image,
Pagination,
Select,
Space,
Table,
Tag
} from 'antdv-next';
import TagEditModal from './components/tag-edit-modal.vue';
defineOptions({ name: 'DynamicTagList' });
const accessStore = useAccessStore();
const sysOriginOptions = getAllowedSysOrigins(accessStore.accessCodes || []);
const loading = ref(false);
const total = ref(0);
const list = ref<Array<Record<string, any>>>([]);
const modalOpen = ref(false);
const activeRecord = ref<null | Record<string, any>>(null);
const query = reactive({
cursor: 1,
del: false as boolean,
languageType: '',
limit: 20,
sysOrigin: sysOriginOptions[0]?.value ?? 'LIKEI',
});
const columns = [
{ dataIndex: 'id', key: 'id', title: 'ID', width: 90 },
{ dataIndex: 'sysOrigin', key: 'sysOrigin', title: '归属系统', width: 120 },
{ dataIndex: 'imageUrl', key: 'imageUrl', title: '背景', width: 240 },
{ dataIndex: 'tag', key: 'tag', title: '标签', width: 160 },
{ dataIndex: 'description', key: 'description', title: '描述', width: 220 },
{ dataIndex: 'sort', key: 'sort', title: '排序', width: 100 },
{ dataIndex: 'languageType', key: 'languageType', title: '语言', width: 120 },
{ dataIndex: 'del', key: 'del', title: '状态', width: 120 },
{ dataIndex: 'updateTime', key: 'updateTime', title: '更新时间', width: 180 },
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 100 },
];
const scroll = computed(() => ({ x: 1500 }));
function getLanguage(value?: string) {
return LANGUAGE_OPTIONS.find((item) => item.value === value)?.name || '-';
}
async function loadData(reset = false) {
if (reset) {
query.cursor = 1;
}
loading.value = true;
try {
const result = await dynamicTagTable({ ...query });
list.value = result.records || [];
total.value = result.total || 0;
} finally {
loading.value = false;
}
}
function handleSearch() {
loadData(true);
}
function handlePageChange(page: number, pageSize: number) {
query.cursor = page;
query.limit = pageSize;
loadData();
}
function openCreate() {
activeRecord.value = null;
modalOpen.value = true;
}
function openEdit(record: Record<string, any>) {
activeRecord.value = { ...record };
modalOpen.value = true;
}
loadData(true);
</script>
<template>
<Page title="标签列表">
<Card>
<div class="toolbar">
<Space wrap>
<SysOriginSelect
v-model:value="query.sysOrigin"
style="width: 140px"
@change="handleSearch"
:options="sysOriginOptions"
></SysOriginSelect>
<Select
v-model:value="query.del"
option-label-prop="label"
style="width: 140px"
@change="handleSearch"
:options="[{ label: '上架', value: false as any }, { label: '下架', value: true as any }]"
/>
<Select
v-model:value="query.languageType"
allow-clear
option-label-prop="label"
placeholder="语言"
style="width: 160px"
@change="handleSearch"
:options="LANGUAGE_OPTIONS.map((item) => ({ label: item.name, value: item.value as any }))"
/>
<Button :loading="loading" type="primary" @click="handleSearch">
搜索
</Button>
<Button @click="openCreate">新增</Button>
</Space>
</div>
<Table
:columns="columns"
:data-source="list"
:loading="loading"
:pagination="false"
:scroll="scroll"
row-key="id"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'sysOrigin'">
<SysOriginLabel :value="record.sysOrigin" />
</template>
<template v-else-if="column.key === 'imageUrl'">
<Image :src="record.imageUrl" class="cover-image" />
</template>
<template v-else-if="column.key === 'languageType'">
{{ getLanguage(record.languageType) }}
</template>
<template v-else-if="column.key === 'del'">
<Tag :color="record.del ? 'error' : 'success'">
{{ record.del ? '已下架' : '已上架' }}
</Tag>
</template>
<template v-else-if="column.key === 'updateTime'">
{{ formatDate(record.updateTime || record.createTime) }}
</template>
<template v-else-if="column.key === 'actions'">
<Button size="small" type="link" @click="openEdit(record)">
修改
</Button>
</template>
</template>
</Table>
<div class="pagination">
<Pagination
:current="query.cursor"
:page-size="query.limit"
:total="total"
show-size-changer
@change="handlePageChange"
@showSizeChange="handlePageChange"
/>
</div>
</Card>
<TagEditModal
:open="modalOpen"
:record="activeRecord"
:sys-origin-options="sysOriginOptions"
@close="modalOpen = false"
@success="loadData(true)"
/>
</Page>
</template>
<style scoped>
.toolbar {
margin-bottom: 16px;
}
.cover-image {
border-radius: 12px;
height: 84px;
object-fit: cover;
width: 168px;
}
.pagination {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
</style>

View File

@ -1,519 +0,0 @@
<script lang="ts" setup>
import {
computed,
reactive,
ref,
watch } from 'vue';
import { useRouter } from 'vue-router';
import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores';
import {
closeUserDynamicTop,
deleteUserDynamic,
setUserDynamicTop,
userDynamicTable,
} from '#/api/legacy/dynamic';
import AccountInput from '#/components/account-input.vue';
import SysOriginTag from '#/components/sys-origin-tag.vue';
import { formatDate,
getAllowedSysOrigins } from '#/views/system/shared';
import {
Button,
Card,
Checkbox,
Col,
DateRangePicker,
Empty,
Image,
InputNumber,
Modal,
Pagination,
Row,
Select,
Space,
Spin,
Tag,
message
} from 'antdv-next';
import AccountHandleModal from '#/views/approval/components/account-handle-modal.vue';
defineOptions({ name: 'DynamicUserDynamicList' });
const router = useRouter();
const accessStore = useAccessStore();
const sysOriginOptions = getAllowedSysOrigins(accessStore.accessCodes || []);
const loading = ref(false);
const total = ref(0);
const list = ref<Array<Record<string, any>>>([]);
const rangeDate = ref<[string, string] | null>(null);
const selectedKeys = ref<string[]>([]);
const topModalOpen = ref(false);
const topSaving = ref(false);
const accountHandleOpen = ref(false);
const activeUserId = ref('');
const topForm = reactive({
dynamicId: '',
sysOrigin: '',
weights: undefined as number | undefined,
});
const query = reactive({
cursor: 1,
endTime: '',
limit: 30,
startTime: '',
sysOrigin: sysOriginOptions[0]?.value ?? 'LIKEI',
top: '',
userId: '',
});
watch(rangeDate, (value) => {
query.startTime = value?.[0] || '';
query.endTime = value?.[1] || '';
});
const allChecked = computed(
() => list.value.length > 0 && selectedKeys.value.length === list.value.length,
);
const indeterminate = computed(
() =>
selectedKeys.value.length > 0 &&
selectedKeys.value.length < list.value.length,
);
function getPictures(record: Record<string, any>) {
return (Array.isArray(record.pictures) ? record.pictures : [])
.map((item: any) => item?.resourceUrl || item?.url || '')
.filter(Boolean);
}
function getUserAvatar(record: Record<string, any>) {
return record.userBaseInfo?.userAvatar || record.userAvatar || '';
}
function getUserName(record: Record<string, any>) {
return record.userBaseInfo?.userNickname || record.userNickname || '-';
}
function getUserAccount(record: Record<string, any>) {
return record.userBaseInfo?.actualAccount || record.actualAccount || '-';
}
function getRowKey(record: Record<string, any>) {
return String(record.id || '');
}
async function loadData(reset = false) {
if (reset) {
query.cursor = 1;
}
loading.value = true;
try {
const result = await userDynamicTable({ ...query });
list.value = result.records || [];
total.value = result.total || 0;
selectedKeys.value = [];
} finally {
loading.value = false;
}
}
function handleSearch() {
loadData(true);
}
function handlePageChange(page: number, pageSize: number) {
query.cursor = page;
query.limit = pageSize;
loadData();
}
function toggleAll(checked: boolean) {
selectedKeys.value = checked ? list.value.map((item) => getRowKey(item)) : [];
}
function toggleRecord(record: Record<string, any>) {
const key = getRowKey(record);
const index = selectedKeys.value.findIndex((item) => item === key);
if (index >= 0) {
selectedKeys.value.splice(index, 1);
return;
}
selectedKeys.value.push(key);
}
function openUserDetails(record: Record<string, any>) {
const userId = record.userId || record.userBaseInfo?.id;
if (!userId) {
return;
}
router.push(`/common/user/deatils/${userId}`);
}
function openAccountHandle(record: Record<string, any>) {
activeUserId.value = String(record.userId || record.userBaseInfo?.id || '');
accountHandleOpen.value = true;
}
function openTopEdit(record: Record<string, any>) {
topForm.dynamicId = String(record.id || '');
topForm.sysOrigin = query.sysOrigin;
topForm.weights = Number(record.weights || 0);
topModalOpen.value = true;
}
async function handleCloseTop(record: Record<string, any>) {
await closeUserDynamicTop(record.id);
message.success('操作成功');
await loadData(true);
}
async function handleSubmitTop() {
if (!topForm.dynamicId) {
return;
}
if (topForm.weights === undefined || topForm.weights === null) {
message.warning('请输入权重');
return;
}
topSaving.value = true;
try {
await setUserDynamicTop({
dynamicId: topForm.dynamicId,
sysOrigin: topForm.sysOrigin,
weights: topForm.weights,
});
message.success('保存成功');
topModalOpen.value = false;
await loadData(true);
} finally {
topSaving.value = false;
}
}
function handleDeleteSelected() {
if (selectedKeys.value.length === 0) {
message.warning('请勾选审批项');
return;
}
Modal.confirm({
title: '确认删除选中记录吗?',
async onOk() {
await deleteUserDynamic(selectedKeys.value);
message.success('删除成功');
await loadData();
},
});
}
loadData(true);
</script>
<template>
<Page title="用户动态">
<div class="page-grid">
<Card>
<div class="toolbar">
<Space wrap>
<SysOriginSelect
v-model:value="query.sysOrigin"
style="width: 140px"
@change="handleSearch"
:options="sysOriginOptions"
></SysOriginSelect>
<Select
v-model:value="query.top"
option-label-prop="label"
style="width: 140px"
@change="handleSearch"
:options="[{ label: '全部', value: '' as any }, { label: '已置顶', value: '1' as any }, { label: '未置顶', value: '0' as any }]"
/>
<DateRangePicker
v-model:value="rangeDate"
show-time
style="width: 360px"
value-format="x"
/>
<AccountInput
v-model:value="query.userId"
:sys-origin="query.sysOrigin"
placeholder="用户ID"
style="width: 220px"
/>
<Button :loading="loading" type="primary" @click="handleSearch">
搜索
</Button>
</Space>
</div>
</Card>
<Card>
<div class="selection-bar">
<Checkbox
:checked="allChecked"
:indeterminate="indeterminate"
@change="(event) => toggleAll(!!event.target.checked)"
>
全选
</Checkbox>
<span class="selection-text">
已勾选 <strong>{{ selectedKeys.length }}</strong>
</span>
<Button danger @click="handleDeleteSelected">删除</Button>
</div>
<Spin :spinning="loading">
<Empty v-if="list.length === 0" description="暂无动态" />
<Row v-else :gutter="[16, 16]">
<Col
v-for="record in list"
:key="getRowKey(record)"
:lg="8"
:md="12"
:sm="24"
:xs="24"
>
<Card class="dynamic-card">
<div class="card-header">
<Checkbox
:checked="selectedKeys.includes(getRowKey(record))"
@change="() => toggleRecord(record)"
>
选中
</Checkbox>
<Tag v-if="record.top === true" color="magenta">
置顶权重{{ record.weights || 0 }}
</Tag>
</div>
<div class="content-block">
<div class="content-label">动态内容</div>
<div class="content-text">{{ record.content || '-' }}</div>
</div>
<div v-if="getPictures(record).length > 0" class="picture-row">
<div
v-for="item in getPictures(record)"
:key="item"
class="picture-item"
>
<Image :src="item" class="picture-image" />
</div>
</div>
<div class="user-row">
<img
:src="getUserAvatar(record) || 'https://dummyimage.com/48x48/e2e8f0/64748b&text=U'"
alt=""
class="user-avatar"
>
<div class="user-meta">
<Button class="user-link" type="link" @click="openUserDetails(record)">
{{ getUserName(record) }}
</Button>
<div class="meta-line">{{ getUserAccount(record) }}</div>
<div class="meta-line">
性别{{ record.userSexName || '-' }} / 年龄{{ record.userBaseInfo?.age || '-' }}
</div>
<div class="meta-line">创建时间{{ formatDate(record.createTime) }}</div>
</div>
</div>
<div class="card-actions">
<Button size="small" type="link" @click="openAccountHandle(record)">
账号处理
</Button>
<Button size="small" type="link" @click="openTopEdit(record)">
置顶
</Button>
<Button
v-if="record.top === true"
size="small"
type="link"
@click="handleCloseTop(record)"
>
取消置顶
</Button>
</div>
</Card>
</Col>
</Row>
</Spin>
<div class="pagination">
<Pagination
:current="query.cursor"
:page-size="query.limit"
:total="total"
show-size-changer
@change="handlePageChange"
@showSizeChange="handlePageChange"
/>
</div>
</Card>
</div>
<Modal
:confirm-loading="topSaving"
:open="topModalOpen"
destroy-on-close
title="编辑置顶动态"
@cancel="topModalOpen = false"
@ok="handleSubmitTop"
>
<div class="top-form">
<div class="top-row">
<span>平台</span>
<SysOriginTag :value="query.sysOrigin" />
</div>
<div class="top-row top-input">
<span>权重</span>
<InputNumber
v-model:value="topForm.weights"
:min="0"
placeholder="数字越大越排在前面"
style="width: 100%"
/>
</div>
</div>
</Modal>
<AccountHandleModal
:open="accountHandleOpen"
:user-id="activeUserId"
@close="accountHandleOpen = false"
@success="loadData()"
/>
</Page>
</template>
<style scoped>
.page-grid {
display: grid;
gap: 16px;
}
.toolbar {
min-height: 40px;
}
.selection-bar {
align-items: center;
display: flex;
gap: 12px;
margin-bottom: 16px;
}
.selection-text {
color: #64748b;
}
.dynamic-card {
height: 100%;
}
.card-header {
align-items: center;
display: flex;
justify-content: space-between;
margin-bottom: 12px;
}
.content-block {
margin-bottom: 14px;
}
.content-label {
color: #2563eb;
font-weight: 600;
margin-bottom: 6px;
}
.content-text {
color: #334155;
line-height: 1.7;
min-height: 48px;
overflow-wrap: anywhere;
}
.picture-row {
display: flex;
gap: 8px;
margin-bottom: 14px;
overflow-x: auto;
}
.picture-item {
flex: 0 0 auto;
}
.picture-image {
border-radius: 12px;
height: 220px;
object-fit: cover;
width: 180px;
}
.user-row {
align-items: flex-start;
display: flex;
gap: 12px;
}
.user-avatar {
border-radius: 999px;
height: 48px;
object-fit: cover;
width: 48px;
}
.user-meta {
min-width: 0;
}
.user-link {
padding-left: 0;
}
.meta-line {
color: #64748b;
line-height: 1.8;
overflow-wrap: anywhere;
}
.card-actions {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 12px;
}
.pagination {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
.top-form {
display: grid;
gap: 12px;
}
.top-row {
align-items: center;
display: flex;
gap: 12px;
}
.top-input {
align-items: stretch;
flex-direction: column;
}
</style>

View File

@ -232,7 +232,6 @@ export const CURRENCY_ORIGIN_OPTIONS = [
{ value: 'USER_REFUND', name: '用户退款' }, { value: 'USER_REFUND', name: '用户退款' },
{ value: 'ACTIVITY_FRIENDSHIP_CARD_REWARDS', name: '每周特殊关系卡片奖励' }, { value: 'ACTIVITY_FRIENDSHIP_CARD_REWARDS', name: '每周特殊关系卡片奖励' },
{ value: 'WEEKLY_GAME_TASKS', name: '每周游戏任务奖励' }, { value: 'WEEKLY_GAME_TASKS', name: '每周游戏任务奖励' },
{ value: 'SEND_DYNAMIC_PAY_GOLD', name: '超限制发动态支付金币' },
{ value: 'BUY_EMOJI', name: '购买表情包' }, { value: 'BUY_EMOJI', name: '购买表情包' },
{ value: 'BET_TEEN_PATTI', name: '炸金花' }, { value: 'BET_TEEN_PATTI', name: '炸金花' },
{ value: 'TEEN_PATTI', name: '炸金花游戏 ' }, { value: 'TEEN_PATTI', name: '炸金花游戏 ' },

View File

@ -82,7 +82,7 @@ const pageActionOpen = ref(false);
const tableAreaRef = ref<HTMLDivElement | null>(null); const tableAreaRef = ref<HTMLDivElement | null>(null);
const pagerRef = ref<HTMLDivElement | null>(null); const pagerRef = ref<HTMLDivElement | null>(null);
const tableScrollY = ref(320); const tableScrollY = ref(320);
const amountFormatter = new Intl.NumberFormat('en-US', { const amountFormatter = new Intl.NumberFormat('zh-CN', {
maximumFractionDigits: 2, maximumFractionDigits: 2,
}); });
const tableScroll = computed(() => ({ x: 1000, y: tableScrollY.value })); const tableScroll = computed(() => ({ x: 1000, y: tableScrollY.value }));
@ -685,7 +685,7 @@ loadData(true);
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'cover'"> <template v-if="column.key === 'cover'">
<RewardIcon :item="record" :size="48" /> <RewardIcon :item="record" :size="42" />
</template> </template>
<template v-else-if="column.key === 'del'"> <template v-else-if="column.key === 'del'">
<Switch <Switch
@ -893,6 +893,7 @@ loadData(true);
:global(.table-action-menu__item) { :global(.table-action-menu__item) {
color: rgb(71 84 103); color: rgb(71 84 103);
height: var(--density-button-height) !important;
justify-content: flex-start; justify-content: flex-start;
padding-inline: 10px; padding-inline: 10px;
text-align: left; text-align: left;
@ -931,6 +932,7 @@ loadData(true);
.resource-table :deep(.ant-table) { .resource-table :deep(.ant-table) {
color: rgb(52 64 84); color: rgb(52 64 84);
font-size: var(--density-table-body-font-size);
} }
.resource-table { .resource-table {
@ -962,10 +964,11 @@ loadData(true);
background: #fafafa; background: #fafafa;
border-bottom-color: rgb(237 240 244); border-bottom-color: rgb(237 240 244);
color: rgb(71 84 103); color: rgb(71 84 103);
font-size: var(--font-size-base); font-size: var(--density-table-header-font-size);
font-weight: 500; font-weight: 500;
line-height: 1.35; line-height: 1.35;
padding: 10px 16px; height: var(--density-table-header-height);
padding: 8px var(--density-table-cell-padding-x);
} }
.resource-table :deep(.ant-table-thead > tr > th::before) { .resource-table :deep(.ant-table-thead > tr > th::before) {
@ -974,9 +977,9 @@ loadData(true);
.resource-table :deep(.ant-table-tbody > tr > td) { .resource-table :deep(.ant-table-tbody > tr > td) {
border-bottom-color: rgb(237 240 244); border-bottom-color: rgb(237 240 244);
height: 54px; height: var(--density-table-row-height);
line-height: 1.35; line-height: 1.35;
padding: 3px 16px; padding: 3px var(--density-table-cell-padding-x);
} }
.resource-table :deep(.ant-table-body) { .resource-table :deep(.ant-table-body) {
@ -984,13 +987,13 @@ loadData(true);
} }
.resource-table :deep(.switch-column) { .resource-table :deep(.switch-column) {
padding-left: 5px !important; padding-left: var(--density-table-fixed-cell-padding-x) !important;
padding-right: 5px !important; padding-right: var(--density-table-fixed-cell-padding-x) !important;
} }
.resource-table :deep(.action-column) { .resource-table :deep(.action-column) {
padding-left: 5px !important; padding-left: var(--density-table-fixed-cell-padding-x) !important;
padding-right: 5px !important; padding-right: var(--density-table-fixed-cell-padding-x) !important;
} }
.resource-table :deep(.ant-table-tbody > tr:hover > td) { .resource-table :deep(.ant-table-tbody > tr:hover > td) {

View File

@ -35,7 +35,7 @@ function formatPrice(value: any) {
if (!Number.isFinite(amount)) { if (!Number.isFinite(amount)) {
return String(value || '-'); return String(value || '-');
} }
return amount.toLocaleString('en-US', { return amount.toLocaleString('zh-CN', {
maximumFractionDigits: 2, maximumFractionDigits: 2,
}); });
} }
@ -123,7 +123,7 @@ onBeforeUnmount(() => {
" "
alt="" alt=""
class="latest-purchase__avatar" class="latest-purchase__avatar"
> />
<div class="latest-purchase__content"> <div class="latest-purchase__content">
<div class="latest-purchase__header"> <div class="latest-purchase__header">
<span <span

View File

@ -26,7 +26,7 @@ function normalizeNumber(value: any) {
} }
function formatValue(value: number) { function formatValue(value: number) {
return value.toLocaleString('en-US', { return value.toLocaleString('zh-CN', {
maximumFractionDigits: 2, maximumFractionDigits: 2,
}); });
} }

View File

@ -768,13 +768,13 @@ function calcPercent(numerator?: null | number | string, denominator?: null | nu
function formatInteger(value?: null | number | string) { function formatInteger(value?: null | number | string) {
const number = Number(value || 0); const number = Number(value || 0);
return Number.isFinite(number) ? number.toLocaleString('en-US', { maximumFractionDigits: 0 }) : '0'; return Number.isFinite(number) ? number.toLocaleString('zh-CN', { maximumFractionDigits: 0 }) : '0';
} }
function formatAmount(value?: null | number | string) { function formatAmount(value?: null | number | string) {
const number = Number(value || 0); const number = Number(value || 0);
return Number.isFinite(number) return Number.isFinite(number)
? number.toLocaleString('en-US', { ? number.toLocaleString('zh-CN', {
maximumFractionDigits: 2, maximumFractionDigits: 2,
minimumFractionDigits: 0, minimumFractionDigits: 0,
}) })
@ -784,7 +784,7 @@ function formatAmount(value?: null | number | string) {
function formatPercent(value?: null | number | string) { function formatPercent(value?: null | number | string) {
const number = Number(value || 0); const number = Number(value || 0);
return Number.isFinite(number) return Number.isFinite(number)
? `${number.toLocaleString('en-US', { ? `${number.toLocaleString('zh-CN', {
maximumFractionDigits: 2, maximumFractionDigits: 2,
minimumFractionDigits: 2, minimumFractionDigits: 2,
})}%` })}%`

View File

@ -27,7 +27,6 @@
} }
}, },
"dependencies": { "dependencies": {
"@intlify/unplugin-vue-i18n": "catalog:",
"@jspm/generator": "catalog:", "@jspm/generator": "catalog:",
"@tailwindcss/vite": "catalog:", "@tailwindcss/vite": "catalog:",
"@vben/node-utils": "workspace:*", "@vben/node-utils": "workspace:*",

View File

@ -33,7 +33,6 @@ function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) {
env, env,
extraAppConfig: true, extraAppConfig: true,
html: true, html: true,
i18n: true,
importmapOptions: defaultImportmapOptions, importmapOptions: defaultImportmapOptions,
injectAppLoading: true, injectAppLoading: true,
injectMetadata: true, injectMetadata: true,

View File

@ -36,7 +36,6 @@ const defaultImportmapOptions: ImportmapPluginOptions = {
{ name: 'vue' }, { name: 'vue' },
{ name: 'pinia' }, { name: 'pinia' },
{ name: 'vue-router' }, { name: 'vue-router' },
// { name: 'vue-i18n' },
{ name: 'dayjs' }, { name: 'dayjs' },
{ name: 'vue-demi' }, { name: 'vue-demi' },
], ],

View File

@ -7,7 +7,6 @@ import type {
LibraryPluginOptions, LibraryPluginOptions,
} from '../typing'; } from '../typing';
import viteVueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
import tailwindcss from '@tailwindcss/vite'; import tailwindcss from '@tailwindcss/vite';
import viteVue from '@vitejs/plugin-vue'; import viteVue from '@vitejs/plugin-vue';
import viteVueJsx from '@vitejs/plugin-vue-jsx'; import viteVueJsx from '@vitejs/plugin-vue-jsx';
@ -104,7 +103,6 @@ async function loadApplicationPlugins(
compressTypes, compressTypes,
extraAppConfig, extraAppConfig,
html, html,
i18n,
importmap, importmap,
importmapOptions, importmapOptions,
injectAppLoading, injectAppLoading,
@ -121,18 +119,6 @@ async function loadApplicationPlugins(
return await loadConditionPlugins([ return await loadConditionPlugins([
...commonPlugins, ...commonPlugins,
{
condition: i18n,
plugins: async () => {
return [
viteVueI18nPlugin({
compositionOnly: true,
fullInstall: true,
runtimeOnly: true,
}),
];
},
},
{ {
condition: print, condition: print,
plugins: async () => { plugins: async () => {

View File

@ -194,11 +194,6 @@ interface ApplicationPluginOptions extends CommonPluginOptions {
* @default true * @default true
*/ */
html?: boolean; html?: boolean;
/**
*
* @default false
*/
i18n?: boolean;
/** /**
* ImportMap CDN * ImportMap CDN
* @default false * @default false

View File

@ -93,7 +93,24 @@
/* 基本文字大小 */ /* 基本文字大小 */
--font-size-base: 14px; --font-size-base: 14px;
--menu-font-size: calc(var(--font-size-base) * 0.875); --menu-font-size: var(--font-size-base);
/* 后台紧凑密度 */
--density-button-height: 30px;
--density-button-sm-height: 24px;
--density-menu-font-size: 14px;
--density-menu-icon-size: 16px;
--density-menu-indent: 12px;
--density-menu-item-height: 40px;
--density-sub-menu-item-height: 36px;
--density-pagination-height: 28px;
--density-resource-image-size: 42px;
--density-table-body-font-size: 13px;
--density-table-cell-padding-x: 12px;
--density-table-fixed-cell-padding-x: 6px;
--density-table-header-font-size: 13px;
--density-table-header-height: 42px;
--density-table-row-height: 54px;
/* =============component & UI============= */ /* =============component & UI============= */

View File

@ -1,3 +1,3 @@
# Simple i18n # Simple Messages
Simple i18 implementation Static Chinese messages for shared UI defaults.

View File

@ -1,27 +1,17 @@
import type { Locale } from './messages'; import { computed } from 'vue';
import { computed, ref } from 'vue';
import { createSharedComposable } from '@vueuse/core'; import { createSharedComposable } from '@vueuse/core';
import { getMessages } from './messages'; import { getMessages } from './messages';
export const useSimpleLocale = createSharedComposable(() => { export const useSimpleLocale = createSharedComposable(() => {
const currentLocale = ref<Locale>('zh-CN');
const setSimpleLocale = (locale: Locale) => {
currentLocale.value = locale;
};
const $t = computed(() => { const $t = computed(() => {
const localeMessages = getMessages(currentLocale.value); const localeMessages = getMessages();
return (key: string) => { return (key: string) => {
return localeMessages[key] || key; return localeMessages[key] || key;
}; };
}); });
return { return {
$t, $t,
currentLocale,
setSimpleLocale,
}; };
}); });

View File

@ -1,24 +1,11 @@
export type Locale = 'en-US' | 'zh-CN'; export const messages: Record<string, string> = {
cancel: '取消',
export const messages: Record<Locale, Record<string, string>> = { collapse: '收起',
'en-US': { confirm: '确认',
cancel: 'Cancel', expand: '展开',
collapse: 'Collapse', prompt: '提示',
confirm: 'Confirm', reset: '重置',
expand: 'Expand', submit: '提交',
prompt: 'Prompt',
reset: 'Reset',
submit: 'Submit',
},
'zh-CN': {
cancel: '取消',
collapse: '收起',
confirm: '确认',
expand: '展开',
prompt: '提示',
reset: '重置',
submit: '提交',
},
}; };
export const getMessages = (locale: Locale) => messages[locale]; export const getMessages = () => messages;

View File

@ -26,7 +26,6 @@ exports[`defaultPreferences immutability test > should not modify the config obj
"enableStickyPreferencesNavigationBar": true, "enableStickyPreferencesNavigationBar": true,
"isMobile": false, "isMobile": false,
"layout": "sidebar-nav", "layout": "sidebar-nav",
"locale": "zh-CN",
"loginExpiredMode": "page", "loginExpiredMode": "page",
"name": "Vben Admin", "name": "Vben Admin",
"preferencesButtonPosition": "auto", "preferencesButtonPosition": "auto",
@ -117,7 +116,6 @@ exports[`defaultPreferences immutability test > should not modify the config obj
"widget": { "widget": {
"fullscreen": true, "fullscreen": true,
"globalSearch": true, "globalSearch": true,
"languageToggle": true,
"lockScreen": true, "lockScreen": true,
"refresh": true, "refresh": true,
"sidebarToggle": true, "sidebarToggle": true,

View File

@ -32,7 +32,7 @@ describe('preferences', () => {
it('initializes preferences with overrides', async () => { it('initializes preferences with overrides', async () => {
const overrides: any = { const overrides: any = {
app: { app: {
locale: 'en-US', name: '测试后台',
}, },
}; };
await preferenceManager.initPreferences({ await preferenceManager.initPreferences({
@ -110,14 +110,6 @@ describe('preferences', () => {
expect(preferenceManager.getPreferences().app.isMobile).toBe(true); expect(preferenceManager.getPreferences().app.isMobile).toBe(true);
}); });
it('updates the locale preference correctly', () => {
preferenceManager.updatePreferences({
app: { locale: 'en-US' },
});
expect(preferenceManager.getPreferences().app.locale).toBe('en-US');
});
it('updates the sidebar width correctly', () => { it('updates the sidebar width correctly', () => {
preferenceManager.updatePreferences({ preferenceManager.updatePreferences({
sidebar: { width: 200 }, sidebar: { width: 200 },
@ -145,7 +137,7 @@ describe('preferences', () => {
it('resets preferences to default correctly', () => { it('resets preferences to default correctly', () => {
// 先更新一些偏好设置 // 先更新一些偏好设置
preferenceManager.updatePreferences({ preferenceManager.updatePreferences({
app: { locale: 'en-US' }, app: { name: '测试后台' },
sidebar: { collapsed: true, width: 200 }, sidebar: { collapsed: true, width: 200 },
theme: { theme: {
radius: '0.75', radius: '0.75',
@ -169,15 +161,13 @@ describe('preferences', () => {
}); });
it('reverts to default when a preference field is deleted', () => { it('reverts to default when a preference field is deleted', () => {
preferenceManager.updatePreferences({ const originalName = preferenceManager.getPreferences().app.name;
app: { locale: 'en-US' },
});
preferenceManager.updatePreferences({ preferenceManager.updatePreferences({
app: { locale: undefined }, app: { name: undefined },
}); });
expect(preferenceManager.getPreferences().app.locale).toBe('en-US'); expect(preferenceManager.getPreferences().app.name).toBe(originalName);
}); });
it('ignores updates with invalid preference value types', () => { it('ignores updates with invalid preference value types', () => {
@ -209,7 +199,7 @@ describe('preferences', () => {
it('applies updates immediately after initialization', async () => { it('applies updates immediately after initialization', async () => {
const overrides: any = { const overrides: any = {
app: { app: {
locale: 'en-US', name: '测试后台',
}, },
}; };

View File

@ -26,7 +26,6 @@ const defaultPreferences: Preferences = {
enableStickyPreferencesNavigationBar: true, enableStickyPreferencesNavigationBar: true,
isMobile: false, isMobile: false,
layout: 'sidebar-nav', layout: 'sidebar-nav',
locale: 'zh-CN',
loginExpiredMode: 'page', loginExpiredMode: 'page',
name: 'Vben Admin', name: 'Vben Admin',
preferencesButtonPosition: 'auto', preferencesButtonPosition: 'auto',
@ -118,7 +117,6 @@ const defaultPreferences: Preferences = {
widget: { widget: {
fullscreen: true, fullscreen: true,
globalSearch: true, globalSearch: true,
languageToggle: true,
lockScreen: true, lockScreen: true,
refresh: true, refresh: true,
sidebarToggle: true, sidebarToggle: true,

View File

@ -18,7 +18,6 @@ import { updateCSSVariables } from './update-css-variables';
const STORAGE_KEYS = { const STORAGE_KEYS = {
MAIN: 'preferences', MAIN: 'preferences',
LOCALE: 'preferences-locale',
} as const; } as const;
function pickKnownPreferences(preferences: DeepPartial<Preferences>) { function pickKnownPreferences(preferences: DeepPartial<Preferences>) {
@ -203,7 +202,6 @@ class PreferenceManager {
*/ */
private saveToCache(preference: Preferences) { private saveToCache(preference: Preferences) {
this.cache.setItem(STORAGE_KEYS.MAIN, preference); this.cache.setItem(STORAGE_KEYS.MAIN, preference);
this.cache.setItem(STORAGE_KEYS.LOCALE, preference.app.locale);
} }
/** /**

View File

@ -13,8 +13,6 @@ import type {
TabsStyleType, TabsStyleType,
} from '@vben-core/typings'; } from '@vben-core/typings';
type SupportedLanguagesType = 'en-US' | 'zh-CN';
interface AppPreferences { interface AppPreferences {
/** 权限模式 */ /** 权限模式 */
accessMode: AccessModeType; accessMode: AccessModeType;
@ -66,8 +64,6 @@ interface AppPreferences {
isMobile: boolean; isMobile: boolean;
/** 布局方式 */ /** 布局方式 */
layout: LayoutType; layout: LayoutType;
/** 支持的语言 */
locale: SupportedLanguagesType;
/** 登录过期模式 */ /** 登录过期模式 */
loginExpiredMode: LoginExpiredModeType; loginExpiredMode: LoginExpiredModeType;
/** 应用名 */ /** 应用名 */
@ -241,8 +237,6 @@ interface WidgetPreferences {
fullscreen: boolean; fullscreen: boolean;
/** 是否启用全局搜索部件 */ /** 是否启用全局搜索部件 */
globalSearch: boolean; globalSearch: boolean;
/** 是否启用语言切换部件 */
languageToggle: boolean;
/** 是否开启锁屏功能 */ /** 是否开启锁屏功能 */
lockScreen: boolean; lockScreen: boolean;
/** 显示刷新按钮 */ /** 显示刷新按钮 */
@ -297,7 +291,6 @@ export type {
PreferencesKeys, PreferencesKeys,
ShortcutKeyPreferences, ShortcutKeyPreferences,
SidebarPreferences, SidebarPreferences,
SupportedLanguagesType,
TabbarPreferences, TabbarPreferences,
ThemePreferences, ThemePreferences,
TransitionPreferences, TransitionPreferences,

View File

@ -29,7 +29,7 @@ function updateCSSVariables(preferences: Preferences) {
); );
document.documentElement.style.setProperty( document.documentElement.style.setProperty(
'--menu-font-size', '--menu-font-size',
`calc(${fontSize}px * 0.875)`, `${fontSize}px`,
); );
} }
} }

View File

@ -22,10 +22,6 @@ function usePreferences() {
return false; return false;
}); });
const locale = computed(() => {
return preferences.app.locale;
});
const isMobile = computed(() => { const isMobile = computed(() => {
return appPreferences.value.isMobile; return appPreferences.value.isMobile;
}); });
@ -238,7 +234,6 @@ function usePreferences() {
isSideNav, isSideNav,
keepAlive, keepAlive,
layout, layout,
locale,
preferencesButtonPosition, preferencesButtonPosition,
sidebarCollapsed, sidebarCollapsed,
theme, theme,

View File

@ -433,7 +433,7 @@ $namespace: vben;
max-width: var(--menu-title-width); max-width: var(--menu-title-width);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
font-size: var(--menu-font-size) !important; font-size: var(--density-menu-font-size, var(--menu-font-size)) !important;
white-space: nowrap; white-space: nowrap;
opacity: 1; opacity: 1;
} }
@ -445,20 +445,21 @@ $namespace: vben;
.#{$namespace}-menu__popup-container, .#{$namespace}-menu__popup-container,
.#{$namespace}-menu { .#{$namespace}-menu {
--menu-title-width: 140px; --menu-title-width: 140px;
--menu-item-icon-size: var(--font-size-base, 16px); --menu-item-icon-size: var(--density-menu-icon-size, 16px);
--menu-item-height: 38px; --menu-item-height: var(--density-menu-item-height, 40px);
--menu-item-padding-y: 21px; --menu-sub-item-height: var(--density-sub-menu-item-height, 36px);
--menu-item-padding-x: 12px; --menu-item-padding-y: 0px;
--menu-item-popup-padding-y: 20px; --menu-item-padding-x: 10px;
--menu-item-popup-padding-y: 12px;
--menu-item-popup-padding-x: 12px; --menu-item-popup-padding-x: 12px;
--menu-item-margin-y: 2px; --menu-item-margin-y: 2px;
--menu-item-margin-x: 0px; --menu-item-margin-x: 0px;
--menu-item-collapse-padding-y: 23.5px; --menu-item-collapse-padding-y: 18px;
--menu-item-collapse-padding-x: 0px; --menu-item-collapse-padding-x: 0px;
--menu-item-collapse-margin-y: 4px; --menu-item-collapse-margin-y: 2px;
--menu-item-collapse-margin-x: 0px; --menu-item-collapse-margin-x: 0px;
--menu-item-radius: 0px; --menu-item-radius: 0px;
--menu-item-indent: 16px; --menu-item-indent: var(--density-menu-indent, 12px);
&.is-light { &.is-light {
--menu-background-color: hsl(var(--menu)); --menu-background-color: hsl(var(--menu));
@ -529,6 +530,10 @@ $namespace: vben;
// //
&.is-vertical { &.is-vertical {
&:not(.#{$namespace}-menu.is-collapse) { &:not(.#{$namespace}-menu.is-collapse) {
& .#{$namespace}-sub-menu > .#{$namespace}-menu {
--menu-item-height: var(--menu-sub-item-height);
}
& .#{$namespace}-menu-item, & .#{$namespace}-menu-item,
& .#{$namespace}-sub-menu-content, & .#{$namespace}-sub-menu-content,
& .#{$namespace}-menu-item-group__title { & .#{$namespace}-menu-item-group__title {
@ -680,7 +685,7 @@ $namespace: vben;
} }
&__popup { &__popup {
padding: 10px 0; padding: 6px 0;
border-radius: var(--menu-item-radius); border-radius: var(--menu-item-radius);
.#{$namespace}-sub-menu-content, .#{$namespace}-sub-menu-content,
@ -762,7 +767,7 @@ $namespace: vben;
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 0 var(--menu-item-padding-x); padding: 0 var(--menu-item-padding-x);
font-size: var(--menu-font-size) !important; font-size: var(--density-menu-font-size, var(--menu-font-size)) !important;
line-height: var(--menu-item-height); line-height: var(--menu-item-height);
} }
} }
@ -789,7 +794,7 @@ $namespace: vben;
.#{$namespace}-sub-menu-content { .#{$namespace}-sub-menu-content {
height: var(--menu-item-height); height: var(--menu-item-height);
font-size: var(--menu-font-size) !important; font-size: var(--density-menu-font-size, var(--menu-font-size)) !important;
@include menu-item; @include menu-item;

View File

@ -61,9 +61,9 @@ function menuIcon(menu: MenuRecordRaw) {
@reference "@vben/tailwind-config/theme"; @reference "@vben/tailwind-config/theme";
.vben-normal-menu { .vben-normal-menu {
--menu-item-margin-y: 4px; --menu-item-margin-y: 2px;
--menu-item-margin-x: 0px; --menu-item-margin-x: 0px;
--menu-item-padding-y: 9px; --menu-item-padding-y: 6px;
--menu-item-padding-x: 0px; --menu-item-padding-x: 0px;
--menu-item-radius: 0px; --menu-item-radius: 0px;
@ -84,7 +84,7 @@ function menuIcon(menu: MenuRecordRaw) {
} }
.vben-normal-menu.is-collapse .vben-normal-menu__icon { .vben-normal-menu.is-collapse .vben-normal-menu__icon {
font-size: calc(var(--font-size-base, 16px) * 1.25); font-size: var(--density-menu-icon-size, 16px);
} }
.vben-normal-menu__item { .vben-normal-menu__item {
@ -128,7 +128,7 @@ function menuIcon(menu: MenuRecordRaw) {
.vben-normal-menu__icon { .vben-normal-menu__icon {
@apply max-h-5; @apply max-h-5;
font-size: calc(var(--font-size-base, 16px) * 1.25); font-size: var(--density-menu-icon-size, 16px);
transition: all 0.25s ease; transition: all 0.25s ease;
} }
@ -137,7 +137,7 @@ function menuIcon(menu: MenuRecordRaw) {
width: 100%; width: 100%;
margin-bottom: 0; margin-bottom: 0;
font-size: calc(var(--font-size-base, 16px) * 0.75); font-size: 12px;
font-weight: 400; font-weight: 400;
text-align: center; text-align: center;
transition: all 0.25s ease; transition: all 0.25s ease;

View File

@ -2,22 +2,3 @@
* @zh_CN url * @zh_CN url
*/ */
export const LOGIN_PATH = '/auth/login'; export const LOGIN_PATH = '/auth/login';
export interface LanguageOption {
label: string;
value: 'en-US' | 'zh-CN';
}
/**
* Supported languages
*/
export const SUPPORT_LANGUAGES: LanguageOption[] = [
{
label: '简体中文',
value: 'zh-CN',
},
{
label: 'English',
value: 'en-US',
},
];

View File

@ -24,7 +24,11 @@ export function useAntdDesignTokens() {
colorPrimary: '', colorPrimary: '',
colorSuccess: '', colorSuccess: '',
colorTextBase: '', colorTextBase: '',
controlHeight: 30,
controlHeightLG: 34,
controlHeightSM: 24,
fontSize: 14, fontSize: 14,
fontSizeSM: 13,
colorWarning: '', colorWarning: '',
zIndexPopupBase: 2000, // 调整基础弹层层级,避免下拉等组件被弹窗或者最大化状态下的表格遮挡 zIndexPopupBase: 2000, // 调整基础弹层层级,避免下拉等组件被弹窗或者最大化状态下的表格遮挡
}); });
@ -51,6 +55,16 @@ export function useAntdDesignTokens() {
tokens.fontSize = Number.parseFloat( tokens.fontSize = Number.parseFloat(
getCssVariableValue('--font-size-base', false), getCssVariableValue('--font-size-base', false),
); );
tokens.fontSizeSM = Number.parseFloat(
getCssVariableValue('--density-table-body-font-size', false),
);
tokens.controlHeight = Number.parseFloat(
getCssVariableValue('--density-button-height', false),
);
tokens.controlHeightSM = Number.parseFloat(
getCssVariableValue('--density-button-sm-height', false),
);
tokens.controlHeightLG = Math.max(tokens.controlHeight + 4, 34);
getCssVariableValue('--primary-foreground'); getCssVariableValue('--primary-foreground');

View File

@ -58,7 +58,7 @@ export function useTabs() {
* *
* *
* @description * @description
* @description , * @description ,
* *
* @param title - * @param title -
* - 静态标题: 直接传入字符串 * - 静态标题: 直接传入字符串
@ -68,8 +68,8 @@ export function useTabs() {
* // 静态标题 * // 静态标题
* setTabTitle('标签页') * setTabTitle('标签页')
* *
* // 动态标题(多语言) * // 动态标题
* setTabTitle(computed(() => t('page.title'))) * setTabTitle(computed(() => `标签页 ${count.value}`))
*/ */
async function setTabTitle(title: ComputedRef<string> | string) { async function setTabTitle(title: ComputedRef<string> | string) {
tabbarStore.setUpdateTime(); tabbarStore.setUpdateTime();

View File

@ -32,7 +32,7 @@ const props = withDefaults(defineProps<Props>(), {
pageTitle: '', pageTitle: '',
sloganImage: '', sloganImage: '',
toolbar: true, toolbar: true,
toolbarList: () => ['language', 'layout'], toolbarList: () => ['layout'],
clickLogo: () => {}, clickLogo: () => {},
}); });

View File

@ -1,12 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ToolbarType } from './types'; import type { ToolbarType } from './types';
import { preferences } from '@vben/preferences'; import { AuthenticationLayoutToggle } from '../widgets';
import {
AuthenticationLayoutToggle,
LanguageToggle,
} from '../widgets';
interface Props { interface Props {
toolbarList?: ToolbarType[]; toolbarList?: ToolbarType[];
@ -17,11 +12,10 @@ defineOptions({
}); });
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
toolbarList: () => ['language', 'layout'], toolbarList: () => ['layout'],
}); });
const showLayout = () => props.toolbarList.includes('layout'); const showLayout = () => props.toolbarList.includes('layout');
const showLanguage = () => props.toolbarList.includes('language');
</script> </script>
<template> <template>
@ -35,6 +29,5 @@ const showLanguage = () => props.toolbarList.includes('language');
<div class="hidden md:flex"> <div class="hidden md:flex">
<AuthenticationLayoutToggle v-if="showLayout()" /> <AuthenticationLayoutToggle v-if="showLayout()" />
</div> </div>
<LanguageToggle v-if="showLanguage() && preferences.widget.languageToggle" />
</div> </div>
</template> </template>

View File

@ -1 +1 @@
export type ToolbarType = 'language' | 'layout'; export type ToolbarType = 'layout';

View File

@ -10,7 +10,6 @@ import { VbenFullScreen, VbenIconButton } from '@vben-core/shadcn-ui';
import { import {
GlobalSearch, GlobalSearch,
LanguageToggle,
PreferencesButton, PreferencesButton,
TimezoneButton, TimezoneButton,
} from '../../widgets'; } from '../../widgets';
@ -54,12 +53,6 @@ const rightSlots = computed(() => {
name: 'preferences', name: 'preferences',
}); });
} }
if (preferences.widget.languageToggle) {
list.push({
index: REFERENCE_VALUE + 30,
name: 'language-toggle',
});
}
if (preferences.widget.timezone) { if (preferences.widget.timezone) {
list.push({ list.push({
index: REFERENCE_VALUE + 40, index: REFERENCE_VALUE + 40,
@ -148,9 +141,6 @@ function clearPreferencesAndLogout() {
@clear-preferences-and-logout="clearPreferencesAndLogout" @clear-preferences-and-logout="clearPreferencesAndLogout"
/> />
</template> </template>
<template v-else-if="slot.name === 'language-toggle'">
<LanguageToggle class="mr-1" />
</template>
<template v-else-if="slot.name === 'fullscreen'"> <template v-else-if="slot.name === 'fullscreen'">
<VbenFullScreen class="mr-1" /> <VbenFullScreen class="mr-1" />
</template> </template>

View File

@ -8,7 +8,7 @@ import { computed, onMounted, useSlots, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useRefresh } from '@vben/hooks'; import { useRefresh } from '@vben/hooks';
import { $t, i18n } from '@vben/locales'; import { $t } from '@vben/locales';
import { import {
preferences, preferences,
updatePreferences, updatePreferences,
@ -200,10 +200,6 @@ function refreshAll() {
refresh(); refresh();
} }
//
// i18n.global.localepreference.app.localewatchpreference.app.locale
watch(i18n.global.locale, refreshAll, { flush: 'post' });
// //
watch(() => timezoneStore.timezone, refreshAll, { flush: 'post' }); watch(() => timezoneStore.timezone, refreshAll, { flush: 'post' });

View File

@ -21,7 +21,7 @@ import {
RotateCw, RotateCw,
X, X,
} from '@vben/icons'; } from '@vben/icons';
import { $t, useI18n } from '@vben/locales'; import { $t } from '@vben/locales';
import { getTabKey, useAccessStore, useTabbarStore } from '@vben/stores'; import { getTabKey, useAccessStore, useTabbarStore } from '@vben/stores';
import { filterTree } from '@vben/utils'; import { filterTree } from '@vben/utils';
@ -51,13 +51,11 @@ export function useTabbar() {
return getTabKey(route); return getTabKey(route);
}); });
const { locale } = useI18n();
const currentTabs = ref<RouteLocationNormalizedGeneric[]>(); const currentTabs = ref<RouteLocationNormalizedGeneric[]>();
watch( watch(
[ [
() => tabbarStore.getTabs, () => tabbarStore.getTabs,
() => tabbarStore.updateTime, () => tabbarStore.updateTime,
() => locale.value,
], ],
([tabs]) => { ([tabs]) => {
currentTabs.value = tabs.map((item) => wrapperTabLocale(item)); currentTabs.value = tabs.map((item) => wrapperTabLocale(item));

View File

@ -1,6 +1,5 @@
export * from './check-updates'; export * from './check-updates';
export * from './global-search'; export * from './global-search';
export { default as LanguageToggle } from './language-toggle.vue';
export { default as AuthenticationLayoutToggle } from './layout-toggle.vue'; export { default as AuthenticationLayoutToggle } from './layout-toggle.vue';
export * from './lock-screen'; export * from './lock-screen';
export * from './preferences'; export * from './preferences';

View File

@ -1,39 +0,0 @@
<script setup lang="ts">
import type { SupportedLanguagesType } from '@vben/locales';
import { SUPPORT_LANGUAGES } from '@vben/constants';
import { Languages } from '@vben/icons';
import { loadLocaleMessages } from '@vben/locales';
import { preferences, updatePreferences } from '@vben/preferences';
import { VbenDropdownRadioMenu, VbenIconButton } from '@vben-core/shadcn-ui';
defineOptions({
name: 'LanguageToggle',
});
async function handleUpdate(value: string | undefined) {
if (!value) return;
const locale = value as SupportedLanguagesType;
updatePreferences({
app: {
locale,
},
});
await loadLocaleMessages(locale);
}
</script>
<template>
<div>
<VbenDropdownRadioMenu
:menus="SUPPORT_LANGUAGES"
:model-value="preferences.app.locale"
@update:model-value="handleUpdate"
>
<VbenIconButton class="hover:animate-[shrink_0.3s_ease-in-out]">
<Languages class="size-4 text-foreground" />
</VbenIconButton>
</VbenDropdownRadioMenu>
</div>
</template>

View File

@ -2,7 +2,7 @@
import { computed, reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
import { LockKeyhole } from '@vben/icons'; import { LockKeyhole } from '@vben/icons';
import { $t, useI18n } from '@vben/locales'; import { $t } from '@vben/locales';
import { storeToRefs, useAccessStore } from '@vben/stores'; import { storeToRefs, useAccessStore } from '@vben/stores';
import { useScrollLock } from '@vben-core/composables'; import { useScrollLock } from '@vben-core/composables';
@ -25,14 +25,13 @@ withDefaults(defineProps<Props>(), {
defineEmits<{ toLogin: [] }>(); defineEmits<{ toLogin: [] }>();
const { locale } = useI18n();
const accessStore = useAccessStore(); const accessStore = useAccessStore();
const now = useNow(); const now = useNow();
const meridiem = useDateFormat(now, 'A'); const meridiem = useDateFormat(now, 'A');
const hour = useDateFormat(now, 'HH'); const hour = useDateFormat(now, 'HH');
const minute = useDateFormat(now, 'mm'); const minute = useDateFormat(now, 'mm');
const date = useDateFormat(now, 'YYYY-MM-DD dddd', { locales: locale.value }); const date = useDateFormat(now, 'YYYY-MM-DD dddd', { locales: 'zh-CN' });
const showUnlockForm = ref(false); const showUnlockForm = ref(false);
const { lockScreenPassword } = storeToRefs(accessStore); const { lockScreenPassword } = storeToRefs(accessStore);

View File

@ -1,16 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { SUPPORT_LANGUAGES } from '@vben/constants';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import InputItem from '../input-item.vue'; import InputItem from '../input-item.vue';
import SelectItem from '../select-item.vue';
import SwitchItem from '../switch-item.vue'; import SwitchItem from '../switch-item.vue';
defineOptions({ defineOptions({
name: 'PreferenceGeneralConfig', name: 'PreferenceGeneralConfig',
}); });
const appLocale = defineModel<string>('appLocale');
const appDynamicTitle = defineModel<boolean>('appDynamicTitle'); const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
const appWatermark = defineModel<boolean>('appWatermark'); const appWatermark = defineModel<boolean>('appWatermark');
const appWatermarkContent = defineModel<string>('appWatermarkContent'); const appWatermarkContent = defineModel<string>('appWatermarkContent');
@ -21,9 +18,6 @@ const appEnableCopyPreferences = defineModel<boolean>(
</script> </script>
<template> <template>
<SelectItem v-model="appLocale" :items="SUPPORT_LANGUAGES">
{{ $t('preferences.language') }}
</SelectItem>
<SwitchItem v-model="appDynamicTitle"> <SwitchItem v-model="appDynamicTitle">
{{ $t('preferences.dynamicTitle') }} {{ $t('preferences.dynamicTitle') }}
</SwitchItem> </SwitchItem>

View File

@ -14,7 +14,6 @@ defineOptions({
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch'); const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch');
const widgetFullscreen = defineModel<boolean>('widgetFullscreen'); const widgetFullscreen = defineModel<boolean>('widgetFullscreen');
const widgetLanguageToggle = defineModel<boolean>('widgetLanguageToggle');
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle'); const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
const widgetLockScreen = defineModel<boolean>('widgetLockScreen'); const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
const appPreferencesButtonPosition = defineModel<string>( const appPreferencesButtonPosition = defineModel<string>(
@ -42,9 +41,6 @@ const positionItems = computed((): SelectOption[] => [
<SwitchItem v-model="widgetGlobalSearch"> <SwitchItem v-model="widgetGlobalSearch">
{{ $t('preferences.widget.globalSearch') }} {{ $t('preferences.widget.globalSearch') }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="widgetLanguageToggle">
{{ $t('preferences.widget.languageToggle') }}
</SwitchItem>
<SwitchItem v-model="widgetFullscreen"> <SwitchItem v-model="widgetFullscreen">
{{ $t('preferences.widget.fullscreen') }} {{ $t('preferences.widget.fullscreen') }}
</SwitchItem> </SwitchItem>

View File

@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SupportedLanguagesType } from '@vben/locales';
import type { import type {
ContentCompactType, ContentCompactType,
LayoutHeaderMenuAlignType, LayoutHeaderMenuAlignType,
@ -14,10 +13,9 @@ import type { SegmentedItem } from '@vben-core/shadcn-ui';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { Copy, Pin, PinOff, RotateCw } from '@vben/icons'; import { Copy, Pin, PinOff, RotateCw } from '@vben/icons';
import { $t, loadLocaleMessages } from '@vben/locales'; import { $t } from '@vben/locales';
import { import {
clearCache, clearCache,
preferences,
resetPreferences, resetPreferences,
usePreferences, usePreferences,
} from '@vben/preferences'; } from '@vben/preferences';
@ -55,7 +53,6 @@ const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
const message = globalShareState.getMessage(); const message = globalShareState.getMessage();
const appLocale = defineModel<SupportedLanguagesType>('appLocale');
const appDynamicTitle = defineModel<boolean>('appDynamicTitle'); const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
const appLayout = defineModel<LayoutType>('appLayout'); const appLayout = defineModel<LayoutType>('appLayout');
const appColorGrayMode = defineModel<boolean>('appColorGrayMode'); const appColorGrayMode = defineModel<boolean>('appColorGrayMode');
@ -149,7 +146,6 @@ const shortcutKeysGlobalLockScreen = defineModel<boolean>(
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch'); const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch');
const widgetFullscreen = defineModel<boolean>('widgetFullscreen'); const widgetFullscreen = defineModel<boolean>('widgetFullscreen');
const widgetLanguageToggle = defineModel<boolean>('widgetLanguageToggle');
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle'); const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
const widgetLockScreen = defineModel<boolean>('widgetLockScreen'); const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
const widgetRefresh = defineModel<boolean>('widgetRefresh'); const widgetRefresh = defineModel<boolean>('widgetRefresh');
@ -202,12 +198,11 @@ async function handleClearCache() {
emit('clearPreferencesAndLogout'); emit('clearPreferencesAndLogout');
} }
async function handleReset() { function handleReset() {
if (!diffPreference.value) { if (!diffPreference.value) {
return; return;
} }
resetPreferences(); resetPreferences();
await loadLocaleMessages(preferences.app.locale);
} }
</script> </script>
@ -268,7 +263,6 @@ async function handleReset() {
v-model:app-dynamic-title="appDynamicTitle" v-model:app-dynamic-title="appDynamicTitle"
v-model:app-enable-check-updates="appEnableCheckUpdates" v-model:app-enable-check-updates="appEnableCheckUpdates"
v-model:app-enable-copy-preferences="appEnableCopyPreferences" v-model:app-enable-copy-preferences="appEnableCopyPreferences"
v-model:app-locale="appLocale"
v-model:app-watermark="appWatermark" v-model:app-watermark="appWatermark"
v-model:app-watermark-content="appWatermarkContent" v-model:app-watermark-content="appWatermarkContent"
/> />
@ -362,7 +356,6 @@ async function handleReset() {
" "
v-model:widget-fullscreen="widgetFullscreen" v-model:widget-fullscreen="widgetFullscreen"
v-model:widget-global-search="widgetGlobalSearch" v-model:widget-global-search="widgetGlobalSearch"
v-model:widget-language-toggle="widgetLanguageToggle"
v-model:widget-lock-screen="widgetLockScreen" v-model:widget-lock-screen="widgetLockScreen"
v-model:widget-refresh="widgetRefresh" v-model:widget-refresh="widgetRefresh"
v-model:widget-sidebar-toggle="widgetSidebarToggle" v-model:widget-sidebar-toggle="widgetSidebarToggle"

View File

@ -2,7 +2,7 @@
import { computed } from 'vue'; import { computed } from 'vue';
import { Settings } from '@vben/icons'; import { Settings } from '@vben/icons';
import { $t, loadLocaleMessages } from '@vben/locales'; import { $t } from '@vben/locales';
import { preferences, updatePreferences } from '@vben/preferences'; import { preferences, updatePreferences } from '@vben/preferences';
import { capitalizeFirstLetter } from '@vben/utils'; import { capitalizeFirstLetter } from '@vben/utils';
@ -42,9 +42,6 @@ const listen = computed(() => {
val: any, val: any,
) => { ) => {
updatePreferences({ [key]: { [subKey]: val } }); updatePreferences({ [key]: { [subKey]: val } });
if (key === 'app' && subKey === 'locale') {
loadLocaleMessages(val);
}
}; };
} }
} else { } else {

View File

@ -19,7 +19,6 @@ import {
VxeUI, VxeUI,
VxeUpload, VxeUpload,
} from 'vxe-pc-ui'; } from 'vxe-pc-ui';
import enUS from 'vxe-pc-ui/lib/language/en-US'; // 导入默认的语言
import zhCN from 'vxe-pc-ui/lib/language/zh-CN'; import zhCN from 'vxe-pc-ui/lib/language/zh-CN';
import { import {
VxeColgroup, VxeColgroup,
@ -117,19 +116,15 @@ export function setupVbenVxeTable(setupOptions: SetupVxeTable) {
if (useVbenFormFromParam) { if (useVbenFormFromParam) {
tableFormFactory = useVbenFormFromParam; tableFormFactory = useVbenFormFromParam;
} }
const { isDark, locale } = usePreferences(); const { isDark } = usePreferences();
const zhCNLocale = normalizeVxeLocale(zhCN);
const localMap = {
'zh-CN': normalizeVxeLocale(zhCN),
'en-US': normalizeVxeLocale(enUS),
};
watch( watch(
[() => isDark.value, () => locale.value], () => isDark.value,
([isDarkValue, localeValue]) => { (isDarkValue) => {
VxeUI.setTheme(isDarkValue ? 'dark' : 'light'); VxeUI.setTheme(isDarkValue ? 'dark' : 'light');
VxeUI.setI18n(localeValue, localMap[localeValue]); VxeUI.setI18n('zh-CN', zhCNLocale);
VxeUI.setLanguage(localeValue); VxeUI.setLanguage('zh-CN');
}, },
{ {
immediate: true, immediate: true,

View File

@ -20,9 +20,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@intlify/core-base": "catalog:",
"@vben-core/composables": "workspace:*", "@vben-core/composables": "workspace:*",
"vue": "catalog:", "vue": "catalog:"
"vue-i18n": "catalog:"
} }
} }

View File

@ -1,147 +0,0 @@
import type { App } from 'vue';
import type { Locale } from 'vue-i18n';
import type {
ImportLocaleFn,
LoadMessageFn,
LocaleSetupOptions,
SupportedLanguagesType,
} from './typing';
import { unref } from 'vue';
import { createI18n } from 'vue-i18n';
import { useSimpleLocale } from '@vben-core/composables';
const i18n = createI18n({
globalInjection: true,
legacy: false,
locale: '',
messages: {},
});
const modules = import.meta.glob('./langs/**/*.json');
const { setSimpleLocale } = useSimpleLocale();
const localesMap = loadLocalesMapFromDir(
/\.\/langs\/([^/]+)\/(.*)\.json$/,
modules,
);
let loadMessages: LoadMessageFn;
/**
* Load locale modules
* @param modules
*/
function loadLocalesMap(modules: Record<string, () => Promise<unknown>>) {
const localesMap: Record<Locale, ImportLocaleFn> = {};
for (const [path, loadLocale] of Object.entries(modules)) {
const key = path.match(/([\w-]*)\.(json)/)?.[1];
if (key) {
localesMap[key] = loadLocale as ImportLocaleFn;
}
}
return localesMap;
}
/**
* Load locale modules with directory structure
* @param regexp - Regular expression to match language and file names
* @param modules - The modules object containing paths and import functions
* @returns A map of locales to their corresponding import functions
*/
function loadLocalesMapFromDir(
regexp: RegExp,
modules: Record<string, () => Promise<unknown>>,
): Record<Locale, ImportLocaleFn> {
const localesRaw: Record<Locale, Record<string, () => Promise<unknown>>> = {};
const localesMap: Record<Locale, ImportLocaleFn> = {};
// Iterate over the modules to extract language and file names
for (const path in modules) {
const match = path.match(regexp);
if (match) {
const [_, locale, fileName] = match;
if (locale && fileName) {
if (!localesRaw[locale]) {
localesRaw[locale] = {};
}
if (modules[path]) {
localesRaw[locale][fileName] = modules[path];
}
}
}
}
// Convert raw locale data into async import functions
for (const [locale, files] of Object.entries(localesRaw)) {
localesMap[locale] = async () => {
const messages: Record<string, any> = {};
for (const [fileName, importFn] of Object.entries(files)) {
messages[fileName] = ((await importFn()) as any)?.default;
}
return { default: messages };
};
}
return localesMap;
}
/**
* Set i18n language
* @param locale
*/
function setI18nLanguage(locale: Locale) {
i18n.global.locale.value = locale;
document?.querySelector('html')?.setAttribute('lang', locale);
}
async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
const { defaultLocale = 'zh-CN' } = options;
// app可以自行扩展一些第三方库和组件库的国际化
loadMessages = options.loadMessages || (async () => ({}));
app.use(i18n);
await loadLocaleMessages(defaultLocale);
// 在控制台打印警告
i18n.global.setMissingHandler((locale, key) => {
if (options.missingWarn && key.includes('.')) {
console.warn(
`[intlify] Not found '${key}' key in '${locale}' locale messages.`,
);
}
});
}
/**
* Load locale messages
* @param lang
*/
async function loadLocaleMessages(lang: SupportedLanguagesType) {
if (unref(i18n.global.locale) === lang) {
return setI18nLanguage(lang);
}
setSimpleLocale(lang);
const message = await localesMap[lang]?.();
if (message?.default) {
i18n.global.setLocaleMessage(lang, message.default);
}
const mergeMessage = await loadMessages(lang);
i18n.global.mergeLocaleMessage(lang, mergeMessage);
return setI18nLanguage(lang);
}
export {
i18n,
loadLocaleMessages,
loadLocalesMap,
loadLocalesMapFromDir,
setupI18n,
};

View File

@ -1,30 +1,8 @@
import {
i18n,
loadLocaleMessages,
loadLocalesMap,
loadLocalesMapFromDir,
setupI18n,
} from './i18n';
const $t = i18n.global.t;
const $te = i18n.global.te;
export { export {
$t, $t,
$te, $te,
i18n, mergeLocaleMessages,
loadLocaleMessages, setupLocale,
loadLocalesMap, } from './locale';
loadLocalesMapFromDir,
setupI18n,
};
export {
type ImportLocaleFn,
type LocaleSetupOptions,
type SupportedLanguagesType,
} from './typing';
export type { CompileError } from '@intlify/core-base';
export { useI18n } from 'vue-i18n'; export type { LocaleSetupOptions } from './typing';
export type { Locale } from 'vue-i18n';

View File

@ -1,65 +0,0 @@
{
"welcomeBack": "Welcome Back",
"pageTitle": "Plug-and-play Admin system",
"pageDesc": "Efficient, versatile frontend template",
"loginSuccess": "Login Successful",
"loginSuccessDesc": "Welcome Back",
"loginSubtitle": "Enter your account details to manage your projects",
"selectAccount": "Quick Select Account",
"username": "Username",
"password": "Password",
"usernameTip": "Please enter username",
"passwordErrorTip": "Password is incorrect",
"passwordTip": "Please enter password",
"verifyRequiredTip": "Please complete the verification first",
"rememberMe": "Remember Me",
"createAnAccount": "Create an Account",
"createAccount": "Create Account",
"alreadyHaveAccount": "Already have an account?",
"accountTip": "Don't have an account?",
"signUp": "Sign Up",
"signUpSubtitle": "Make managing your applications simple and fun",
"confirmPassword": "Confirm Password",
"confirmPasswordTip": "The passwords do not match",
"agree": "I agree to",
"privacyPolicy": "Privacy-policy",
"terms": "Terms",
"agreeTip": "Please agree to the Privacy Policy and Terms",
"goToLogin": "Login instead",
"passwordStrength": "Use 8 or more characters with a mix of letters, numbers & symbols",
"forgetPassword": "Forget Password?",
"forgetPasswordSubtitle": "Enter your email and we'll send you instructions to reset your password",
"emailTip": "Please enter email",
"emailValidErrorTip": "The email format you entered is incorrect",
"sendResetLink": "Send Reset Link",
"email": "Email",
"qrcodeSubtitle": "Scan the QR code with your phone to login",
"qrcodePrompt": "Click 'Confirm' after scanning to complete login",
"qrcodeLogin": "QR Code Login",
"wechatLogin": "Wechat Login",
"qqLogin": "QQ Login",
"githubLogin": "Github Login",
"googleLogin": "Google Login",
"dingdingLogin": "Dingding Login",
"codeSubtitle": "Enter your phone number to start managing your project",
"code": "Security code",
"codeTip": "Security code required {0} characters",
"mobile": "Mobile",
"mobileLogin": "Mobile Login",
"mobileTip": "Please enter mobile number",
"mobileErrortip": "The phone number format is incorrect",
"sendCode": "Get Security code",
"sendText": "Resend in {0}s",
"thirdPartyLogin": "Or continue with",
"weChat": "WeChat",
"qq": "QQ",
"gitHub": "GitHub",
"google": "Google",
"loginAgainTitle": "Please Log In Again",
"loginAgainSubTitle": "Your login session has expired. Please log in again to continue.",
"layout": {
"center": "Align Center",
"alignLeft": "Align Left",
"alignRight": "Align Right"
}
}

View File

@ -1,24 +0,0 @@
{
"back": "Back",
"backToHome": "Back To Home",
"login": "Login",
"logout": "Logout",
"prompt": "Prompt",
"cancel": "Cancel",
"confirm": "Confirm",
"reset": "Reset",
"noData": "No Data",
"refresh": "Refresh",
"loadingMenu": "Loading Menu",
"query": "Search",
"search": "Search",
"enabled": "Enabled",
"disabled": "Disabled",
"edit": "Edit",
"delete": "Delete",
"create": "Create",
"yes": "Yes",
"no": "No",
"showSearchPanel": "Show search panel",
"hideSearchPanel": "Hide search panel"
}

View File

@ -1,165 +0,0 @@
{
"title": "Preferences",
"subtitle": "Customize Preferences & Preview in Real Time",
"enableStickyPreferencesNavigationBar": "Enable sticky preferences navigation bar",
"disableStickyPreferencesNavigationBar": "Disable sticky preferences navigation bar",
"resetTip": "Data has changed, click to reset",
"resetTitle": "Reset Preferences",
"resetSuccess": "Preferences reset successfully",
"appearance": "Appearance",
"layout": "Layout",
"content": "Content",
"other": "Other",
"wide": "Wide",
"compact": "Fixed",
"followSystem": "Follow System",
"vertical": "Vertical",
"verticalTip": "Side vertical menu mode",
"horizontal": "Horizontal",
"horizontalTip": "Horizontal menu mode, all menus displayed at the top",
"twoColumn": "Two Column",
"twoColumnTip": "Vertical Two Column Menu Mode",
"headerSidebarNav": "Header Vertical",
"headerSidebarNavTip": "Header Full Width, Sidebar Navigation Mode",
"headerTwoColumn": "Header Two Column",
"headerTwoColumnTip": "Header Navigation & Sidebar Two Column co-exists",
"mixedMenu": "Mixed Menu",
"mixedMenuTip": "Vertical & Horizontal Menu Co-exists",
"fullContent": "Full Content",
"fullContentTip": "Only display content body, hide all menus",
"normal": "Normal",
"plain": "Plain",
"rounded": "Rounded",
"copyPreferences": "Copy Preferences",
"enableCopyPreferences": "Show copy preferences button",
"copyPreferencesSuccessTitle": "Copy successful",
"copyPreferencesSuccess": "Copy successful, please override in `src/preferences.ts` under app",
"clearAndLogout": "Clear Cache & Logout",
"mode": "Mode",
"general": "General",
"language": "Language",
"dynamicTitle": "Dynamic Title",
"watermark": "Watermark",
"watermarkContent": "Please input Watermark content",
"checkUpdates": "Periodic update check",
"position": {
"title": "Preferences Postion",
"header": "Header",
"auto": "Auto",
"fixed": "Fixed"
},
"sidebar": {
"buttons": "Show Buttons",
"buttonFixed": "Fixed",
"buttonCollapsed": "Collapsed",
"title": "Sidebar",
"width": "Width",
"visible": "Show Sidebar",
"draggable": "Drag Sidebar Menu",
"collapsed": "Collpase Menu",
"collapsedShowTitle": "Show Menu Title",
"autoActivateChild": "Auto Activate SubMenu",
"autoActivateChildTip": "`Enabled` to automatically activate the submenu while click menu.",
"expandOnHover": "Expand On Hover",
"expandOnHoverTip": "When the mouse hovers over menu, \n `Enabled` to expand children menus \n `Disabled` to expand whole sidebar."
},
"tabbar": {
"title": "Tabbar",
"enable": "Enable Tab Bar",
"icon": "Show Tabbar Icon",
"showMore": "Show More Button",
"showMaximize": "Show Maximize Button",
"persist": "Persist Tabs",
"visitHistory": "Visit History",
"visitHistoryTip": "When enabled, the tab bar records tab visit history. \nClosing the current tab will automatically select the last opened tab.",
"maxCount": "Max Count of Tabs",
"maxCountTip": "When the number of tabs exceeds the maximum,\nthe oldest tab will be closed.\n Set to 0 to disable count checking.",
"draggable": "Enable Draggable Sort",
"wheelable": "Support Mouse Wheel",
"middleClickClose": "Close Tab when Mouse Middle Button Click",
"wheelableTip": "When enabled, the Tabbar area responds to vertical scrolling events of the scroll wheel.",
"styleType": {
"title": "Tabs Style",
"chrome": "Chrome",
"card": "Card",
"plain": "Plain",
"brisk": "Brisk"
},
"contextMenu": {
"reload": "Reload",
"close": "Close",
"pin": "Pin",
"unpin": "Unpin",
"closeLeft": "Close Left Tabs",
"closeRight": "Close Right Tabs",
"closeOther": "Close Other Tabs",
"closeAll": "Close All Tabs",
"openInNewWindow": "Open in New Window",
"maximize": "Maximize",
"restoreMaximize": "Restore"
}
},
"navigationMenu": {
"title": "Navigation Menu",
"style": "Navigation Menu Style",
"accordion": "Sidebar Accordion Menu",
"split": "Navigation Menu Separation",
"splitTip": "When enabled, the sidebar displays the top bar's submenu"
},
"animation": {
"title": "Animation",
"loading": "Page Loading",
"transition": "Page Transition",
"progress": "Page Progress"
},
"theme": {
"title": "Theme",
"radius": "Radius",
"fontSize": "Font Size",
"fontSizeTip": "Adjust global font size with real-time preview",
"weakMode": "Weak Mode",
"grayMode": "Gray Mode"
},
"header": {
"title": "Header",
"visible": "Show Header",
"modeStatic": "Static",
"modeFixed": "Fixed",
"modeAuto": "Auto hide & Show",
"modeAutoScroll": "Scroll to Hide & Show",
"menuAlign": "Menu Align",
"menuAlignStart": "Start",
"menuAlignEnd": "End",
"menuAlignCenter": "Center"
},
"footer": {
"title": "Footer",
"visible": "Show Footer",
"fixed": "Fixed at Bottom"
},
"copyright": {
"title": "Copyright",
"enable": "Enable Copyright",
"companyName": "Company Name",
"companySiteLink": "Company Site Link",
"date": "Date",
"icp": "ICP License Number",
"icpLink": "ICP Site Link"
},
"shortcutKeys": {
"title": "Shortcut Keys",
"global": "Global",
"search": "Global Search",
"logout": "Logout",
"preferences": "Preferences"
},
"widget": {
"title": "Widget",
"globalSearch": "Enable Global Search",
"fullscreen": "Enable Fullscreen",
"languageToggle": "Enable Language Toggle",
"sidebarToggle": "Enable Sidebar Toggle",
"lockScreen": "Enable Lock Screen",
"refresh": "Enable Refresh"
}
}

View File

@ -1,4 +0,0 @@
{
"updatePassword": "Update Password",
"updateBasicProfile": "Update Basic Profile"
}

View File

@ -1,117 +0,0 @@
{
"formRules": {
"required": "Please enter {0}",
"selectRequired": "Please select {0}",
"minLength": "{0} must be at least {1} characters",
"maxLength": "{0} can be at most {1} characters",
"length": "{0} must be {1} characters long",
"alreadyExists": "{0} `{1}` already exists",
"startWith": "{0} must start with `{1}`",
"invalidURL": "Please input a valid URL",
"sizeLimit": "The file size cannot exceed {0}MB",
"previewWarning": "Unable to open the file, there is no available URL or preview address"
},
"actionTitle": {
"edit": "Modify {0}",
"create": "Create {0}",
"delete": "Delete {0}",
"view": "View {0}"
},
"actionMessage": {
"deleteConfirm": "Are you sure to delete {0}?",
"deleting": "Deleting {0} ...",
"deleteSuccess": "{0} deleted successfully",
"operationSuccess": "Operation succeeded",
"operationFailed": "Operation failed"
},
"placeholder": {
"input": "Please enter",
"select": "Please select",
"upload": "Click to upload"
},
"captcha": {
"title": "Please complete the security verification",
"sliderSuccessText": "Passed",
"sliderDefaultText": "Slider and drag",
"alt": "Supports img tag src attribute value",
"sliderRotateDefaultTip": "Click picture to refresh",
"sliderTranslateDefaultTip": "Click picture to refresh",
"sliderRotateFailTip": "Validation failed",
"sliderRotateSuccessTip": "Validation successful, time {0} seconds",
"sliderTranslateFailTip": "Validation failed",
"sliderTranslateSuccessTip": "Validation successful, time {0} seconds",
"refreshAriaLabel": "Refresh captcha",
"confirmAriaLabel": "Confirm selection",
"confirm": "Confirm",
"pointAriaLabel": "Click point",
"clickInOrder": "Please click in order"
},
"iconPicker": {
"placeholder": "Select an icon",
"search": "Search icon..."
},
"jsonViewer": {
"copy": "Copy",
"copied": "Copied"
},
"crop": {
"title": "Image Cropping",
"titleTip": "Cropping Ratio {0}",
"confirm": "Crop",
"cancel": "Cancel cropping",
"errorTip": "Cropping error"
},
"fallback": {
"pageNotFound": "Oops! Page Not Found",
"pageNotFoundDesc": "Sorry, we couldn't find the page you were looking for.",
"forbidden": "Oops! Access Denied",
"forbiddenDesc": "Sorry, but you don't have permission to access this page.",
"internalError": "Oops! Something Went Wrong",
"internalErrorDesc": "Sorry, but the server encountered an error.",
"offline": "Offline Page",
"offlineError": "Oops! Network Error",
"offlineErrorDesc": "Sorry, can't connect to the internet. Check your connection.",
"comingSoon": "Coming Soon",
"http": {
"requestTimeout": "The request timed out. Please try again later.",
"networkError": "A network error occurred. Please check your internet connection and try again.",
"badRequest": "Bad Request. Please check your input and try again.",
"unauthorized": "Unauthorized. Please log in to continue.",
"forbidden": "Forbidden. You do not have permission to access this resource.",
"notFound": "Not Found. The requested resource could not be found.",
"internalServerError": "Internal Server Error. Something went wrong on our end. Please try again later."
}
},
"widgets": {
"document": "Document",
"qa": "Q&A",
"setting": "Settings",
"logoutTip": "Do you want to logout?",
"checkUpdatesTitle": "New Version Available",
"checkUpdatesDescription": "Click to refresh and get the latest version",
"search": {
"title": "Search",
"searchNavigate": "Search Navigation",
"select": "Select",
"navigate": "Navigate",
"close": "Close",
"noResults": "No Search Results Found",
"noRecent": "No Search History",
"recent": "Search History"
},
"lockScreen": {
"title": "Lock Screen",
"screenButton": "Locking",
"password": "Password",
"placeholder": "Please enter password",
"unlock": "Click to unlock",
"errorPasswordTip": "Password error, please re-enter",
"backToLogin": "Back to login",
"entry": "Enter the system"
},
"timezone": {
"setTimezone": "Set Timezone",
"setSuccess": "Timezone set successfully"
}
}
}

View File

@ -157,7 +157,6 @@
"title": "小部件", "title": "小部件",
"globalSearch": "启用全局搜索", "globalSearch": "启用全局搜索",
"fullscreen": "启用全屏", "fullscreen": "启用全屏",
"languageToggle": "启用语言切换",
"sidebarToggle": "启用侧边栏切换", "sidebarToggle": "启用侧边栏切换",
"lockScreen": "启用锁屏", "lockScreen": "启用锁屏",
"refresh": "启用刷新" "refresh": "启用刷新"

View File

@ -0,0 +1,106 @@
import type { App } from 'vue';
import type { LocaleSetupOptions } from './typing';
const messages: Record<string, any> = {};
const coreModules = import.meta.glob('./langs/zh-CN/*.json', { eager: true });
function isRecord(value: unknown): value is Record<string, any> {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}
function normalizeModule(module: unknown) {
return isRecord(module) && 'default' in module ? module.default : module;
}
function mergeDeep(target: Record<string, any>, source?: Record<string, any>) {
if (!source) {
return target;
}
for (const [key, value] of Object.entries(source)) {
if (isRecord(value) && isRecord(target[key])) {
mergeDeep(target[key], value);
} else {
target[key] = value;
}
}
return target;
}
function toLocaleMessages(
modules: Record<string, unknown>,
regexp = /\.\/langs\/zh-CN\/(.*)\.json$/,
) {
const result: Record<string, any> = {};
for (const [path, module] of Object.entries(modules)) {
const fileName = path.match(regexp)?.[1];
if (!fileName) {
continue;
}
result[fileName] = normalizeModule(module);
}
return result;
}
function mergeLocaleMessages(message: Record<string, any>) {
mergeDeep(messages, message);
}
mergeLocaleMessages(toLocaleMessages(coreModules));
function getValueByPath(path: string) {
if (!path) {
return path;
}
let current: unknown = messages;
for (const key of path.split('.')) {
current = isRecord(current) ? current[key] : undefined;
}
return current;
}
function $te(key: string) {
return getValueByPath(key) !== undefined;
}
function formatMessage(
message: string,
params?: Array<unknown> | Record<string, unknown>,
) {
if (!params) {
return message;
}
return message.replaceAll(/\{(\w+)\}/g, (placeholder, key: string) => {
const value = Array.isArray(params) ? params[Number(key)] : params[key];
return value === undefined || value === null ? placeholder : String(value);
});
}
function $t(
key?: null | string,
params?: Array<unknown> | Record<string, unknown>,
) {
if (!key) {
return '';
}
const value = getValueByPath(key);
return typeof value === 'string' ? formatMessage(value, params) : key;
}
function setLocale() {
document?.querySelector('html')?.setAttribute('lang', 'zh-CN');
}
async function setupLocale(app: App, options: LocaleSetupOptions = {}) {
void app;
void options;
setLocale();
}
export {
$t,
$te,
mergeLocaleMessages,
setupLocale,
};

View File

@ -1,23 +1,4 @@
export type SupportedLanguagesType = 'en-US' | 'zh-CN';
export type ImportLocaleFn = () => Promise<{ default: Record<string, string> }>;
export type LoadMessageFn = (
lang: SupportedLanguagesType,
) => Promise<Record<string, string> | undefined>;
export interface LocaleSetupOptions { export interface LocaleSetupOptions {
/**
* Default language
* @default zh-CN
*/
defaultLocale?: SupportedLanguagesType;
/**
* Load message function
* @param lang
* @returns
*/
loadMessages?: LoadMessageFn;
/** /**
* Whether to warn when the key is not found * Whether to warn when the key is not found
*/ */

View File

@ -468,7 +468,7 @@ export const useTabbarStore = defineStore('core-tabbar', {
* *
* @zh_CN * @zh_CN
* @zh_CN , * @zh_CN ,
* @zh_CN * @zh_CN
* *
* @param {TabDefinition} tab - * @param {TabDefinition} tab -
* @param {ComputedRef<string> | string} title - , * @param {ComputedRef<string> | string} title - ,

View File

@ -7,6 +7,12 @@
color: inherit; color: inherit;
} }
.ant-app .ant-btn {
height: var(--density-button-height) !important;
padding-inline: 12px;
font-size: var(--font-size-base);
}
.ant-btn { .ant-btn {
.anticon { .anticon {
display: inline-flex; display: inline-flex;
@ -24,6 +30,56 @@
} }
} }
.ant-app .ant-btn-sm {
height: var(--density-button-sm-height) !important;
padding-inline: 8px;
}
.ant-app .ant-btn-icon-only {
width: var(--density-button-height) !important;
padding-inline: 0;
}
.ant-app .ant-btn-sm.ant-btn-icon-only {
width: var(--density-button-sm-height) !important;
}
.ant-table-wrapper .ant-table {
font-size: var(--density-table-body-font-size);
}
.ant-table-wrapper .ant-table-thead > tr > th {
height: var(--density-table-header-height);
padding: 8px var(--density-table-cell-padding-x);
font-size: var(--density-table-header-font-size);
}
.ant-table-wrapper .ant-table-tbody > tr > td {
height: var(--density-table-row-height);
padding: 3px var(--density-table-cell-padding-x);
}
.ant-pagination {
font-size: var(--density-table-body-font-size);
}
.ant-pagination .ant-pagination-item,
.ant-pagination .ant-pagination-next,
.ant-pagination .ant-pagination-prev {
min-width: var(--density-pagination-height);
height: var(--density-pagination-height);
line-height: calc(var(--density-pagination-height) - 2px);
}
.ant-pagination .ant-select-selector {
min-height: var(--density-pagination-height) !important;
}
.ant-pagination .ant-select-single,
.ant-pagination .ant-select-single .ant-select-selector {
height: var(--density-pagination-height) !important;
}
.ant-tag { .ant-tag {
> svg { > svg {
display: inline-block; display: inline-block;

472
pnpm-lock.yaml generated
View File

@ -30,12 +30,6 @@ catalogs:
'@iconify/vue': '@iconify/vue':
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.0.0 version: 5.0.0
'@intlify/core-base':
specifier: ^11.3.0
version: 11.3.0
'@intlify/unplugin-vue-i18n':
specifier: ^11.0.7
version: 11.0.7
'@jspm/generator': '@jspm/generator':
specifier: ^2.12.0 specifier: ^2.12.0
version: 2.12.0 version: 2.12.0
@ -360,9 +354,6 @@ catalogs:
vue-eslint-parser: vue-eslint-parser:
specifier: ^10.4.0 specifier: ^10.4.0
version: 10.4.0 version: 10.4.0
vue-i18n:
specifier: ^11.3.0
version: 11.3.0
vue-json-viewer: vue-json-viewer:
specifier: ^3.0.4 specifier: ^3.0.4
version: 3.0.4 version: 3.0.4
@ -706,9 +697,6 @@ importers:
internal/vite-config: internal/vite-config:
dependencies: dependencies:
'@intlify/unplugin-vue-i18n':
specifier: 'catalog:'
version: 11.0.7(@vue/compiler-dom@3.5.30)(eslint@10.1.0(jiti@2.6.1))(rollup@4.60.0)(typescript@5.9.3)(vue-i18n@11.3.0(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))
'@jspm/generator': '@jspm/generator':
specifier: 'catalog:' specifier: 'catalog:'
version: 2.12.0 version: 2.12.0
@ -1339,18 +1327,12 @@ importers:
packages/locales: packages/locales:
dependencies: dependencies:
'@intlify/core-base':
specifier: 'catalog:'
version: 11.3.0
'@vben-core/composables': '@vben-core/composables':
specifier: workspace:* specifier: workspace:*
version: link:../@core/composables version: link:../@core/composables
vue: vue:
specifier: ^3.5.30 specifier: ^3.5.30
version: 3.5.30(typescript@5.9.3) version: 3.5.30(typescript@5.9.3)
vue-i18n:
specifier: 'catalog:'
version: 11.3.0(vue@3.5.30(typescript@5.9.3))
packages/preferences: packages/preferences:
dependencies: dependencies:
@ -2481,312 +2463,156 @@ packages:
'@epic-web/invariant@1.0.0': '@epic-web/invariant@1.0.0':
resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==}
'@esbuild/aix-ppc64@0.25.12':
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/aix-ppc64@0.27.4': '@esbuild/aix-ppc64@0.27.4':
resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ppc64] cpu: [ppc64]
os: [aix] os: [aix]
'@esbuild/android-arm64@0.25.12':
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.27.4': '@esbuild/android-arm64@0.27.4':
resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
'@esbuild/android-arm@0.25.12':
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-arm@0.27.4': '@esbuild/android-arm@0.27.4':
resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm] cpu: [arm]
os: [android] os: [android]
'@esbuild/android-x64@0.25.12':
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/android-x64@0.27.4': '@esbuild/android-x64@0.27.4':
resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [android] os: [android]
'@esbuild/darwin-arm64@0.25.12':
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-arm64@0.27.4': '@esbuild/darwin-arm64@0.27.4':
resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@esbuild/darwin-x64@0.25.12':
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.27.4': '@esbuild/darwin-x64@0.27.4':
resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@esbuild/freebsd-arm64@0.25.12':
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-arm64@0.27.4': '@esbuild/freebsd-arm64@0.27.4':
resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [freebsd] os: [freebsd]
'@esbuild/freebsd-x64@0.25.12':
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.27.4': '@esbuild/freebsd-x64@0.27.4':
resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
'@esbuild/linux-arm64@0.25.12':
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm64@0.27.4': '@esbuild/linux-arm64@0.27.4':
resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@esbuild/linux-arm@0.25.12':
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-arm@0.27.4': '@esbuild/linux-arm@0.27.4':
resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@esbuild/linux-ia32@0.25.12':
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.27.4': '@esbuild/linux-ia32@0.27.4':
resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ia32] cpu: [ia32]
os: [linux] os: [linux]
'@esbuild/linux-loong64@0.25.12':
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-loong64@0.27.4': '@esbuild/linux-loong64@0.27.4':
resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
'@esbuild/linux-mips64el@0.25.12':
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-mips64el@0.27.4': '@esbuild/linux-mips64el@0.27.4':
resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [mips64el] cpu: [mips64el]
os: [linux] os: [linux]
'@esbuild/linux-ppc64@0.25.12':
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.27.4': '@esbuild/linux-ppc64@0.27.4':
resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
'@esbuild/linux-riscv64@0.25.12':
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.27.4': '@esbuild/linux-riscv64@0.27.4':
resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
'@esbuild/linux-s390x@0.25.12':
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.27.4': '@esbuild/linux-s390x@0.27.4':
resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
'@esbuild/linux-x64@0.25.12':
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/linux-x64@0.27.4': '@esbuild/linux-x64@0.27.4':
resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@esbuild/netbsd-arm64@0.25.12':
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-arm64@0.27.4': '@esbuild/netbsd-arm64@0.27.4':
resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [netbsd] os: [netbsd]
'@esbuild/netbsd-x64@0.25.12':
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.27.4': '@esbuild/netbsd-x64@0.27.4':
resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [netbsd] os: [netbsd]
'@esbuild/openbsd-arm64@0.25.12':
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-arm64@0.27.4': '@esbuild/openbsd-arm64@0.27.4':
resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [openbsd] os: [openbsd]
'@esbuild/openbsd-x64@0.25.12':
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openbsd-x64@0.27.4': '@esbuild/openbsd-x64@0.27.4':
resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [openbsd] os: [openbsd]
'@esbuild/openharmony-arm64@0.25.12':
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/openharmony-arm64@0.27.4': '@esbuild/openharmony-arm64@0.27.4':
resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [openharmony] os: [openharmony]
'@esbuild/sunos-x64@0.25.12':
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/sunos-x64@0.27.4': '@esbuild/sunos-x64@0.27.4':
resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [sunos] os: [sunos]
'@esbuild/win32-arm64@0.25.12':
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.27.4': '@esbuild/win32-arm64@0.27.4':
resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@esbuild/win32-ia32@0.25.12':
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.27.4': '@esbuild/win32-ia32@0.27.4':
resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
'@esbuild/win32-x64@0.25.12':
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@esbuild/win32-x64@0.27.4': '@esbuild/win32-x64@0.27.4':
resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -2897,65 +2723,6 @@ packages:
'@internationalized/number@3.6.5': '@internationalized/number@3.6.5':
resolution: {integrity: sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==} resolution: {integrity: sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==}
'@intlify/bundle-utils@11.0.7':
resolution: {integrity: sha512-fEO3CJGPymxieGh8BHox7d6stgajDQae7wgpH6YYw7WX+cdW6jTTXyljZqz7OV3JcwlS9M9UHSoO+YwiO56IhA==}
engines: {node: '>= 20'}
peerDependencies:
petite-vue-i18n: '*'
vue-i18n: '*'
peerDependenciesMeta:
petite-vue-i18n:
optional: true
vue-i18n:
optional: true
'@intlify/core-base@11.3.0':
resolution: {integrity: sha512-NNX5jIwF4TJBe7RtSKDMOA6JD9mp2mRcBHAwt2X+Q8PvnZub0yj5YYXlFu2AcESdgQpEv/5Yx2uOCV/yh7YkZg==}
engines: {node: '>= 16'}
'@intlify/devtools-types@11.3.0':
resolution: {integrity: sha512-G9CNL4WpANWVdUjubOIIS7/D2j/0j+1KJmhBJxHilWNKr9mmt3IjFV3Hq4JoBP23uOoC5ynxz/FHZ42M+YxfGw==}
engines: {node: '>= 16'}
'@intlify/message-compiler@11.3.0':
resolution: {integrity: sha512-RAJp3TMsqohg/Wa7bVF3cChRhecSYBLrTCQSj7j0UtWVFLP+6iEJoE2zb7GU5fp+fmG5kCbUdzhmlAUCWXiUJw==}
engines: {node: '>= 16'}
'@intlify/shared@11.3.0':
resolution: {integrity: sha512-LC6P/uay7rXL5zZ5+5iRJfLs/iUN8apu9tm8YqQVmW3Uq3X4A0dOFUIDuAmB7gAC29wTHOS3EiN/IosNSz0eNQ==}
engines: {node: '>= 16'}
'@intlify/unplugin-vue-i18n@11.0.7':
resolution: {integrity: sha512-wswKprS1D8VfnxxVhKxug5wa3MbDSOcCoXOBjnzhMK+6NfP6h6UI8pFqSBIvcW8nPDuzweTc0Sk3PeBCcubfoQ==}
engines: {node: '>= 20'}
peerDependencies:
petite-vue-i18n: '*'
vue: ^3.5.30
vue-i18n: '*'
peerDependenciesMeta:
petite-vue-i18n:
optional: true
vue-i18n:
optional: true
'@intlify/vue-i18n-extensions@8.0.0':
resolution: {integrity: sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==}
engines: {node: '>= 18'}
peerDependencies:
'@intlify/shared': ^9.0.0 || ^10.0.0 || ^11.0.0
'@vue/compiler-dom': ^3.0.0
vue: ^3.5.30
vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0
peerDependenciesMeta:
'@intlify/shared':
optional: true
'@vue/compiler-dom':
optional: true
vue:
optional: true
vue-i18n:
optional: true
'@isaacs/cliui@8.0.2': '@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -4240,9 +4007,6 @@ packages:
'@vue/compiler-ssr@3.5.30': '@vue/compiler-ssr@3.5.30':
resolution: {integrity: sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==} resolution: {integrity: sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==}
'@vue/devtools-api@6.6.4':
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
'@vue/devtools-api@7.7.9': '@vue/devtools-api@7.7.9':
resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==}
@ -5403,11 +5167,6 @@ packages:
resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==}
engines: {node: '>=0.12'} engines: {node: '>=0.12'}
esbuild@0.25.12:
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'}
hasBin: true
esbuild@0.27.4: esbuild@0.27.4:
resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -5436,11 +5195,6 @@ packages:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'} engines: {node: '>=12'}
escodegen@2.1.0:
resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
engines: {node: '>=6.0'}
hasBin: true
eslint-compat-utils@0.5.1: eslint-compat-utils@0.5.1:
resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -5552,10 +5306,6 @@ packages:
resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24} engines: {node: ^20.19.0 || ^22.13.0 || >=24}
espree@9.6.1:
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
esprima@4.0.1: esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -6413,10 +6163,6 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
hasBin: true hasBin: true
jsonc-eslint-parser@2.4.2:
resolution: {integrity: sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
jsonc-eslint-parser@3.1.0: jsonc-eslint-parser@3.1.0:
resolution: {integrity: sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==} resolution: {integrity: sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24} engines: {node: ^20.19.0 || ^22.13.0 || >=24}
@ -8567,12 +8313,6 @@ packages:
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
vue-i18n@11.3.0:
resolution: {integrity: sha512-1J+xDfDJTLhDxElkd3+XUhT7FYSZd2b8pa7IRKGxhWH/8yt6PTvi3xmWhGwhYT5EaXdatui11pF2R6tL73/zPA==}
engines: {node: '>= 16'}
peerDependencies:
vue: ^3.5.30
vue-json-viewer@3.0.4: vue-json-viewer@3.0.4:
resolution: {integrity: sha512-pnC080rTub6YjccthVSNQod2z9Sl5IUUq46srXtn6rxwhW8QM4rlYn+CTSLFKXWfw+N3xv77Cioxw7B4XUKIbQ==} resolution: {integrity: sha512-pnC080rTub6YjccthVSNQod2z9Sl5IUUq46srXtn6rxwhW8QM4rlYn+CTSLFKXWfw+N3xv77Cioxw7B4XUKIbQ==}
peerDependencies: peerDependencies:
@ -8848,10 +8588,6 @@ packages:
yallist@4.0.0: yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
yaml-eslint-parser@1.3.2:
resolution: {integrity: sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==}
engines: {node: ^14.17.0 || >=16.0.0}
yaml-eslint-parser@2.0.0: yaml-eslint-parser@2.0.0:
resolution: {integrity: sha512-h0uDm97wvT2bokfwwTmY6kJ1hp6YDFL0nRHwNKz8s/VD1FH/vvZjAKoMUE+un0eaYBSG7/c6h+lJTP+31tjgTw==} resolution: {integrity: sha512-h0uDm97wvT2bokfwwTmY6kJ1hp6YDFL0nRHwNKz8s/VD1FH/vvZjAKoMUE+un0eaYBSG7/c6h+lJTP+31tjgTw==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24} engines: {node: ^20.19.0 || ^22.13.0 || >=24}
@ -10188,159 +9924,81 @@ snapshots:
'@epic-web/invariant@1.0.0': {} '@epic-web/invariant@1.0.0': {}
'@esbuild/aix-ppc64@0.25.12':
optional: true
'@esbuild/aix-ppc64@0.27.4': '@esbuild/aix-ppc64@0.27.4':
optional: true optional: true
'@esbuild/android-arm64@0.25.12':
optional: true
'@esbuild/android-arm64@0.27.4': '@esbuild/android-arm64@0.27.4':
optional: true optional: true
'@esbuild/android-arm@0.25.12':
optional: true
'@esbuild/android-arm@0.27.4': '@esbuild/android-arm@0.27.4':
optional: true optional: true
'@esbuild/android-x64@0.25.12':
optional: true
'@esbuild/android-x64@0.27.4': '@esbuild/android-x64@0.27.4':
optional: true optional: true
'@esbuild/darwin-arm64@0.25.12':
optional: true
'@esbuild/darwin-arm64@0.27.4': '@esbuild/darwin-arm64@0.27.4':
optional: true optional: true
'@esbuild/darwin-x64@0.25.12':
optional: true
'@esbuild/darwin-x64@0.27.4': '@esbuild/darwin-x64@0.27.4':
optional: true optional: true
'@esbuild/freebsd-arm64@0.25.12':
optional: true
'@esbuild/freebsd-arm64@0.27.4': '@esbuild/freebsd-arm64@0.27.4':
optional: true optional: true
'@esbuild/freebsd-x64@0.25.12':
optional: true
'@esbuild/freebsd-x64@0.27.4': '@esbuild/freebsd-x64@0.27.4':
optional: true optional: true
'@esbuild/linux-arm64@0.25.12':
optional: true
'@esbuild/linux-arm64@0.27.4': '@esbuild/linux-arm64@0.27.4':
optional: true optional: true
'@esbuild/linux-arm@0.25.12':
optional: true
'@esbuild/linux-arm@0.27.4': '@esbuild/linux-arm@0.27.4':
optional: true optional: true
'@esbuild/linux-ia32@0.25.12':
optional: true
'@esbuild/linux-ia32@0.27.4': '@esbuild/linux-ia32@0.27.4':
optional: true optional: true
'@esbuild/linux-loong64@0.25.12':
optional: true
'@esbuild/linux-loong64@0.27.4': '@esbuild/linux-loong64@0.27.4':
optional: true optional: true
'@esbuild/linux-mips64el@0.25.12':
optional: true
'@esbuild/linux-mips64el@0.27.4': '@esbuild/linux-mips64el@0.27.4':
optional: true optional: true
'@esbuild/linux-ppc64@0.25.12':
optional: true
'@esbuild/linux-ppc64@0.27.4': '@esbuild/linux-ppc64@0.27.4':
optional: true optional: true
'@esbuild/linux-riscv64@0.25.12':
optional: true
'@esbuild/linux-riscv64@0.27.4': '@esbuild/linux-riscv64@0.27.4':
optional: true optional: true
'@esbuild/linux-s390x@0.25.12':
optional: true
'@esbuild/linux-s390x@0.27.4': '@esbuild/linux-s390x@0.27.4':
optional: true optional: true
'@esbuild/linux-x64@0.25.12':
optional: true
'@esbuild/linux-x64@0.27.4': '@esbuild/linux-x64@0.27.4':
optional: true optional: true
'@esbuild/netbsd-arm64@0.25.12':
optional: true
'@esbuild/netbsd-arm64@0.27.4': '@esbuild/netbsd-arm64@0.27.4':
optional: true optional: true
'@esbuild/netbsd-x64@0.25.12':
optional: true
'@esbuild/netbsd-x64@0.27.4': '@esbuild/netbsd-x64@0.27.4':
optional: true optional: true
'@esbuild/openbsd-arm64@0.25.12':
optional: true
'@esbuild/openbsd-arm64@0.27.4': '@esbuild/openbsd-arm64@0.27.4':
optional: true optional: true
'@esbuild/openbsd-x64@0.25.12':
optional: true
'@esbuild/openbsd-x64@0.27.4': '@esbuild/openbsd-x64@0.27.4':
optional: true optional: true
'@esbuild/openharmony-arm64@0.25.12':
optional: true
'@esbuild/openharmony-arm64@0.27.4': '@esbuild/openharmony-arm64@0.27.4':
optional: true optional: true
'@esbuild/sunos-x64@0.25.12':
optional: true
'@esbuild/sunos-x64@0.27.4': '@esbuild/sunos-x64@0.27.4':
optional: true optional: true
'@esbuild/win32-arm64@0.25.12':
optional: true
'@esbuild/win32-arm64@0.27.4': '@esbuild/win32-arm64@0.27.4':
optional: true optional: true
'@esbuild/win32-ia32@0.25.12':
optional: true
'@esbuild/win32-ia32@0.27.4': '@esbuild/win32-ia32@0.27.4':
optional: true optional: true
'@esbuild/win32-x64@0.25.12':
optional: true
'@esbuild/win32-x64@0.27.4': '@esbuild/win32-x64@0.27.4':
optional: true optional: true
@ -10459,71 +10117,6 @@ snapshots:
dependencies: dependencies:
'@swc/helpers': 0.5.19 '@swc/helpers': 0.5.19
'@intlify/bundle-utils@11.0.7(vue-i18n@11.3.0(vue@3.5.30(typescript@5.9.3)))':
dependencies:
'@intlify/message-compiler': 11.3.0
'@intlify/shared': 11.3.0
acorn: 8.16.0
esbuild: 0.25.12
escodegen: 2.1.0
estree-walker: 2.0.2
jsonc-eslint-parser: 2.4.2
source-map-js: 1.2.1
yaml-eslint-parser: 1.3.2
optionalDependencies:
vue-i18n: 11.3.0(vue@3.5.30(typescript@5.9.3))
'@intlify/core-base@11.3.0':
dependencies:
'@intlify/devtools-types': 11.3.0
'@intlify/message-compiler': 11.3.0
'@intlify/shared': 11.3.0
'@intlify/devtools-types@11.3.0':
dependencies:
'@intlify/core-base': 11.3.0
'@intlify/shared': 11.3.0
'@intlify/message-compiler@11.3.0':
dependencies:
'@intlify/shared': 11.3.0
source-map-js: 1.2.1
'@intlify/shared@11.3.0': {}
'@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.30)(eslint@10.1.0(jiti@2.6.1))(rollup@4.60.0)(typescript@5.9.3)(vue-i18n@11.3.0(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1))
'@intlify/bundle-utils': 11.0.7(vue-i18n@11.3.0(vue@3.5.30(typescript@5.9.3)))
'@intlify/shared': 11.3.0
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.0)(@vue/compiler-dom@3.5.30)(vue-i18n@11.3.0(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))
'@rollup/pluginutils': 5.3.0(rollup@4.60.0)
'@typescript-eslint/scope-manager': 8.57.1
'@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3)
debug: 4.4.3
fast-glob: 3.3.3
pathe: 2.0.3
picocolors: 1.1.1
unplugin: 2.3.11
vue: 3.5.30(typescript@5.9.3)
optionalDependencies:
vue-i18n: 11.3.0(vue@3.5.30(typescript@5.9.3))
transitivePeerDependencies:
- '@vue/compiler-dom'
- eslint
- rollup
- supports-color
- typescript
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.3.0)(@vue/compiler-dom@3.5.30)(vue-i18n@11.3.0(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))':
dependencies:
'@babel/parser': 7.29.2
optionalDependencies:
'@intlify/shared': 11.3.0
'@vue/compiler-dom': 3.5.30
vue: 3.5.30(typescript@5.9.3)
vue-i18n: 11.3.0(vue@3.5.30(typescript@5.9.3))
'@isaacs/cliui@8.0.2': '@isaacs/cliui@8.0.2':
dependencies: dependencies:
string-width: 5.1.2 string-width: 5.1.2
@ -11773,8 +11366,6 @@ snapshots:
'@vue/compiler-dom': 3.5.30 '@vue/compiler-dom': 3.5.30
'@vue/shared': 3.5.30 '@vue/shared': 3.5.30
'@vue/devtools-api@6.6.4': {}
'@vue/devtools-api@7.7.9': '@vue/devtools-api@7.7.9':
dependencies: dependencies:
'@vue/devtools-kit': 7.7.9 '@vue/devtools-kit': 7.7.9
@ -13150,35 +12741,6 @@ snapshots:
ext: 1.7.0 ext: 1.7.0
optional: true optional: true
esbuild@0.25.12:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.12
'@esbuild/android-arm': 0.25.12
'@esbuild/android-arm64': 0.25.12
'@esbuild/android-x64': 0.25.12
'@esbuild/darwin-arm64': 0.25.12
'@esbuild/darwin-x64': 0.25.12
'@esbuild/freebsd-arm64': 0.25.12
'@esbuild/freebsd-x64': 0.25.12
'@esbuild/linux-arm': 0.25.12
'@esbuild/linux-arm64': 0.25.12
'@esbuild/linux-ia32': 0.25.12
'@esbuild/linux-loong64': 0.25.12
'@esbuild/linux-mips64el': 0.25.12
'@esbuild/linux-ppc64': 0.25.12
'@esbuild/linux-riscv64': 0.25.12
'@esbuild/linux-s390x': 0.25.12
'@esbuild/linux-x64': 0.25.12
'@esbuild/netbsd-arm64': 0.25.12
'@esbuild/netbsd-x64': 0.25.12
'@esbuild/openbsd-arm64': 0.25.12
'@esbuild/openbsd-x64': 0.25.12
'@esbuild/openharmony-arm64': 0.25.12
'@esbuild/sunos-x64': 0.25.12
'@esbuild/win32-arm64': 0.25.12
'@esbuild/win32-ia32': 0.25.12
'@esbuild/win32-x64': 0.25.12
esbuild@0.27.4: esbuild@0.27.4:
optionalDependencies: optionalDependencies:
'@esbuild/aix-ppc64': 0.27.4 '@esbuild/aix-ppc64': 0.27.4
@ -13221,14 +12783,6 @@ snapshots:
escape-string-regexp@5.0.0: {} escape-string-regexp@5.0.0: {}
escodegen@2.1.0:
dependencies:
esprima: 4.0.1
estraverse: 5.3.0
esutils: 2.0.3
optionalDependencies:
source-map: 0.6.1
eslint-compat-utils@0.5.1(eslint@10.1.0(jiti@2.6.1)): eslint-compat-utils@0.5.1(eslint@10.1.0(jiti@2.6.1)):
dependencies: dependencies:
eslint: 10.1.0(jiti@2.6.1) eslint: 10.1.0(jiti@2.6.1)
@ -13412,12 +12966,6 @@ snapshots:
acorn-jsx: 5.3.2(acorn@8.16.0) acorn-jsx: 5.3.2(acorn@8.16.0)
eslint-visitor-keys: 5.0.1 eslint-visitor-keys: 5.0.1
espree@9.6.1:
dependencies:
acorn: 8.16.0
acorn-jsx: 5.3.2(acorn@8.16.0)
eslint-visitor-keys: 3.4.3
esprima@4.0.1: {} esprima@4.0.1: {}
esquery@1.7.0: esquery@1.7.0:
@ -14254,13 +13802,6 @@ snapshots:
json5@2.2.3: {} json5@2.2.3: {}
jsonc-eslint-parser@2.4.2:
dependencies:
acorn: 8.16.0
eslint-visitor-keys: 3.4.3
espree: 9.6.1
semver: 7.7.4
jsonc-eslint-parser@3.1.0: jsonc-eslint-parser@3.1.0:
dependencies: dependencies:
acorn: 8.16.0 acorn: 8.16.0
@ -16511,14 +16052,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
vue-i18n@11.3.0(vue@3.5.30(typescript@5.9.3)):
dependencies:
'@intlify/core-base': 11.3.0
'@intlify/devtools-types': 11.3.0
'@intlify/shared': 11.3.0
'@vue/devtools-api': 6.6.4
vue: 3.5.30(typescript@5.9.3)
vue-json-viewer@3.0.4(vue@3.5.30(typescript@5.9.3)): vue-json-viewer@3.0.4(vue@3.5.30(typescript@5.9.3)):
dependencies: dependencies:
clipboard: 2.0.11 clipboard: 2.0.11
@ -16883,11 +16416,6 @@ snapshots:
yallist@4.0.0: {} yallist@4.0.0: {}
yaml-eslint-parser@1.3.2:
dependencies:
eslint-visitor-keys: 3.4.3
yaml: 2.8.3
yaml-eslint-parser@2.0.0: yaml-eslint-parser@2.0.0:
dependencies: dependencies:
eslint-visitor-keys: 5.0.1 eslint-visitor-keys: 5.0.1