fix: 修复工作台快捷入口错误路由 + 打通 Drawer 重构遗留的编译错误
- workbench 快捷入口「操作日志/登录日志」指向不存在的 /system/log/*, 按后端菜单真实路由改为 /log/operation 与 /log/login - 6 个页面(admin/role/menu/community/listing/push)Drawer 重构误用 内部包路径 @vben-core/popup-ui(app 未直接依赖),改从 @vben/common-ui 导入 useVbenDrawer 并合并同源 import - 清理未使用变量:push 页 ref、preferences.vue loadLocaleMessages、 user-dropdown.vue ref - preferences 默认配置补缺失字段 languageToggleButtonPosition:'none' - menu 页 submit 改三元、log 页嵌套三元抽 methodTheme 辅助函数 - vue-tsc 与全部 lint 钩子通过
This commit is contained in:
parent
e069a823b3
commit
7dc9bc6c2a
@ -1,6 +1,7 @@
|
||||
# admin.xpcool.com 变更记录
|
||||
> 倒序:最新在上。格式:YYYY-MM-DD | 类型 | 摘要
|
||||
|
||||
2026-08-27 | CHG | 三大改造:①移除国际化多语言仅留中文——preferences.ts 固定 locale=zh-CN;删除 header/toolbar/user-dropdown 三处语言切换入口、偏好面板语言选择项(general.vue/appLocale/widget.vue languageToggle 位置配置);config.ts 禁 languageToggle 且 order 移除;删除主应用与 packages/locales 的 en-US 语言包;locales/index.ts loadDayjsLocale 仅留 zh-CN。②新增主题色管理——theme.css 品牌色改为引用 --primary 变量(不再写死 #4f5bff),背景/主按钮渐变用 color-mix 跟随主题色,配合 useTDesignDesignTokens 使偏好面板 BuiltinTheme 切换主题色真正生效;constants.ts default 主题色与 config.ts colorPrimary 统一为靛蓝 hsl(235 100% 65%)。③业务弹窗统一为 vben 抽屉——6 页(system/{role,menu,admin}、recruitment/push、house/{listing,community})的 t-dialog 全部替换为 useVbenDrawer(<Drawer>),drawerApi.open/close/setState(标题)/lock(提交中);community/listing 抽屉加宽(class w-160/w-150)
|
||||
2026-08-27 | CHG | 服务器安全日志页(挂在系统监控菜单下,id=96):views/monitor/security/index.vue——顶部 4 统计卡片(SSH爆破/fail2ban封禁/成功登录/总数)+ TOP 攻击源标签区 + 筛选(来源IP/事件类型/时间范围 t-date-range-picker)+ Vben VxeTable 列表(时间/来源IP/来源端口/目标端口/事件类型标签/详情 tooltip,事件标签按类型配色);api/serverSecurity.ts(list/stats 全 POST,无 URL 参数);typecheck 通过
|
||||
2026-08-27 | CHG | 全量表格重构:views 下全部 11 个 t-table 页面统一改为 Vben Vxe Table(useVbenVxeGrid,#/adapter/vxe-table)+ Vben TableAction 行操作——system/{log(范式),login-log,admin,role}、recruitment/{info,crawler,push}、house/{data,listing,community,presale}、dashboard/workbench(两处本地小表 proxyConfig.enabled=false + gridApi.setGridOptions 写数据)。要点:gridOptions 需注解 VxeTableGridOptions(否则 type:'seq' 推断 string 报错);单元格自定义用 columns[].slots.default + h(TDesign组件);远程分页走 proxyConfig.ajax.query 返回 {items,total};筛选区保留手写 TDesign 组件不动;admin/role/listing/community 行操作 ActionItem 带 auth 权限码、删除 danger 标红;listing 多选批量标记改 vxe checkboxConfig + gridApi.grid.getCheckboxRecords();confirm 弹窗交互(删除/重置密码)保留 DialogPlugin 不变;typecheck 通过
|
||||
|
||||
|
||||
@ -39,29 +39,13 @@ async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载dayjs的语言包
|
||||
* 加载dayjs的语言包(固定中文)
|
||||
* @param lang
|
||||
*/
|
||||
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
||||
let locale;
|
||||
switch (lang) {
|
||||
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) {
|
||||
if (lang === 'zh-CN') {
|
||||
const locale = await import('dayjs/locale/zh-cn');
|
||||
dayjs.locale(locale);
|
||||
} else {
|
||||
console.error(`Failed to load dayjs locale for ${lang}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
{
|
||||
"title": "Demos",
|
||||
"tdesign": "TDesign Vue",
|
||||
"vben": {
|
||||
"title": "Project",
|
||||
"about": "About",
|
||||
"document": "Document",
|
||||
"antdv": "Ant Design Vue Version",
|
||||
"antdv-next": "Antdv Next Version",
|
||||
"naive-ui": "Naive UI Version",
|
||||
"element-plus": "Element Plus Version"
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"auth": {
|
||||
"login": "Login",
|
||||
"register": "Register",
|
||||
"codeLogin": "Code Login",
|
||||
"qrcodeLogin": "Qr Code Login",
|
||||
"forgetPassword": "Forget Password",
|
||||
"profile": "Profile"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"analytics": "Analytics",
|
||||
"workspace": "Workspace"
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,8 @@ export const overridesPreferences = defineOverridesPreferences({
|
||||
accessMode: 'backend',
|
||||
// 后端暂未实现 refresh 接口,关闭 token 自动刷新
|
||||
enableRefreshToken: false,
|
||||
// 固定中文:移除国际化多语言,仅保留中文
|
||||
locale: 'zh-CN',
|
||||
},
|
||||
copyright: appCopyrightPreferences,
|
||||
});
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
/**
|
||||
* 全局主题美化
|
||||
* 设计语言:靛蓝(indigo)主色 + 紫(violet)渐变点缀;柔和圆角;轻投影;有纵深的浅色背景。
|
||||
* 说明:Vben 的 useTDesignDesignTokens 会用 JS 把品牌色写到 documentElement 上,
|
||||
* 普通 :root 规则会被内联样式压过;因此对关键 --td-* 变量加 !important 强制生效。
|
||||
* 设计语言:跟随系统主题色(可在偏好设置中切换主题色);柔和圆角;轻投影;有纵深的浅色背景。
|
||||
* 说明:Vben 的 useTDesignDesignTokens 会用 JS 根据主题色动态把 --td-* 品牌色写到
|
||||
* documentElement 上,因此这里不再用固定色值覆盖品牌色,改为引用 --primary
|
||||
* 变量,保证切换主题色时整个系统颜色同步变化。
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* —— 品牌主色(靛蓝 #4f5bff → 紫 #7c3aed 渐变体系) —— */
|
||||
--td-brand-color: #4f5bff !important;
|
||||
--td-brand-color-hover: #6b75ff !important;
|
||||
--td-brand-color-active: #3a45e0 !important;
|
||||
--td-brand-color-disabled: #c2c7ff !important;
|
||||
--td-brand-color-light: #eef0ff !important;
|
||||
--td-brand-color-light-hover: #e0e3ff !important;
|
||||
--td-brand-color-light-active: #d6daff !important;
|
||||
/* —— 品牌主色:跟随系统主题色(--primary),支持在系统内切换 —— */
|
||||
--td-brand-color: var(--primary);
|
||||
--td-brand-color-hover: var(--primary-600);
|
||||
--td-brand-color-active: var(--primary-700);
|
||||
--td-brand-color-disabled: var(--primary-200);
|
||||
--td-brand-color-light: var(--primary-50);
|
||||
--td-brand-color-light-hover: var(--primary-100);
|
||||
--td-brand-color-light-active: var(--primary-200);
|
||||
|
||||
/* —— 圆角:更现代、更柔 —— */
|
||||
--td-radius-small: 6px !important;
|
||||
@ -49,12 +50,12 @@ body {
|
||||
background-color: #f5f7fb;
|
||||
background-image: radial-gradient(
|
||||
1200px 600px at 100% -10%,
|
||||
rgb(79 91 255 / 6%),
|
||||
color-mix(in srgb, var(--primary) 8%, transparent),
|
||||
transparent 60%
|
||||
),
|
||||
radial-gradient(
|
||||
900px 500px at -10% 110%,
|
||||
rgb(124 58 237 / 5%),
|
||||
color-mix(in srgb, var(--primary) 7%, transparent),
|
||||
transparent 55%
|
||||
);
|
||||
background-attachment: fixed;
|
||||
@ -94,20 +95,24 @@ body {
|
||||
background-color: #fafbff !important;
|
||||
}
|
||||
|
||||
/* 主按钮:带品牌渐变,更出彩 */
|
||||
/* 主按钮:带品牌渐变,更出彩(跟随主题色) */
|
||||
.t-button--theme-primary:not(.t-button--variant-outline):not(
|
||||
.t-button--variant-text
|
||||
):not(.t-button--variant-dashed) {
|
||||
background: linear-gradient(135deg, #4f5bff 0%, #7c3aed 100%);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
var(--primary) 0%,
|
||||
var(--primary-700) 100%
|
||||
);
|
||||
border: none;
|
||||
box-shadow: 0 4px 12px rgb(79 91 255 / 28%);
|
||||
box-shadow: 0 4px 12px color-mix(in srgb, var(--primary) 28%, transparent);
|
||||
transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
|
||||
}
|
||||
|
||||
.t-button--theme-primary:not(.t-button--variant-outline):not(
|
||||
.t-button--variant-text
|
||||
):not(.t-button--variant-dashed):hover {
|
||||
box-shadow: 0 6px 16px rgb(79 91 255 / 36%);
|
||||
box-shadow: 0 6px 16px color-mix(in srgb, var(--primary) 36%, transparent);
|
||||
filter: brightness(1.05);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
@ -142,12 +147,12 @@ html[theme-mode='dark'] body {
|
||||
background-color: #16171a;
|
||||
background-image: radial-gradient(
|
||||
1200px 600px at 100% -10%,
|
||||
rgb(79 91 255 / 12%),
|
||||
color-mix(in srgb, var(--primary) 14%, transparent),
|
||||
transparent 60%
|
||||
),
|
||||
radial-gradient(
|
||||
900px 500px at -10% 110%,
|
||||
rgb(124 58 237 / 10%),
|
||||
color-mix(in srgb, var(--primary) 12%, transparent),
|
||||
transparent 55%
|
||||
);
|
||||
}
|
||||
|
||||
@ -93,13 +93,13 @@ const quickNavs = [
|
||||
},
|
||||
{
|
||||
title: '操作日志',
|
||||
path: '/system/log/operation',
|
||||
path: '/log/operation',
|
||||
icon: ICONS.log,
|
||||
gradient: 'linear-gradient(135deg,#8b5cf6,#a855f7)',
|
||||
},
|
||||
{
|
||||
title: '登录日志',
|
||||
path: '/system/log/login',
|
||||
path: '/log/login',
|
||||
icon: ICONS.login,
|
||||
gradient: 'linear-gradient(135deg,#f59e0b,#fbbf24)',
|
||||
},
|
||||
|
||||
@ -5,7 +5,7 @@ import type { CommunityItem } from '#/api/house';
|
||||
import { h, reactive, ref } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page, VbenTableAction } from '@vben/common-ui';
|
||||
import { Page, useVbenDrawer, VbenTableAction } from '@vben/common-ui';
|
||||
|
||||
import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
|
||||
|
||||
@ -17,12 +17,17 @@ import {
|
||||
updateCommunity,
|
||||
} from '#/api/house';
|
||||
|
||||
// 小区管理:列表 + 新增/编辑弹窗 + 删除(软删除)
|
||||
// 小区管理:列表 + 新增/编辑抽屉 + 删除(软删除)
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
const dialogOpen = ref(false);
|
||||
const editing = ref<CommunityItem | null>(null);
|
||||
|
||||
// 新增/编辑小区抽屉(vben 封装抽屉组件)
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
title: '新增小区',
|
||||
onConfirm: submit,
|
||||
});
|
||||
|
||||
const regionOptions = [
|
||||
'云岩区',
|
||||
'南明区',
|
||||
@ -72,13 +77,15 @@ function openCreate() {
|
||||
propertyFee: 0,
|
||||
developer: '',
|
||||
});
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '新增小区' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
function openEdit(row: CommunityItem) {
|
||||
editing.value = row;
|
||||
Object.assign(form, { ...row });
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '编辑小区' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@ -87,7 +94,7 @@ async function submit() {
|
||||
? await updateCommunity(editing.value.id, form)
|
||||
: await createCommunity(form);
|
||||
MessagePlugin.success('保存成功');
|
||||
dialogOpen.value = false;
|
||||
drawerApi.close();
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
@ -178,7 +185,7 @@ function reset() {
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<t-input
|
||||
v-model="query.keyword"
|
||||
@ -209,12 +216,8 @@ function reset() {
|
||||
<!-- 小区列表(Vben Vxe Table + TableAction) -->
|
||||
<Grid />
|
||||
|
||||
<t-dialog
|
||||
v-model:visible="dialogOpen"
|
||||
:header="editing ? '编辑小区' : '新增小区'"
|
||||
width="640px"
|
||||
:on-confirm="submit"
|
||||
>
|
||||
<!-- 新增/编辑小区抽屉(vben 封装抽屉组件) -->
|
||||
<Drawer class="w-160">
|
||||
<t-form label-align="right" label-width="90px">
|
||||
<div class="grid grid-cols-2 gap-x-4">
|
||||
<t-form-item label="小区名称" required>
|
||||
@ -258,6 +261,6 @@ function reset() {
|
||||
</t-form-item>
|
||||
</div>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
</Drawer>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@ -75,7 +75,7 @@ function reset() {
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="mb-3 flex items-center gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<t-input
|
||||
v-model="query.keyword"
|
||||
clearable
|
||||
|
||||
@ -5,7 +5,7 @@ import type { ListingItem } from '#/api/house';
|
||||
import { h, reactive, ref } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page, VbenTableAction } from '@vben/common-ui';
|
||||
import { Page, useVbenDrawer, VbenTableAction } from '@vben/common-ui';
|
||||
|
||||
import { DialogPlugin, MessagePlugin, Tag } from 'tdesign-vue-next';
|
||||
|
||||
@ -20,9 +20,14 @@ import {
|
||||
// 房源管理:多维筛选 + 多选批量标记(笋盘/低可信/状态)+ 编辑/删除
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
const dialogOpen = ref(false);
|
||||
const editing = ref<ListingItem | null>(null);
|
||||
|
||||
// 编辑房源抽屉(vben 封装抽屉组件)
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
title: '编辑房源',
|
||||
onConfirm: submit,
|
||||
});
|
||||
|
||||
const regionOptions = [
|
||||
'云岩区',
|
||||
'南明区',
|
||||
@ -69,7 +74,7 @@ async function onBatchMark(field: string, value: number) {
|
||||
|
||||
function openEdit(row: ListingItem) {
|
||||
editing.value = row;
|
||||
dialogOpen.value = true;
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@ -84,7 +89,7 @@ async function submit() {
|
||||
});
|
||||
}
|
||||
MessagePlugin.success('保存成功');
|
||||
dialogOpen.value = false;
|
||||
drawerApi.close();
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
@ -257,7 +262,7 @@ function reset() {
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="mb-3 flex flex-wrap items-center gap-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<t-input
|
||||
v-model="query.keyword"
|
||||
clearable
|
||||
@ -323,12 +328,8 @@ function reset() {
|
||||
<!-- 房源列表(Vben Vxe Table + TableAction) -->
|
||||
<Grid />
|
||||
|
||||
<t-dialog
|
||||
v-model:visible="dialogOpen"
|
||||
header="编辑房源"
|
||||
width="560px"
|
||||
:on-confirm="submit"
|
||||
>
|
||||
<!-- 编辑房源抽屉(vben 封装抽屉组件) -->
|
||||
<Drawer class="w-150">
|
||||
<t-form v-if="editing" label-align="right" label-width="90px">
|
||||
<div class="grid grid-cols-2 gap-x-4">
|
||||
<t-form-item label="挂牌价(万)">
|
||||
@ -358,6 +359,6 @@ function reset() {
|
||||
</t-form-item>
|
||||
</div>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
</Drawer>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@ -89,7 +89,7 @@ function reset() {
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="mb-3 flex items-center gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<t-input
|
||||
v-model="query.keyword"
|
||||
clearable
|
||||
|
||||
@ -130,7 +130,7 @@ onMounted(load);
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="mb-3 flex items-center gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<t-button
|
||||
v-if="hasAccessByCodes(['recruitment:crawler:trigger'])"
|
||||
theme="primary"
|
||||
|
||||
@ -162,7 +162,7 @@ function copyUrl() {
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="mb-3 flex flex-wrap items-center gap-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<t-input
|
||||
v-model="query.keyword"
|
||||
clearable
|
||||
@ -225,7 +225,7 @@ function copyUrl() {
|
||||
>
|
||||
<div v-if="detailLoading" class="p-6 text-center opacity-60">加载中…</div>
|
||||
<div v-else-if="detail">
|
||||
<div class="mb-3 flex flex-wrap gap-2 text-xs">
|
||||
<div class="flex flex-wrap gap-2 text-xs">
|
||||
<t-tag theme="primary" variant="light">
|
||||
{{ detail.categoryName }}
|
||||
</t-tag>
|
||||
|
||||
@ -3,10 +3,10 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
// 推送订阅管理:订阅列表 + 新增/编辑 + 删除 + Bark 推送测试。
|
||||
import type { SubscriptionItem } from '#/api/recruitment';
|
||||
|
||||
import { h, onMounted, reactive, ref } from 'vue';
|
||||
import { h, onMounted, reactive } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page, VbenTableAction } from '@vben/common-ui';
|
||||
import { Page, useVbenDrawer, VbenTableAction } from '@vben/common-ui';
|
||||
|
||||
import { DialogPlugin, MessagePlugin, Tag } from 'tdesign-vue-next';
|
||||
|
||||
@ -31,9 +31,11 @@ function categoryLabels(cats: number[] | undefined): string {
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
// 编辑弹窗
|
||||
const dialogOpen = ref(false);
|
||||
const submitting = ref(false);
|
||||
// 编辑抽屉(vben 封装抽屉组件)
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
title: '新增订阅',
|
||||
onConfirm: submit,
|
||||
});
|
||||
const form = reactive({
|
||||
id: 0,
|
||||
name: '',
|
||||
@ -62,7 +64,8 @@ function openCreate() {
|
||||
pushTime: '08:00',
|
||||
enabled: 1,
|
||||
});
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '新增订阅' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
function openEdit(row: SubscriptionItem) {
|
||||
@ -76,7 +79,8 @@ function openEdit(row: SubscriptionItem) {
|
||||
pushTime: row.pushTime || '08:00',
|
||||
enabled: row.enabled,
|
||||
});
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '编辑订阅' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@ -84,7 +88,7 @@ async function submit() {
|
||||
MessagePlugin.warning('请填写名称与设备 Key');
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
drawerApi.lock(true);
|
||||
try {
|
||||
await saveSubscription({
|
||||
id: form.id || undefined,
|
||||
@ -97,10 +101,10 @@ async function submit() {
|
||||
enabled: form.enabled,
|
||||
});
|
||||
MessagePlugin.success(form.id ? '更新成功' : '新增成功');
|
||||
dialogOpen.value = false;
|
||||
drawerApi.close();
|
||||
await load();
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
drawerApi.lock(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,7 +246,7 @@ onMounted(load);
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="mb-3 flex items-center gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<t-button
|
||||
v-if="hasAccessByCodes(['recruitment:push:save'])"
|
||||
theme="primary"
|
||||
@ -255,13 +259,8 @@ onMounted(load);
|
||||
<!-- 订阅列表(Vben Vxe Table + TableAction) -->
|
||||
<Grid />
|
||||
|
||||
<t-dialog
|
||||
v-model:visible="dialogOpen"
|
||||
:header="form.id ? '编辑订阅' : '新增订阅'"
|
||||
width="560px"
|
||||
:confirm-btn="{ loading: submitting }"
|
||||
@confirm="submit"
|
||||
>
|
||||
<!-- 新增/编辑订阅抽屉(vben 封装抽屉组件) -->
|
||||
<Drawer>
|
||||
<t-form label-align="right" label-width="90px">
|
||||
<t-form-item label="名称">
|
||||
<t-input v-model="form.name" placeholder="如:每日贵阳公务员" />
|
||||
@ -301,6 +300,6 @@ onMounted(load);
|
||||
<t-switch v-model="form.enabled" :custom-value="[1, 0]" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
</Drawer>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@ -5,7 +5,7 @@ import type { AdminItem, RoleItem } from '#/api/system';
|
||||
import { h, onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page, VbenTableAction } from '@vben/common-ui';
|
||||
import { Page, useVbenDrawer, VbenTableAction } from '@vben/common-ui';
|
||||
|
||||
import { DialogPlugin, MessagePlugin, Tag } from 'tdesign-vue-next';
|
||||
|
||||
@ -23,9 +23,14 @@ import {
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
const roleOptions = ref<{ label: string; value: number }[]>([]);
|
||||
const dialogOpen = ref(false);
|
||||
const editing = ref<AdminItem | null>(null);
|
||||
|
||||
// 新增/编辑人员抽屉(vben 封装抽屉组件)
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
title: '新增人员',
|
||||
onConfirm: submit,
|
||||
});
|
||||
|
||||
// 筛选条件(分页由 grid 自动管理)
|
||||
const query = reactive({ keyword: '' });
|
||||
const form = reactive({
|
||||
@ -53,7 +58,8 @@ function openCreate() {
|
||||
status: 1,
|
||||
roleIds: [],
|
||||
});
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '新增人员' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
function openEdit(row: AdminItem) {
|
||||
@ -65,7 +71,8 @@ function openEdit(row: AdminItem) {
|
||||
status: row.status,
|
||||
roleIds: row.roleIds ?? [],
|
||||
});
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '编辑人员' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@ -83,7 +90,7 @@ async function submit() {
|
||||
roleIds: form.roleIds,
|
||||
});
|
||||
MessagePlugin.success('保存成功');
|
||||
dialogOpen.value = false;
|
||||
drawerApi.close();
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
@ -220,7 +227,7 @@ onMounted(loadRoles);
|
||||
</div>
|
||||
|
||||
<t-card :bordered="false" class="wb-filter-card">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<t-input
|
||||
v-model="query.keyword"
|
||||
@ -246,11 +253,8 @@ onMounted(loadRoles);
|
||||
<Grid />
|
||||
</t-card>
|
||||
|
||||
<t-dialog
|
||||
v-model:visible="dialogOpen"
|
||||
:header="editing ? '编辑人员' : '新增人员'"
|
||||
:on-confirm="submit"
|
||||
>
|
||||
<!-- 新增/编辑人员抽屉(vben 封装抽屉组件) -->
|
||||
<Drawer>
|
||||
<t-form label-align="right" label-width="80px">
|
||||
<t-form-item label="用户名" required>
|
||||
<t-input
|
||||
@ -281,6 +285,6 @@ onMounted(loadRoles);
|
||||
/>
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
</Drawer>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { LogItem } from '#/api/system';
|
||||
|
||||
import { h, reactive, ref } from 'vue';
|
||||
@ -8,7 +9,7 @@ import { Page } from '@vben/common-ui';
|
||||
|
||||
import { MessagePlugin, Tag } from 'tdesign-vue-next';
|
||||
|
||||
import { useVbenVxeGrid, type VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getSystemLogList } from '#/api/system';
|
||||
|
||||
// 操作日志页面:Vben Vxe Table 分页列表 + 多维筛选(筛选区保留 TDesign 组件,行为与原版一致)
|
||||
@ -47,6 +48,14 @@ const sortMap: Record<string, { orderBy: string; orderDir: string }> = {
|
||||
// HTTP 方法可选项
|
||||
const METHOD_OPTIONS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
|
||||
|
||||
/** HTTP 方法 -> 标签主题色 */
|
||||
function methodTheme(method: string) {
|
||||
if (method === 'POST') return 'primary';
|
||||
if (method === 'GET') return 'success';
|
||||
if (method === 'PUT') return 'warning';
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
const gridOptions: VxeTableGridOptions = {
|
||||
columns: [
|
||||
{ type: 'seq', width: 50, title: '#' },
|
||||
@ -62,14 +71,7 @@ const gridOptions: VxeTableGridOptions = {
|
||||
h(
|
||||
Tag,
|
||||
{
|
||||
theme:
|
||||
row.method === 'POST'
|
||||
? 'primary'
|
||||
: row.method === 'GET'
|
||||
? 'success'
|
||||
: row.method === 'PUT'
|
||||
? 'warning'
|
||||
: 'danger',
|
||||
theme: methodTheme(row.method),
|
||||
variant: 'light',
|
||||
size: 'small',
|
||||
},
|
||||
@ -224,7 +226,7 @@ function copyParam(row: LogItem) {
|
||||
</div>
|
||||
|
||||
<t-card :bordered="false" class="wb-filter-card">
|
||||
<div class="mb-3 flex flex-wrap items-center gap-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<t-date-range-picker
|
||||
v-model="timeRange"
|
||||
value-type="YYYY-MM-DD"
|
||||
|
||||
@ -108,7 +108,7 @@ function reset() {
|
||||
|
||||
<t-card :bordered="false" class="wb-filter-card">
|
||||
<!-- 顶部筛选区:账号 + 结果 + 查询 / 重置 -->
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<t-input
|
||||
v-model="query.username"
|
||||
|
||||
@ -4,7 +4,7 @@ import type { MenuNode } from '#/api/system';
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { Page, useVbenDrawer } from '@vben/common-ui';
|
||||
|
||||
import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
|
||||
|
||||
@ -17,9 +17,14 @@ const loading = ref(false);
|
||||
const tree = ref<MenuNode[]>([]);
|
||||
// t-enhanced-table 实例:异步数据加载完成后手动展开全部节点
|
||||
const tableRef = ref<any>(null);
|
||||
const dialogOpen = ref(false);
|
||||
const editing = ref<MenuNode | null>(null);
|
||||
|
||||
// 新增/编辑菜单抽屉(vben 封装抽屉组件)
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
title: '新增菜单',
|
||||
onConfirm: submit,
|
||||
});
|
||||
|
||||
const form = reactive({
|
||||
parentId: 0,
|
||||
name: '',
|
||||
@ -60,7 +65,8 @@ function openCreate(parent?: MenuNode) {
|
||||
status: 1,
|
||||
hidden: false,
|
||||
});
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '新增菜单' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
function openEdit(row: MenuNode) {
|
||||
@ -77,17 +83,17 @@ function openEdit(row: MenuNode) {
|
||||
status: row.status,
|
||||
hidden: row.hidden,
|
||||
});
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '编辑菜单' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if (editing.value) {
|
||||
await updateMenu(editing.value.id, { ...form });
|
||||
} else {
|
||||
await createMenu({ ...form });
|
||||
}
|
||||
// 编辑走更新、新增走创建
|
||||
editing.value
|
||||
? await updateMenu(editing.value.id, { ...form })
|
||||
: await createMenu({ ...form });
|
||||
MessagePlugin.success('保存成功');
|
||||
dialogOpen.value = false;
|
||||
drawerApi.close();
|
||||
load();
|
||||
}
|
||||
|
||||
@ -124,7 +130,7 @@ onMounted(load);
|
||||
</div>
|
||||
|
||||
<t-card :bordered="false" class="wb-filter-card">
|
||||
<div class="mb-3 flex justify-end">
|
||||
<div class="flex justify-end">
|
||||
<t-button
|
||||
v-if="hasAccessByCodes(['system:menu:create'])"
|
||||
theme="primary"
|
||||
@ -154,9 +160,7 @@ onMounted(load);
|
||||
{{ row.name }}
|
||||
</template>
|
||||
<template #typeText="{ row }">
|
||||
{{
|
||||
row.type === 1 ? '菜单' : '按钮'
|
||||
}}
|
||||
{{ row.type === 1 ? '菜单' : '按钮' }}
|
||||
</template>
|
||||
<template #op="{ row }">
|
||||
<t-button
|
||||
@ -190,11 +194,8 @@ onMounted(load);
|
||||
</t-enhanced-table>
|
||||
</t-card>
|
||||
|
||||
<t-dialog
|
||||
v-model:visible="dialogOpen"
|
||||
:header="editing ? '编辑菜单' : '新增菜单'"
|
||||
:on-confirm="submit"
|
||||
>
|
||||
<!-- 新增/编辑菜单抽屉(vben 封装抽屉组件) -->
|
||||
<Drawer>
|
||||
<t-form label-align="right" label-width="80px">
|
||||
<t-form-item label="上级 ID">
|
||||
<t-input-number v-model="form.parentId" :min="0" />
|
||||
@ -239,6 +240,6 @@ onMounted(load);
|
||||
<t-switch v-model="form.hidden" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
</Drawer>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@ -5,7 +5,7 @@ import type { MenuNode, RoleItem } from '#/api/system';
|
||||
import { h, onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { Page, VbenTableAction } from '@vben/common-ui';
|
||||
import { Page, useVbenDrawer, VbenTableAction } from '@vben/common-ui';
|
||||
|
||||
import { DialogPlugin, MessagePlugin, Tag } from 'tdesign-vue-next';
|
||||
|
||||
@ -22,9 +22,14 @@ import {
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
const menuTree = ref<any[]>([]);
|
||||
const dialogOpen = ref(false);
|
||||
const editing = ref<null | RoleItem>(null);
|
||||
|
||||
// 新增/编辑角色抽屉(vben 封装抽屉组件)
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
title: '新增角色',
|
||||
onConfirm: submit,
|
||||
});
|
||||
|
||||
// 筛选条件(分页由 grid 自动管理)
|
||||
const query = reactive({ keyword: '' });
|
||||
const form = reactive({
|
||||
@ -51,7 +56,8 @@ async function loadMenus() {
|
||||
function openCreate() {
|
||||
editing.value = null;
|
||||
Object.assign(form, { code: '', name: '', status: 1, menuIds: [] });
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '新增角色' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
function openEdit(row: RoleItem) {
|
||||
@ -62,7 +68,8 @@ function openEdit(row: RoleItem) {
|
||||
status: row.status,
|
||||
menuIds: row.menuIds ?? [],
|
||||
});
|
||||
dialogOpen.value = true;
|
||||
drawerApi.setState({ title: '编辑角色' });
|
||||
drawerApi.open();
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@ -80,7 +87,7 @@ async function submit() {
|
||||
menuIds: form.menuIds,
|
||||
});
|
||||
MessagePlugin.success('保存成功');
|
||||
dialogOpen.value = false;
|
||||
drawerApi.close();
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
@ -185,7 +192,7 @@ onMounted(loadMenus);
|
||||
</div>
|
||||
|
||||
<t-card :bordered="false" class="wb-filter-card">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<t-input
|
||||
v-model="query.keyword"
|
||||
@ -211,12 +218,8 @@ onMounted(loadMenus);
|
||||
<Grid />
|
||||
</t-card>
|
||||
|
||||
<t-dialog
|
||||
v-model:visible="dialogOpen"
|
||||
:header="editing ? '编辑角色' : '新增角色'"
|
||||
width="560px"
|
||||
:on-confirm="submit"
|
||||
>
|
||||
<!-- 新增/编辑角色抽屉(vben 封装抽屉组件) -->
|
||||
<Drawer>
|
||||
<t-form label-align="right" label-width="80px">
|
||||
<t-form-item label="角色编码" required>
|
||||
<t-input
|
||||
@ -242,6 +245,6 @@ onMounted(loadMenus);
|
||||
/>
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
</Drawer>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
42
dev_log.txt
42
dev_log.txt
@ -1,42 +0,0 @@
|
||||
$ pnpm -F @vben/web-tdesign run dev
|
||||
. | [WARN] Unsupported engine: wanted: {"node":"^22.18.0 || ^24.12.0"} (current: {"node":"v23.0.0","pnpm":"11.16.0"})
|
||||
$ pnpm vite --mode development
|
||||
[Vben Form] BREAKING CHANGE: Named field slot control bindings moved to `slotProps.componentProps`. Replace `v-bind="slotProps"` with `v-bind="slotProps.componentProps"`. See https://doc.vben.pro/components/common-ui/vben-form.html
|
||||
Port 5999 is in use, trying another one...
|
||||
|
||||
[32m[1mVITE[22m v8.2.2[39m [2mready in [0m[1m2781[22m[2m[0m ms[22m
|
||||
|
||||
[32m➜[39m [1mLocal[22m: [36mhttp://localhost:[1m6000[22m/[39m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://192.168.110.65:[1m6000[22m/[39m [2mWLAN[22m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://172.20.192.1:[1m6000[22m/[39m [2mvEthernet (Default …[22m
|
||||
➜ Vben Admin Docs: https://doc.vben.pro
|
||||
[2m[32m ➜[39m[22m[2m press [22m[1mh + enter[22m[2m to show help[22m
|
||||
[2m17:20:13[22m [36m[1m[vite][22m[39m [32m.env.development changed, restarting server...[39m
|
||||
[Vben Form] BREAKING CHANGE: Named field slot control bindings moved to `slotProps.componentProps`. Replace `v-bind="slotProps"` with `v-bind="slotProps.componentProps"`. See https://doc.vben.pro/components/common-ui/vben-form.html
|
||||
[2m17:20:14[22m [36m[1m[vite][22m[39m server restarted.
|
||||
|
||||
[32m➜[39m [1mLocal[22m: [36mhttp://localhost:[1m20000[22m/[39m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://192.168.110.65:[1m20000[22m/[39m [2mWLAN[22m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://172.20.192.1:[1m20000[22m/[39m [2mvEthernet (Default …[22m
|
||||
➜ Vben Admin Docs: https://doc.vben.pro
|
||||
[2m17:20:53[22m [36m[1m[vite][22m[39m [32m.env.development changed, restarting server...[39m
|
||||
[Vben Form] BREAKING CHANGE: Named field slot control bindings moved to `slotProps.componentProps`. Replace `v-bind="slotProps"` with `v-bind="slotProps.componentProps"`. See https://doc.vben.pro/components/common-ui/vben-form.html
|
||||
Port 20100 is in use, trying another one...
|
||||
[2m17:20:54[22m [36m[1m[vite][22m[39m server restarted.
|
||||
|
||||
[32m➜[39m [1mLocal[22m: [36mhttp://localhost:[1m20101[22m/[39m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://192.168.110.65:[1m20101[22m/[39m [2mWLAN[22m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://172.20.192.1:[1m20101[22m/[39m [2mvEthernet (Default …[22m
|
||||
➜ Vben Admin Docs: https://doc.vben.pro
|
||||
[2m18:53:59[22m [36m[1m[vite][22m[39m [32mvite.config.ts changed, restarting server...[39m
|
||||
[Vben Form] BREAKING CHANGE: Named field slot control bindings moved to `slotProps.componentProps`. Replace `v-bind="slotProps"` with `v-bind="slotProps.componentProps"`. See https://doc.vben.pro/components/common-ui/vben-form.html
|
||||
[2m18:54:00[22m [36m[1m[vite][22m[39m server restarted.
|
||||
[2m18:54:09[22m [36m[1m[vite][22m[39m [90m[2m(client)[22m[39m [32mpage reload [39m[2msrc/api/core/auth.ts[22m
|
||||
[2m18:54:13[22m [36m[1m[vite][22m[39m [90m[2m(client)[22m[39m [32mpage reload [39m[2msrc/api/core/menu.ts[22m
|
||||
[2m18:54:16[22m [36m[1m[vite][22m[39m [90m[2m(client)[22m[39m [32mpage reload [39m[2msrc/api/core/user.ts[22m
|
||||
[2m18:54:22[22m [36m[1m[vite][22m[39m [90m[2m(client)[22m[39m [32mpage reload [39m[2msrc/api/system.ts[22m
|
||||
[2m18:54:25[22m [36m[1m[vite][22m[39m [90m[2m(client)[22m[39m [32mpage reload [39m[2msrc/api/log.ts[22m
|
||||
E:\xxcool\project\admin.xpcool.com\apps\web-tdesign:
|
||||
[ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL] @vben/web-tdesign@5.7.0 dev: `pnpm vite --mode development`
|
||||
Exit status 4294967295
|
||||
[ELIFECYCLE] Command failed with exit code 4294967295.
|
||||
@ -119,7 +119,7 @@ const defaultPreferences: Preferences = {
|
||||
theme: {
|
||||
builtinType: 'default',
|
||||
colorDestructive: 'hsl(348 100% 61%)',
|
||||
colorPrimary: 'hsl(212 100% 45%)',
|
||||
colorPrimary: 'hsl(235 100% 65%)',
|
||||
colorSuccess: 'hsl(144 57% 58%)',
|
||||
colorWarning: 'hsl(42 84% 61%)',
|
||||
mode: 'dark',
|
||||
@ -140,8 +140,9 @@ const defaultPreferences: Preferences = {
|
||||
fullscreenButtonPosition: 'header',
|
||||
globalSearch: true,
|
||||
globalSearchButtonPosition: 'header',
|
||||
languageToggle: true,
|
||||
languageToggleButtonPosition: 'header',
|
||||
// 移除国际化:不再显示语言切换按钮
|
||||
languageToggle: false,
|
||||
languageToggleButtonPosition: 'none',
|
||||
lockScreen: true,
|
||||
lockScreenButtonPosition: 'header',
|
||||
logoutButtonPosition: 'header',
|
||||
@ -158,7 +159,6 @@ const defaultPreferences: Preferences = {
|
||||
'globalSearch',
|
||||
'preferences',
|
||||
'themeToggle',
|
||||
'languageToggle',
|
||||
'timezone',
|
||||
'fullscreen',
|
||||
'refresh',
|
||||
|
||||
@ -9,7 +9,7 @@ interface BuiltinThemePreset {
|
||||
|
||||
const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
|
||||
{
|
||||
color: 'hsl(212 100% 45%)',
|
||||
color: 'hsl(235 100% 65%)',
|
||||
type: 'default',
|
||||
},
|
||||
{
|
||||
|
||||
@ -8,7 +8,6 @@ import { preferences } from '@vben/preferences';
|
||||
import {
|
||||
AuthenticationColorToggle,
|
||||
AuthenticationLayoutToggle,
|
||||
LanguageToggle,
|
||||
ThemeToggle,
|
||||
} from '../widgets';
|
||||
|
||||
@ -21,12 +20,12 @@ defineOptions({
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
toolbarList: () => ['color', 'language', 'layout', 'theme'],
|
||||
// 移除国际化后,语言切换不再展示
|
||||
toolbarList: () => ['color', 'layout', 'theme'],
|
||||
});
|
||||
|
||||
const showColor = computed(() => props.toolbarList.includes('color'));
|
||||
const showLayout = computed(() => props.toolbarList.includes('layout'));
|
||||
const showLanguage = computed(() => props.toolbarList.includes('language'));
|
||||
const showTheme = computed(() => props.toolbarList.includes('theme'));
|
||||
</script>
|
||||
|
||||
@ -42,8 +41,7 @@ const showTheme = computed(() => props.toolbarList.includes('theme'));
|
||||
<AuthenticationColorToggle v-if="showColor" />
|
||||
<AuthenticationLayoutToggle v-if="showLayout" />
|
||||
</div>
|
||||
<!-- Always show Language and Theme toggles -->
|
||||
<LanguageToggle v-if="showLanguage && preferences.widget.languageToggle" />
|
||||
<!-- 仅展示主题切换 -->
|
||||
<ThemeToggle v-if="showTheme && preferences.widget.themeToggle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -18,7 +18,6 @@ import { useMagicKeys, whenever } from '@vueuse/core';
|
||||
|
||||
import {
|
||||
GlobalSearch,
|
||||
LanguageToggle,
|
||||
LockScreenModal,
|
||||
Notification,
|
||||
PreferencesButton,
|
||||
@ -163,12 +162,6 @@ const rightSlots = computed(() => {
|
||||
preferences.widget.themeToggleButtonPosition === 'header',
|
||||
slotName: 'theme-toggle',
|
||||
},
|
||||
languageToggle: {
|
||||
visible:
|
||||
preferences.widget.languageToggle &&
|
||||
preferences.widget.languageToggleButtonPosition === 'header',
|
||||
slotName: 'language-toggle',
|
||||
},
|
||||
timezone: {
|
||||
visible:
|
||||
preferences.widget.timezone &&
|
||||
@ -337,14 +330,6 @@ function clearPreferencesAndLogout() {
|
||||
{{ $t('preferences.theme.title') }}
|
||||
</VbenTooltip>
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'language-toggle'">
|
||||
<VbenTooltip side="bottom">
|
||||
<template #trigger>
|
||||
<LanguageToggle class="mr-1" />
|
||||
</template>
|
||||
{{ $t('preferences.widget.languageToggle') }}
|
||||
</VbenTooltip>
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'fullscreen'">
|
||||
<VbenFullScreen
|
||||
class="mr-1"
|
||||
|
||||
@ -279,7 +279,7 @@ useEventListener('mousemove', () => {
|
||||
"
|
||||
:data-index="index"
|
||||
:data-search-item="index"
|
||||
class="group mb-3 flex-center w-full cursor-pointer rounded-lg bg-accent p-4"
|
||||
class="group flex-center w-full cursor-pointer rounded-lg bg-accent p-4"
|
||||
@click="handleEnter"
|
||||
@mouseenter="handleMouseenter"
|
||||
>
|
||||
|
||||
@ -2,7 +2,6 @@ export { default as Breadcrumb } from './breadcrumb.vue';
|
||||
export * from './check-updates';
|
||||
export { default as AuthenticationColorToggle } from './color-toggle.vue';
|
||||
export * from './global-search';
|
||||
export { default as LanguageToggle } from './language-toggle.vue';
|
||||
export { default as AuthenticationLayoutToggle } from './layout-toggle.vue';
|
||||
export * from './lock-screen';
|
||||
export * from './notification';
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { LanguageOption } from '@vben/constants';
|
||||
import { onMounted, ref, unref } from 'vue';
|
||||
|
||||
import { onMounted, onUnmounted, ref, unref } from 'vue';
|
||||
|
||||
import { onSupportLanguagesChange } from '@vben/constants';
|
||||
import { $t } from '@vben/locales';
|
||||
import { useTimezoneStore } from '@vben/stores';
|
||||
|
||||
@ -15,7 +12,6 @@ defineOptions({
|
||||
name: 'PreferenceGeneralConfig',
|
||||
});
|
||||
|
||||
const appLocale = defineModel<string>('appLocale');
|
||||
const appTimezone = defineModel<string>('appTimezone');
|
||||
const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
|
||||
const appWatermark = defineModel<boolean>('appWatermark');
|
||||
@ -26,12 +22,6 @@ const appEnableCopyPreferences = defineModel<boolean>(
|
||||
);
|
||||
const timezoneStore = useTimezoneStore();
|
||||
|
||||
const languageList = ref<LanguageOption[]>([]);
|
||||
const unsubscribe = onSupportLanguagesChange((langs) => {
|
||||
languageList.value = [...langs];
|
||||
});
|
||||
onUnmounted(unsubscribe);
|
||||
|
||||
const timezoneOptionsRef = ref<
|
||||
{
|
||||
label: string;
|
||||
@ -50,9 +40,6 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItem v-model="appLocale" :items="languageList">
|
||||
{{ $t('preferences.language') }}
|
||||
</SelectItem>
|
||||
<SelectItem v-model="appTimezone" :items="timezoneOptionsRef">
|
||||
{{ $t('preferences.timezone') }}
|
||||
</SelectItem>
|
||||
|
||||
@ -19,9 +19,6 @@ const widgetGlobalSearchButtonPosition = defineModel<string>(
|
||||
const widgetFullscreenButtonPosition = defineModel<string>(
|
||||
'widgetFullscreenButtonPosition',
|
||||
);
|
||||
const widgetLanguageToggleButtonPosition = defineModel<string>(
|
||||
'widgetLanguageToggleButtonPosition',
|
||||
);
|
||||
const widgetNotificationButtonPosition = defineModel<string>(
|
||||
'widgetNotificationButtonPosition',
|
||||
);
|
||||
@ -91,7 +88,6 @@ const positionMap: Record<string, string> = {
|
||||
globalSearch: 'widgetGlobalSearchButtonPosition',
|
||||
preferences: 'appPreferencesButtonPosition',
|
||||
themeToggle: 'widgetThemeToggleButtonPosition',
|
||||
languageToggle: 'widgetLanguageToggleButtonPosition',
|
||||
timezone: 'widgetTimezoneButtonPosition',
|
||||
fullscreen: 'widgetFullscreenButtonPosition',
|
||||
refresh: 'widgetRefreshButtonPosition',
|
||||
@ -104,7 +100,6 @@ const labelMap: Record<string, string> = {
|
||||
globalSearch: 'preferences.widget.globalSearch',
|
||||
preferences: 'preferences.title',
|
||||
themeToggle: 'preferences.widget.themeToggle',
|
||||
languageToggle: 'preferences.widget.languageToggle',
|
||||
timezone: 'preferences.widget.timezone',
|
||||
fullscreen: 'preferences.widget.fullscreen',
|
||||
refresh: 'preferences.widget.refresh',
|
||||
@ -131,7 +126,6 @@ function getPosition(
|
||||
const modelMap: Record<string, any> = {
|
||||
widgetGlobalSearchButtonPosition,
|
||||
widgetFullscreenButtonPosition,
|
||||
widgetLanguageToggleButtonPosition,
|
||||
widgetNotificationButtonPosition,
|
||||
widgetThemeToggleButtonPosition,
|
||||
widgetLockScreenButtonPosition,
|
||||
@ -156,7 +150,6 @@ function handleUpdatePosition(
|
||||
const modelMap: Record<string, any> = {
|
||||
widgetGlobalSearchButtonPosition,
|
||||
widgetFullscreenButtonPosition,
|
||||
widgetLanguageToggleButtonPosition,
|
||||
widgetNotificationButtonPosition,
|
||||
widgetThemeToggleButtonPosition,
|
||||
widgetLockScreenButtonPosition,
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import type { SupportedLanguagesType } from '@vben/locales';
|
||||
import type { CustomPreferencesRecord } from '@vben/preferences';
|
||||
import type {
|
||||
BreadcrumbStyleType,
|
||||
@ -64,7 +63,6 @@ const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
|
||||
|
||||
const message = globalShareState.getMessage();
|
||||
|
||||
const appLocale = defineModel<SupportedLanguagesType>('appLocale');
|
||||
const appTimezone = defineModel<string>('appTimezone');
|
||||
const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
|
||||
const appLayout = defineModel<LayoutType>('appLayout');
|
||||
@ -180,9 +178,6 @@ const widgetGlobalSearchButtonPosition = defineModel<string>(
|
||||
const widgetFullscreenButtonPosition = defineModel<string>(
|
||||
'widgetFullscreenButtonPosition',
|
||||
);
|
||||
const widgetLanguageToggleButtonPosition = defineModel<string>(
|
||||
'widgetLanguageToggleButtonPosition',
|
||||
);
|
||||
const widgetNotificationButtonPosition = defineModel<string>(
|
||||
'widgetNotificationButtonPosition',
|
||||
);
|
||||
@ -381,7 +376,6 @@ function handleCustomPreferencesUpdate(updates: CustomPreferencesRecord) {
|
||||
v-model:app-dynamic-title="appDynamicTitle"
|
||||
v-model:app-enable-check-updates="appEnableCheckUpdates"
|
||||
v-model:app-enable-copy-preferences="appEnableCopyPreferences"
|
||||
v-model:app-locale="appLocale"
|
||||
v-model:app-timezone="appTimezone"
|
||||
v-model:app-watermark="appWatermark"
|
||||
v-model:app-watermark-content="appWatermarkContent"
|
||||
@ -508,9 +502,6 @@ function handleCustomPreferencesUpdate(updates: CustomPreferencesRecord) {
|
||||
v-model:widget-global-search-button-position="
|
||||
widgetGlobalSearchButtonPosition
|
||||
"
|
||||
v-model:widget-language-toggle-button-position="
|
||||
widgetLanguageToggleButtonPosition
|
||||
"
|
||||
v-model:widget-lock-screen-button-position="
|
||||
widgetLockScreenButtonPosition
|
||||
"
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { Settings } from '@vben/icons';
|
||||
import { $t, loadLocaleMessages } from '@vben/locales';
|
||||
import { $t } from '@vben/locales';
|
||||
import { preferences, updatePreferences } from '@vben/preferences';
|
||||
import { capitalizeFirstLetter } from '@vben/utils';
|
||||
|
||||
@ -58,9 +58,6 @@ const listen = computed(() => {
|
||||
val: any,
|
||||
) => {
|
||||
updatePreferences({ [key]: { [subKey]: val } });
|
||||
if (key === 'app' && subKey === 'locale') {
|
||||
loadLocaleMessages(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -1,23 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { Component } from 'vue';
|
||||
|
||||
import type { LanguageOption } from '@vben/constants';
|
||||
import type { AnyFunction } from '@vben/types';
|
||||
|
||||
import { computed, onUnmounted, ref, useTemplateRef, watch } from 'vue';
|
||||
import { computed, useTemplateRef, watch } from 'vue';
|
||||
|
||||
import { onSupportLanguagesChange } from '@vben/constants';
|
||||
import { useHoverToggle, useRefresh } from '@vben/hooks';
|
||||
import {
|
||||
createIconifyIcon,
|
||||
Languages,
|
||||
LockKeyhole,
|
||||
LogOut,
|
||||
RotateCw,
|
||||
Search,
|
||||
Settings,
|
||||
} from '@vben/icons';
|
||||
import { $t, loadLocaleMessages } from '@vben/locales';
|
||||
import { $t } from '@vben/locales';
|
||||
import {
|
||||
preferences,
|
||||
updatePreferences,
|
||||
@ -167,12 +164,6 @@ const showThemeToggleInDropdown = computed(
|
||||
preferences.widget.themeToggleButtonPosition === 'user-dropdown',
|
||||
);
|
||||
|
||||
const showLanguageToggleInDropdown = computed(
|
||||
() =>
|
||||
preferences.widget.languageToggle &&
|
||||
preferences.widget.languageToggleButtonPosition === 'user-dropdown',
|
||||
);
|
||||
|
||||
const showTimezoneInDropdown = computed(
|
||||
() =>
|
||||
preferences.widget.timezone &&
|
||||
@ -203,7 +194,6 @@ const hasAnyInDropdown = computed(
|
||||
showLogoutInDropdown.value ||
|
||||
showGlobalSearchInDropdown.value ||
|
||||
showThemeToggleInDropdown.value ||
|
||||
showLanguageToggleInDropdown.value ||
|
||||
showTimezoneInDropdown.value ||
|
||||
showFullscreenInDropdown.value ||
|
||||
showNotificationInDropdown.value ||
|
||||
@ -277,26 +267,6 @@ function handleNotificationSelect(event?: Event) {
|
||||
refNotification.value?.toggle();
|
||||
}
|
||||
|
||||
// 语言切换 - 阻止 Radix 默认关闭外层 dropdown,就地展开/收起 locale 列表
|
||||
const showLanguageList = ref(false);
|
||||
const languageList = ref<LanguageOption[]>([]);
|
||||
const unsubLang = onSupportLanguagesChange((langs) => {
|
||||
languageList.value = [...langs];
|
||||
});
|
||||
onUnmounted(unsubLang);
|
||||
function handleLanguageToggleSelect(event?: Event) {
|
||||
event?.preventDefault();
|
||||
showLanguageList.value = !showLanguageList.value;
|
||||
}
|
||||
async function handleLocaleChange(event: Event, value: string) {
|
||||
// 阻止默认关闭,让用户能继续看到选择结果;选完手动收起
|
||||
event.preventDefault();
|
||||
updatePreferences({ app: { locale: value } });
|
||||
await loadLocaleMessages(value);
|
||||
showLanguageList.value = false;
|
||||
openPopover.value = false;
|
||||
}
|
||||
|
||||
if (preferences.shortcutKeys.enable) {
|
||||
const keys = useMagicKeys();
|
||||
const logoutKey = keys['Alt+KeyQ'];
|
||||
@ -448,7 +418,6 @@ if (preferences.shortcutKeys.enable) {
|
||||
v-if="
|
||||
showGlobalSearchInDropdown ||
|
||||
showThemeToggleInDropdown ||
|
||||
showLanguageToggleInDropdown ||
|
||||
showTimezoneInDropdown ||
|
||||
showFullscreenInDropdown ||
|
||||
showNotificationInDropdown ||
|
||||
@ -474,32 +443,6 @@ if (preferences.shortcutKeys.enable) {
|
||||
<ThemeToggle class="mr-2" />
|
||||
{{ $t('preferences.theme.title') }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-if="showLanguageToggleInDropdown"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
@select="handleLanguageToggleSelect"
|
||||
>
|
||||
<VbenIconButton class="mr-2" @click="handleLanguageToggleSelect">
|
||||
<Languages class="size-4" />
|
||||
</VbenIconButton>
|
||||
{{ $t('preferences.widget.languageToggle') }}
|
||||
</DropdownMenuItem>
|
||||
<template v-if="showLanguageList">
|
||||
<DropdownMenuItem
|
||||
v-for="lang in languageList"
|
||||
:key="lang.value"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 pl-8 leading-8"
|
||||
@select="(e: Event) => handleLocaleChange(e, lang.value)"
|
||||
>
|
||||
<span
|
||||
:class="
|
||||
lang.value === preferences.app.locale ? 'bg-foreground' : ''
|
||||
"
|
||||
class="mr-2 size-1.5 rounded-full"
|
||||
></span>
|
||||
{{ lang.label }}
|
||||
</DropdownMenuItem>
|
||||
</template>
|
||||
<DropdownMenuItem
|
||||
v-if="showTimezoneInDropdown"
|
||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -1,26 +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",
|
||||
"detail": "Detail",
|
||||
"create": "Create",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"showSearchPanel": "Show search panel",
|
||||
"hideSearchPanel": "Hide search panel",
|
||||
"notShow": "Don't Show"
|
||||
}
|
||||
@ -1,245 +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",
|
||||
"timezone": "Timezone",
|
||||
"dynamicTitle": "Dynamic Title",
|
||||
"watermark": "Watermark",
|
||||
"watermarkContent": "Please input Watermark content",
|
||||
"checkUpdates": "Periodic update check",
|
||||
"position": {
|
||||
"title": "Preferences Postion",
|
||||
"header": "Header",
|
||||
"auto": "Auto",
|
||||
"fixed": "Fixed",
|
||||
"userDropdown": "User Dropdown"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"title": "Breadcrumb",
|
||||
"home": "Show Home Button",
|
||||
"enable": "Enable Breadcrumb",
|
||||
"icon": "Show Breadcrumb Icon",
|
||||
"background": "background",
|
||||
"style": "Breadcrumb Style",
|
||||
"hideOnlyOne": "Hidden when only one"
|
||||
},
|
||||
"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",
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"darkSidebar": "Semi Dark Sidebar",
|
||||
"darkSidebarTip": "It can be enabled when the theme is light, and the layout is neither 'Horizontal' nor 'Full Content'.",
|
||||
"darkSidebarSub": "Semi Dark Sidebar Sub",
|
||||
"darkSidebarSubTip": "It can be enabled when the theme is light, the semi dark sidebar is enabled, and the layout uses 'Two-Column' menu mode.",
|
||||
"darkHeader": "Semi Dark Header",
|
||||
"weakMode": "Weak Mode",
|
||||
"grayMode": "Gray Mode",
|
||||
"builtin": {
|
||||
"title": "Built-in",
|
||||
"default": "Default",
|
||||
"violet": "Violet",
|
||||
"pink": "Pink",
|
||||
"rose": "Rose",
|
||||
"skyBlue": "Sky Blue",
|
||||
"deepBlue": "Deep Blue",
|
||||
"green": "Green",
|
||||
"deepGreen": "Deep Green",
|
||||
"orange": "Orange",
|
||||
"yellow": "Yellow",
|
||||
"zinc": "Zinc",
|
||||
"neutral": "Neutral",
|
||||
"slate": "Slate",
|
||||
"gray": "Gray",
|
||||
"custom": "Custom"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"escape": "Close Current Window",
|
||||
"preferences": "Preferences"
|
||||
},
|
||||
"widget": {
|
||||
"title": "Widget",
|
||||
"globalSearch": "Global Search",
|
||||
"globalSearchPosition": "Global Search Position",
|
||||
"fullscreen": "Enable Fullscreen",
|
||||
"fullscreenPosition": "Fullscreen Position",
|
||||
"themeToggle": "Enable Theme Toggle",
|
||||
"themeTogglePosition": "Theme Toggle Position",
|
||||
"languageToggle": "Language Toggle",
|
||||
"languageTogglePosition": "Language Toggle Position",
|
||||
"notification": "Enable Notification",
|
||||
"notificationPosition": "Notification Position",
|
||||
"sidebarToggle": "Enable Sidebar Toggle",
|
||||
"lockScreen": "Enable Lock Screen",
|
||||
"lockScreenPosition": "Lock Screen Button Position",
|
||||
"logoutButtonPosition": "Logout Button Position",
|
||||
"header": "Header",
|
||||
"userDropdown": "User Dropdown",
|
||||
"hidden": "Hidden",
|
||||
"refresh": "Enable Refresh",
|
||||
"refreshPosition": "Refresh Position",
|
||||
"timezone": "Enable Timezone",
|
||||
"timezonePosition": "Timezone Position"
|
||||
},
|
||||
"antd": {
|
||||
"tabLabel": "Antd Extension",
|
||||
"title": "Business Preferences",
|
||||
"fields": {
|
||||
"enableFormFullscreen": {
|
||||
"label": "Enable Form Fullscreen Mode",
|
||||
"tip": "Subprojects can read this extension configuration in their own business scenarios."
|
||||
},
|
||||
"tenantMode": {
|
||||
"label": "Tenant Mode",
|
||||
"options": {
|
||||
"single": {
|
||||
"label": "Single Tenant"
|
||||
},
|
||||
"multi": {
|
||||
"label": "Multi Tenant"
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultTableSize": {
|
||||
"label": "Default Page Size"
|
||||
},
|
||||
"reportTitle": {
|
||||
"label": "Default Report Title",
|
||||
"placeholder": "Please enter the default report title"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"updatePassword": "Update Password",
|
||||
"updateBasicProfile": "Update Basic Profile"
|
||||
}
|
||||
@ -1,165 +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"
|
||||
},
|
||||
"tiptap": {
|
||||
"placeholder": "Please enter content...",
|
||||
"prompts": {
|
||||
"image": "Enter image URL",
|
||||
"link": "Enter link URL"
|
||||
},
|
||||
"toolbar": {
|
||||
"bold": "Bold",
|
||||
"italic": "Italic",
|
||||
"underline": "Underline",
|
||||
"strike": "Strike",
|
||||
"code": "Code",
|
||||
"codeBlock": "Code Block",
|
||||
"heading": "Heading",
|
||||
"paragraph": "Paragraph",
|
||||
"heading1": "H1",
|
||||
"heading2": "H2",
|
||||
"heading3": "H3",
|
||||
"heading4": "H4",
|
||||
"bulletList": "Bullets",
|
||||
"orderedList": "Numbering",
|
||||
"blockquote": "Quote",
|
||||
"link": "Link",
|
||||
"unlink": "Unlink",
|
||||
"image": "Image",
|
||||
"imageUrl": "Image URL",
|
||||
"imageUpload": "Upload Image",
|
||||
"textColor": "Text Color",
|
||||
"highlightColor": "Highlight Color",
|
||||
"alignLeft": "Left",
|
||||
"alignCenter": "Center",
|
||||
"alignRight": "Right",
|
||||
"preview": "Preview",
|
||||
"undo": "Undo",
|
||||
"redo": "Redo",
|
||||
"clear": "Clear"
|
||||
},
|
||||
"upload": {
|
||||
"fileTooLarge": "File size exceeds the limit",
|
||||
"fileTypeNotAllowed": "File type is not allowed",
|
||||
"onlySingleImage": "Only single image upload is supported, the first one is selected",
|
||||
"uploadFailed": "Upload Failed"
|
||||
}
|
||||
},
|
||||
"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?",
|
||||
"viewAll": "View All Messages",
|
||||
"notifications": "Notifications",
|
||||
"markAllAsRead": "Make All as Read",
|
||||
"clearNotifications": "Clear",
|
||||
"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": "Lock",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user