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 变更记录
|
# admin.xpcool.com 变更记录
|
||||||
> 倒序:最新在上。格式:YYYY-MM-DD | 类型 | 摘要
|
> 倒序:最新在上。格式: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 | 服务器安全日志页(挂在系统监控菜单下,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 通过
|
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
|
* @param lang
|
||||||
*/
|
*/
|
||||||
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
||||||
let locale;
|
if (lang === 'zh-CN') {
|
||||||
switch (lang) {
|
const locale = await import('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);
|
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',
|
accessMode: 'backend',
|
||||||
// 后端暂未实现 refresh 接口,关闭 token 自动刷新
|
// 后端暂未实现 refresh 接口,关闭 token 自动刷新
|
||||||
enableRefreshToken: false,
|
enableRefreshToken: false,
|
||||||
|
// 固定中文:移除国际化多语言,仅保留中文
|
||||||
|
locale: 'zh-CN',
|
||||||
},
|
},
|
||||||
copyright: appCopyrightPreferences,
|
copyright: appCopyrightPreferences,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,19 +1,20 @@
|
|||||||
/**
|
/**
|
||||||
* 全局主题美化
|
* 全局主题美化
|
||||||
* 设计语言:靛蓝(indigo)主色 + 紫(violet)渐变点缀;柔和圆角;轻投影;有纵深的浅色背景。
|
* 设计语言:跟随系统主题色(可在偏好设置中切换主题色);柔和圆角;轻投影;有纵深的浅色背景。
|
||||||
* 说明:Vben 的 useTDesignDesignTokens 会用 JS 把品牌色写到 documentElement 上,
|
* 说明:Vben 的 useTDesignDesignTokens 会用 JS 根据主题色动态把 --td-* 品牌色写到
|
||||||
* 普通 :root 规则会被内联样式压过;因此对关键 --td-* 变量加 !important 强制生效。
|
* documentElement 上,因此这里不再用固定色值覆盖品牌色,改为引用 --primary
|
||||||
|
* 变量,保证切换主题色时整个系统颜色同步变化。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* —— 品牌主色(靛蓝 #4f5bff → 紫 #7c3aed 渐变体系) —— */
|
/* —— 品牌主色:跟随系统主题色(--primary),支持在系统内切换 —— */
|
||||||
--td-brand-color: #4f5bff !important;
|
--td-brand-color: var(--primary);
|
||||||
--td-brand-color-hover: #6b75ff !important;
|
--td-brand-color-hover: var(--primary-600);
|
||||||
--td-brand-color-active: #3a45e0 !important;
|
--td-brand-color-active: var(--primary-700);
|
||||||
--td-brand-color-disabled: #c2c7ff !important;
|
--td-brand-color-disabled: var(--primary-200);
|
||||||
--td-brand-color-light: #eef0ff !important;
|
--td-brand-color-light: var(--primary-50);
|
||||||
--td-brand-color-light-hover: #e0e3ff !important;
|
--td-brand-color-light-hover: var(--primary-100);
|
||||||
--td-brand-color-light-active: #d6daff !important;
|
--td-brand-color-light-active: var(--primary-200);
|
||||||
|
|
||||||
/* —— 圆角:更现代、更柔 —— */
|
/* —— 圆角:更现代、更柔 —— */
|
||||||
--td-radius-small: 6px !important;
|
--td-radius-small: 6px !important;
|
||||||
@ -49,12 +50,12 @@ body {
|
|||||||
background-color: #f5f7fb;
|
background-color: #f5f7fb;
|
||||||
background-image: radial-gradient(
|
background-image: radial-gradient(
|
||||||
1200px 600px at 100% -10%,
|
1200px 600px at 100% -10%,
|
||||||
rgb(79 91 255 / 6%),
|
color-mix(in srgb, var(--primary) 8%, transparent),
|
||||||
transparent 60%
|
transparent 60%
|
||||||
),
|
),
|
||||||
radial-gradient(
|
radial-gradient(
|
||||||
900px 500px at -10% 110%,
|
900px 500px at -10% 110%,
|
||||||
rgb(124 58 237 / 5%),
|
color-mix(in srgb, var(--primary) 7%, transparent),
|
||||||
transparent 55%
|
transparent 55%
|
||||||
);
|
);
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
@ -94,20 +95,24 @@ body {
|
|||||||
background-color: #fafbff !important;
|
background-color: #fafbff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 主按钮:带品牌渐变,更出彩 */
|
/* 主按钮:带品牌渐变,更出彩(跟随主题色) */
|
||||||
.t-button--theme-primary:not(.t-button--variant-outline):not(
|
.t-button--theme-primary:not(.t-button--variant-outline):not(
|
||||||
.t-button--variant-text
|
.t-button--variant-text
|
||||||
):not(.t-button--variant-dashed) {
|
):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;
|
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;
|
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--theme-primary:not(.t-button--variant-outline):not(
|
||||||
.t-button--variant-text
|
.t-button--variant-text
|
||||||
):not(.t-button--variant-dashed):hover {
|
):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);
|
filter: brightness(1.05);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
@ -142,12 +147,12 @@ html[theme-mode='dark'] body {
|
|||||||
background-color: #16171a;
|
background-color: #16171a;
|
||||||
background-image: radial-gradient(
|
background-image: radial-gradient(
|
||||||
1200px 600px at 100% -10%,
|
1200px 600px at 100% -10%,
|
||||||
rgb(79 91 255 / 12%),
|
color-mix(in srgb, var(--primary) 14%, transparent),
|
||||||
transparent 60%
|
transparent 60%
|
||||||
),
|
),
|
||||||
radial-gradient(
|
radial-gradient(
|
||||||
900px 500px at -10% 110%,
|
900px 500px at -10% 110%,
|
||||||
rgb(124 58 237 / 10%),
|
color-mix(in srgb, var(--primary) 12%, transparent),
|
||||||
transparent 55%
|
transparent 55%
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,13 +93,13 @@ const quickNavs = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作日志',
|
title: '操作日志',
|
||||||
path: '/system/log/operation',
|
path: '/log/operation',
|
||||||
icon: ICONS.log,
|
icon: ICONS.log,
|
||||||
gradient: 'linear-gradient(135deg,#8b5cf6,#a855f7)',
|
gradient: 'linear-gradient(135deg,#8b5cf6,#a855f7)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '登录日志',
|
title: '登录日志',
|
||||||
path: '/system/log/login',
|
path: '/log/login',
|
||||||
icon: ICONS.login,
|
icon: ICONS.login,
|
||||||
gradient: 'linear-gradient(135deg,#f59e0b,#fbbf24)',
|
gradient: 'linear-gradient(135deg,#f59e0b,#fbbf24)',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import type { CommunityItem } from '#/api/house';
|
|||||||
import { h, reactive, ref } from 'vue';
|
import { h, reactive, ref } from 'vue';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
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';
|
import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
|
||||||
|
|
||||||
@ -17,12 +17,17 @@ import {
|
|||||||
updateCommunity,
|
updateCommunity,
|
||||||
} from '#/api/house';
|
} from '#/api/house';
|
||||||
|
|
||||||
// 小区管理:列表 + 新增/编辑弹窗 + 删除(软删除)
|
// 小区管理:列表 + 新增/编辑抽屉 + 删除(软删除)
|
||||||
const { hasAccessByCodes } = useAccess();
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
const dialogOpen = ref(false);
|
|
||||||
const editing = ref<CommunityItem | null>(null);
|
const editing = ref<CommunityItem | null>(null);
|
||||||
|
|
||||||
|
// 新增/编辑小区抽屉(vben 封装抽屉组件)
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
title: '新增小区',
|
||||||
|
onConfirm: submit,
|
||||||
|
});
|
||||||
|
|
||||||
const regionOptions = [
|
const regionOptions = [
|
||||||
'云岩区',
|
'云岩区',
|
||||||
'南明区',
|
'南明区',
|
||||||
@ -72,13 +77,15 @@ function openCreate() {
|
|||||||
propertyFee: 0,
|
propertyFee: 0,
|
||||||
developer: '',
|
developer: '',
|
||||||
});
|
});
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '新增小区' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEdit(row: CommunityItem) {
|
function openEdit(row: CommunityItem) {
|
||||||
editing.value = row;
|
editing.value = row;
|
||||||
Object.assign(form, { ...row });
|
Object.assign(form, { ...row });
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '编辑小区' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
@ -87,7 +94,7 @@ async function submit() {
|
|||||||
? await updateCommunity(editing.value.id, form)
|
? await updateCommunity(editing.value.id, form)
|
||||||
: await createCommunity(form);
|
: await createCommunity(form);
|
||||||
MessagePlugin.success('保存成功');
|
MessagePlugin.success('保存成功');
|
||||||
dialogOpen.value = false;
|
drawerApi.close();
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +185,7 @@ function reset() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<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">
|
<div class="flex items-center gap-2">
|
||||||
<t-input
|
<t-input
|
||||||
v-model="query.keyword"
|
v-model="query.keyword"
|
||||||
@ -209,12 +216,8 @@ function reset() {
|
|||||||
<!-- 小区列表(Vben Vxe Table + TableAction) -->
|
<!-- 小区列表(Vben Vxe Table + TableAction) -->
|
||||||
<Grid />
|
<Grid />
|
||||||
|
|
||||||
<t-dialog
|
<!-- 新增/编辑小区抽屉(vben 封装抽屉组件) -->
|
||||||
v-model:visible="dialogOpen"
|
<Drawer class="w-160">
|
||||||
:header="editing ? '编辑小区' : '新增小区'"
|
|
||||||
width="640px"
|
|
||||||
:on-confirm="submit"
|
|
||||||
>
|
|
||||||
<t-form label-align="right" label-width="90px">
|
<t-form label-align="right" label-width="90px">
|
||||||
<div class="grid grid-cols-2 gap-x-4">
|
<div class="grid grid-cols-2 gap-x-4">
|
||||||
<t-form-item label="小区名称" required>
|
<t-form-item label="小区名称" required>
|
||||||
@ -258,6 +261,6 @@ function reset() {
|
|||||||
</t-form-item>
|
</t-form-item>
|
||||||
</div>
|
</div>
|
||||||
</t-form>
|
</t-form>
|
||||||
</t-dialog>
|
</Drawer>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -75,7 +75,7 @@ function reset() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<div class="mb-3 flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<t-input
|
<t-input
|
||||||
v-model="query.keyword"
|
v-model="query.keyword"
|
||||||
clearable
|
clearable
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import type { ListingItem } from '#/api/house';
|
|||||||
import { h, reactive, ref } from 'vue';
|
import { h, reactive, ref } from 'vue';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
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';
|
import { DialogPlugin, MessagePlugin, Tag } from 'tdesign-vue-next';
|
||||||
|
|
||||||
@ -20,9 +20,14 @@ import {
|
|||||||
// 房源管理:多维筛选 + 多选批量标记(笋盘/低可信/状态)+ 编辑/删除
|
// 房源管理:多维筛选 + 多选批量标记(笋盘/低可信/状态)+ 编辑/删除
|
||||||
const { hasAccessByCodes } = useAccess();
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
const dialogOpen = ref(false);
|
|
||||||
const editing = ref<ListingItem | null>(null);
|
const editing = ref<ListingItem | null>(null);
|
||||||
|
|
||||||
|
// 编辑房源抽屉(vben 封装抽屉组件)
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
title: '编辑房源',
|
||||||
|
onConfirm: submit,
|
||||||
|
});
|
||||||
|
|
||||||
const regionOptions = [
|
const regionOptions = [
|
||||||
'云岩区',
|
'云岩区',
|
||||||
'南明区',
|
'南明区',
|
||||||
@ -69,7 +74,7 @@ async function onBatchMark(field: string, value: number) {
|
|||||||
|
|
||||||
function openEdit(row: ListingItem) {
|
function openEdit(row: ListingItem) {
|
||||||
editing.value = row;
|
editing.value = row;
|
||||||
dialogOpen.value = true;
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
@ -84,7 +89,7 @@ async function submit() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
MessagePlugin.success('保存成功');
|
MessagePlugin.success('保存成功');
|
||||||
dialogOpen.value = false;
|
drawerApi.close();
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +262,7 @@ function reset() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<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
|
<t-input
|
||||||
v-model="query.keyword"
|
v-model="query.keyword"
|
||||||
clearable
|
clearable
|
||||||
@ -323,12 +328,8 @@ function reset() {
|
|||||||
<!-- 房源列表(Vben Vxe Table + TableAction) -->
|
<!-- 房源列表(Vben Vxe Table + TableAction) -->
|
||||||
<Grid />
|
<Grid />
|
||||||
|
|
||||||
<t-dialog
|
<!-- 编辑房源抽屉(vben 封装抽屉组件) -->
|
||||||
v-model:visible="dialogOpen"
|
<Drawer class="w-150">
|
||||||
header="编辑房源"
|
|
||||||
width="560px"
|
|
||||||
:on-confirm="submit"
|
|
||||||
>
|
|
||||||
<t-form v-if="editing" label-align="right" label-width="90px">
|
<t-form v-if="editing" label-align="right" label-width="90px">
|
||||||
<div class="grid grid-cols-2 gap-x-4">
|
<div class="grid grid-cols-2 gap-x-4">
|
||||||
<t-form-item label="挂牌价(万)">
|
<t-form-item label="挂牌价(万)">
|
||||||
@ -358,6 +359,6 @@ function reset() {
|
|||||||
</t-form-item>
|
</t-form-item>
|
||||||
</div>
|
</div>
|
||||||
</t-form>
|
</t-form>
|
||||||
</t-dialog>
|
</Drawer>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -89,7 +89,7 @@ function reset() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<div class="mb-3 flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<t-input
|
<t-input
|
||||||
v-model="query.keyword"
|
v-model="query.keyword"
|
||||||
clearable
|
clearable
|
||||||
|
|||||||
@ -130,7 +130,7 @@ onMounted(load);
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<div class="mb-3 flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<t-button
|
<t-button
|
||||||
v-if="hasAccessByCodes(['recruitment:crawler:trigger'])"
|
v-if="hasAccessByCodes(['recruitment:crawler:trigger'])"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
|
|||||||
@ -162,7 +162,7 @@ function copyUrl() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<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
|
<t-input
|
||||||
v-model="query.keyword"
|
v-model="query.keyword"
|
||||||
clearable
|
clearable
|
||||||
@ -225,7 +225,7 @@ function copyUrl() {
|
|||||||
>
|
>
|
||||||
<div v-if="detailLoading" class="p-6 text-center opacity-60">加载中…</div>
|
<div v-if="detailLoading" class="p-6 text-center opacity-60">加载中…</div>
|
||||||
<div v-else-if="detail">
|
<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">
|
<t-tag theme="primary" variant="light">
|
||||||
{{ detail.categoryName }}
|
{{ detail.categoryName }}
|
||||||
</t-tag>
|
</t-tag>
|
||||||
|
|||||||
@ -3,10 +3,10 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
|||||||
// 推送订阅管理:订阅列表 + 新增/编辑 + 删除 + Bark 推送测试。
|
// 推送订阅管理:订阅列表 + 新增/编辑 + 删除 + Bark 推送测试。
|
||||||
import type { SubscriptionItem } from '#/api/recruitment';
|
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 { 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';
|
import { DialogPlugin, MessagePlugin, Tag } from 'tdesign-vue-next';
|
||||||
|
|
||||||
@ -31,9 +31,11 @@ function categoryLabels(cats: number[] | undefined): string {
|
|||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
// 编辑弹窗
|
// 编辑抽屉(vben 封装抽屉组件)
|
||||||
const dialogOpen = ref(false);
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
const submitting = ref(false);
|
title: '新增订阅',
|
||||||
|
onConfirm: submit,
|
||||||
|
});
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
id: 0,
|
id: 0,
|
||||||
name: '',
|
name: '',
|
||||||
@ -62,7 +64,8 @@ function openCreate() {
|
|||||||
pushTime: '08:00',
|
pushTime: '08:00',
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
});
|
});
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '新增订阅' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEdit(row: SubscriptionItem) {
|
function openEdit(row: SubscriptionItem) {
|
||||||
@ -76,7 +79,8 @@ function openEdit(row: SubscriptionItem) {
|
|||||||
pushTime: row.pushTime || '08:00',
|
pushTime: row.pushTime || '08:00',
|
||||||
enabled: row.enabled,
|
enabled: row.enabled,
|
||||||
});
|
});
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '编辑订阅' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
@ -84,7 +88,7 @@ async function submit() {
|
|||||||
MessagePlugin.warning('请填写名称与设备 Key');
|
MessagePlugin.warning('请填写名称与设备 Key');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
submitting.value = true;
|
drawerApi.lock(true);
|
||||||
try {
|
try {
|
||||||
await saveSubscription({
|
await saveSubscription({
|
||||||
id: form.id || undefined,
|
id: form.id || undefined,
|
||||||
@ -97,10 +101,10 @@ async function submit() {
|
|||||||
enabled: form.enabled,
|
enabled: form.enabled,
|
||||||
});
|
});
|
||||||
MessagePlugin.success(form.id ? '更新成功' : '新增成功');
|
MessagePlugin.success(form.id ? '更新成功' : '新增成功');
|
||||||
dialogOpen.value = false;
|
drawerApi.close();
|
||||||
await load();
|
await load();
|
||||||
} finally {
|
} finally {
|
||||||
submitting.value = false;
|
drawerApi.lock(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +246,7 @@ onMounted(load);
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<div class="mb-3 flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<t-button
|
<t-button
|
||||||
v-if="hasAccessByCodes(['recruitment:push:save'])"
|
v-if="hasAccessByCodes(['recruitment:push:save'])"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
@ -255,13 +259,8 @@ onMounted(load);
|
|||||||
<!-- 订阅列表(Vben Vxe Table + TableAction) -->
|
<!-- 订阅列表(Vben Vxe Table + TableAction) -->
|
||||||
<Grid />
|
<Grid />
|
||||||
|
|
||||||
<t-dialog
|
<!-- 新增/编辑订阅抽屉(vben 封装抽屉组件) -->
|
||||||
v-model:visible="dialogOpen"
|
<Drawer>
|
||||||
:header="form.id ? '编辑订阅' : '新增订阅'"
|
|
||||||
width="560px"
|
|
||||||
:confirm-btn="{ loading: submitting }"
|
|
||||||
@confirm="submit"
|
|
||||||
>
|
|
||||||
<t-form label-align="right" label-width="90px">
|
<t-form label-align="right" label-width="90px">
|
||||||
<t-form-item label="名称">
|
<t-form-item label="名称">
|
||||||
<t-input v-model="form.name" placeholder="如:每日贵阳公务员" />
|
<t-input v-model="form.name" placeholder="如:每日贵阳公务员" />
|
||||||
@ -301,6 +300,6 @@ onMounted(load);
|
|||||||
<t-switch v-model="form.enabled" :custom-value="[1, 0]" />
|
<t-switch v-model="form.enabled" :custom-value="[1, 0]" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</t-dialog>
|
</Drawer>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import type { AdminItem, RoleItem } from '#/api/system';
|
|||||||
import { h, onMounted, reactive, ref } from 'vue';
|
import { h, onMounted, reactive, ref } from 'vue';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
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';
|
import { DialogPlugin, MessagePlugin, Tag } from 'tdesign-vue-next';
|
||||||
|
|
||||||
@ -23,9 +23,14 @@ import {
|
|||||||
const { hasAccessByCodes } = useAccess();
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
const roleOptions = ref<{ label: string; value: number }[]>([]);
|
const roleOptions = ref<{ label: string; value: number }[]>([]);
|
||||||
const dialogOpen = ref(false);
|
|
||||||
const editing = ref<AdminItem | null>(null);
|
const editing = ref<AdminItem | null>(null);
|
||||||
|
|
||||||
|
// 新增/编辑人员抽屉(vben 封装抽屉组件)
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
title: '新增人员',
|
||||||
|
onConfirm: submit,
|
||||||
|
});
|
||||||
|
|
||||||
// 筛选条件(分页由 grid 自动管理)
|
// 筛选条件(分页由 grid 自动管理)
|
||||||
const query = reactive({ keyword: '' });
|
const query = reactive({ keyword: '' });
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@ -53,7 +58,8 @@ function openCreate() {
|
|||||||
status: 1,
|
status: 1,
|
||||||
roleIds: [],
|
roleIds: [],
|
||||||
});
|
});
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '新增人员' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEdit(row: AdminItem) {
|
function openEdit(row: AdminItem) {
|
||||||
@ -65,7 +71,8 @@ function openEdit(row: AdminItem) {
|
|||||||
status: row.status,
|
status: row.status,
|
||||||
roleIds: row.roleIds ?? [],
|
roleIds: row.roleIds ?? [],
|
||||||
});
|
});
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '编辑人员' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
@ -83,7 +90,7 @@ async function submit() {
|
|||||||
roleIds: form.roleIds,
|
roleIds: form.roleIds,
|
||||||
});
|
});
|
||||||
MessagePlugin.success('保存成功');
|
MessagePlugin.success('保存成功');
|
||||||
dialogOpen.value = false;
|
drawerApi.close();
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +227,7 @@ onMounted(loadRoles);
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<t-card :bordered="false" class="wb-filter-card">
|
<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">
|
<div class="flex items-center gap-2">
|
||||||
<t-input
|
<t-input
|
||||||
v-model="query.keyword"
|
v-model="query.keyword"
|
||||||
@ -246,11 +253,8 @@ onMounted(loadRoles);
|
|||||||
<Grid />
|
<Grid />
|
||||||
</t-card>
|
</t-card>
|
||||||
|
|
||||||
<t-dialog
|
<!-- 新增/编辑人员抽屉(vben 封装抽屉组件) -->
|
||||||
v-model:visible="dialogOpen"
|
<Drawer>
|
||||||
:header="editing ? '编辑人员' : '新增人员'"
|
|
||||||
:on-confirm="submit"
|
|
||||||
>
|
|
||||||
<t-form label-align="right" label-width="80px">
|
<t-form label-align="right" label-width="80px">
|
||||||
<t-form-item label="用户名" required>
|
<t-form-item label="用户名" required>
|
||||||
<t-input
|
<t-input
|
||||||
@ -281,6 +285,6 @@ onMounted(loadRoles);
|
|||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</t-dialog>
|
</Drawer>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { LogItem } from '#/api/system';
|
import type { LogItem } from '#/api/system';
|
||||||
|
|
||||||
import { h, reactive, ref } from 'vue';
|
import { h, reactive, ref } from 'vue';
|
||||||
@ -8,7 +9,7 @@ import { Page } from '@vben/common-ui';
|
|||||||
|
|
||||||
import { MessagePlugin, Tag } from 'tdesign-vue-next';
|
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';
|
import { getSystemLogList } from '#/api/system';
|
||||||
|
|
||||||
// 操作日志页面:Vben Vxe Table 分页列表 + 多维筛选(筛选区保留 TDesign 组件,行为与原版一致)
|
// 操作日志页面:Vben Vxe Table 分页列表 + 多维筛选(筛选区保留 TDesign 组件,行为与原版一致)
|
||||||
@ -47,6 +48,14 @@ const sortMap: Record<string, { orderBy: string; orderDir: string }> = {
|
|||||||
// HTTP 方法可选项
|
// HTTP 方法可选项
|
||||||
const METHOD_OPTIONS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
|
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 = {
|
const gridOptions: VxeTableGridOptions = {
|
||||||
columns: [
|
columns: [
|
||||||
{ type: 'seq', width: 50, title: '#' },
|
{ type: 'seq', width: 50, title: '#' },
|
||||||
@ -62,14 +71,7 @@ const gridOptions: VxeTableGridOptions = {
|
|||||||
h(
|
h(
|
||||||
Tag,
|
Tag,
|
||||||
{
|
{
|
||||||
theme:
|
theme: methodTheme(row.method),
|
||||||
row.method === 'POST'
|
|
||||||
? 'primary'
|
|
||||||
: row.method === 'GET'
|
|
||||||
? 'success'
|
|
||||||
: row.method === 'PUT'
|
|
||||||
? 'warning'
|
|
||||||
: 'danger',
|
|
||||||
variant: 'light',
|
variant: 'light',
|
||||||
size: 'small',
|
size: 'small',
|
||||||
},
|
},
|
||||||
@ -224,7 +226,7 @@ function copyParam(row: LogItem) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<t-card :bordered="false" class="wb-filter-card">
|
<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
|
<t-date-range-picker
|
||||||
v-model="timeRange"
|
v-model="timeRange"
|
||||||
value-type="YYYY-MM-DD"
|
value-type="YYYY-MM-DD"
|
||||||
|
|||||||
@ -108,7 +108,7 @@ function reset() {
|
|||||||
|
|
||||||
<t-card :bordered="false" class="wb-filter-card">
|
<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">
|
<div class="flex items-center gap-2">
|
||||||
<t-input
|
<t-input
|
||||||
v-model="query.username"
|
v-model="query.username"
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import type { MenuNode } from '#/api/system';
|
|||||||
import { nextTick, onMounted, reactive, ref } from 'vue';
|
import { nextTick, onMounted, reactive, ref } from 'vue';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
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';
|
import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
|
||||||
|
|
||||||
@ -17,9 +17,14 @@ const loading = ref(false);
|
|||||||
const tree = ref<MenuNode[]>([]);
|
const tree = ref<MenuNode[]>([]);
|
||||||
// t-enhanced-table 实例:异步数据加载完成后手动展开全部节点
|
// t-enhanced-table 实例:异步数据加载完成后手动展开全部节点
|
||||||
const tableRef = ref<any>(null);
|
const tableRef = ref<any>(null);
|
||||||
const dialogOpen = ref(false);
|
|
||||||
const editing = ref<MenuNode | null>(null);
|
const editing = ref<MenuNode | null>(null);
|
||||||
|
|
||||||
|
// 新增/编辑菜单抽屉(vben 封装抽屉组件)
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
title: '新增菜单',
|
||||||
|
onConfirm: submit,
|
||||||
|
});
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
parentId: 0,
|
parentId: 0,
|
||||||
name: '',
|
name: '',
|
||||||
@ -60,7 +65,8 @@ function openCreate(parent?: MenuNode) {
|
|||||||
status: 1,
|
status: 1,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
});
|
});
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '新增菜单' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEdit(row: MenuNode) {
|
function openEdit(row: MenuNode) {
|
||||||
@ -77,17 +83,17 @@ function openEdit(row: MenuNode) {
|
|||||||
status: row.status,
|
status: row.status,
|
||||||
hidden: row.hidden,
|
hidden: row.hidden,
|
||||||
});
|
});
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '编辑菜单' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
if (editing.value) {
|
// 编辑走更新、新增走创建
|
||||||
await updateMenu(editing.value.id, { ...form });
|
editing.value
|
||||||
} else {
|
? await updateMenu(editing.value.id, { ...form })
|
||||||
await createMenu({ ...form });
|
: await createMenu({ ...form });
|
||||||
}
|
|
||||||
MessagePlugin.success('保存成功');
|
MessagePlugin.success('保存成功');
|
||||||
dialogOpen.value = false;
|
drawerApi.close();
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +130,7 @@ onMounted(load);
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<t-card :bordered="false" class="wb-filter-card">
|
<t-card :bordered="false" class="wb-filter-card">
|
||||||
<div class="mb-3 flex justify-end">
|
<div class="flex justify-end">
|
||||||
<t-button
|
<t-button
|
||||||
v-if="hasAccessByCodes(['system:menu:create'])"
|
v-if="hasAccessByCodes(['system:menu:create'])"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
@ -154,9 +160,7 @@ onMounted(load);
|
|||||||
{{ row.name }}
|
{{ row.name }}
|
||||||
</template>
|
</template>
|
||||||
<template #typeText="{ row }">
|
<template #typeText="{ row }">
|
||||||
{{
|
{{ row.type === 1 ? '菜单' : '按钮' }}
|
||||||
row.type === 1 ? '菜单' : '按钮'
|
|
||||||
}}
|
|
||||||
</template>
|
</template>
|
||||||
<template #op="{ row }">
|
<template #op="{ row }">
|
||||||
<t-button
|
<t-button
|
||||||
@ -190,11 +194,8 @@ onMounted(load);
|
|||||||
</t-enhanced-table>
|
</t-enhanced-table>
|
||||||
</t-card>
|
</t-card>
|
||||||
|
|
||||||
<t-dialog
|
<!-- 新增/编辑菜单抽屉(vben 封装抽屉组件) -->
|
||||||
v-model:visible="dialogOpen"
|
<Drawer>
|
||||||
:header="editing ? '编辑菜单' : '新增菜单'"
|
|
||||||
:on-confirm="submit"
|
|
||||||
>
|
|
||||||
<t-form label-align="right" label-width="80px">
|
<t-form label-align="right" label-width="80px">
|
||||||
<t-form-item label="上级 ID">
|
<t-form-item label="上级 ID">
|
||||||
<t-input-number v-model="form.parentId" :min="0" />
|
<t-input-number v-model="form.parentId" :min="0" />
|
||||||
@ -239,6 +240,6 @@ onMounted(load);
|
|||||||
<t-switch v-model="form.hidden" />
|
<t-switch v-model="form.hidden" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</t-dialog>
|
</Drawer>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import type { MenuNode, RoleItem } from '#/api/system';
|
|||||||
import { h, onMounted, reactive, ref } from 'vue';
|
import { h, onMounted, reactive, ref } from 'vue';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
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';
|
import { DialogPlugin, MessagePlugin, Tag } from 'tdesign-vue-next';
|
||||||
|
|
||||||
@ -22,9 +22,14 @@ import {
|
|||||||
const { hasAccessByCodes } = useAccess();
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
const menuTree = ref<any[]>([]);
|
const menuTree = ref<any[]>([]);
|
||||||
const dialogOpen = ref(false);
|
|
||||||
const editing = ref<null | RoleItem>(null);
|
const editing = ref<null | RoleItem>(null);
|
||||||
|
|
||||||
|
// 新增/编辑角色抽屉(vben 封装抽屉组件)
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
title: '新增角色',
|
||||||
|
onConfirm: submit,
|
||||||
|
});
|
||||||
|
|
||||||
// 筛选条件(分页由 grid 自动管理)
|
// 筛选条件(分页由 grid 自动管理)
|
||||||
const query = reactive({ keyword: '' });
|
const query = reactive({ keyword: '' });
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@ -51,7 +56,8 @@ async function loadMenus() {
|
|||||||
function openCreate() {
|
function openCreate() {
|
||||||
editing.value = null;
|
editing.value = null;
|
||||||
Object.assign(form, { code: '', name: '', status: 1, menuIds: [] });
|
Object.assign(form, { code: '', name: '', status: 1, menuIds: [] });
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '新增角色' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEdit(row: RoleItem) {
|
function openEdit(row: RoleItem) {
|
||||||
@ -62,7 +68,8 @@ function openEdit(row: RoleItem) {
|
|||||||
status: row.status,
|
status: row.status,
|
||||||
menuIds: row.menuIds ?? [],
|
menuIds: row.menuIds ?? [],
|
||||||
});
|
});
|
||||||
dialogOpen.value = true;
|
drawerApi.setState({ title: '编辑角色' });
|
||||||
|
drawerApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
@ -80,7 +87,7 @@ async function submit() {
|
|||||||
menuIds: form.menuIds,
|
menuIds: form.menuIds,
|
||||||
});
|
});
|
||||||
MessagePlugin.success('保存成功');
|
MessagePlugin.success('保存成功');
|
||||||
dialogOpen.value = false;
|
drawerApi.close();
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +192,7 @@ onMounted(loadMenus);
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<t-card :bordered="false" class="wb-filter-card">
|
<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">
|
<div class="flex items-center gap-2">
|
||||||
<t-input
|
<t-input
|
||||||
v-model="query.keyword"
|
v-model="query.keyword"
|
||||||
@ -211,12 +218,8 @@ onMounted(loadMenus);
|
|||||||
<Grid />
|
<Grid />
|
||||||
</t-card>
|
</t-card>
|
||||||
|
|
||||||
<t-dialog
|
<!-- 新增/编辑角色抽屉(vben 封装抽屉组件) -->
|
||||||
v-model:visible="dialogOpen"
|
<Drawer>
|
||||||
:header="editing ? '编辑角色' : '新增角色'"
|
|
||||||
width="560px"
|
|
||||||
:on-confirm="submit"
|
|
||||||
>
|
|
||||||
<t-form label-align="right" label-width="80px">
|
<t-form label-align="right" label-width="80px">
|
||||||
<t-form-item label="角色编码" required>
|
<t-form-item label="角色编码" required>
|
||||||
<t-input
|
<t-input
|
||||||
@ -242,6 +245,6 @@ onMounted(loadMenus);
|
|||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</t-dialog>
|
</Drawer>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</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: {
|
theme: {
|
||||||
builtinType: 'default',
|
builtinType: 'default',
|
||||||
colorDestructive: 'hsl(348 100% 61%)',
|
colorDestructive: 'hsl(348 100% 61%)',
|
||||||
colorPrimary: 'hsl(212 100% 45%)',
|
colorPrimary: 'hsl(235 100% 65%)',
|
||||||
colorSuccess: 'hsl(144 57% 58%)',
|
colorSuccess: 'hsl(144 57% 58%)',
|
||||||
colorWarning: 'hsl(42 84% 61%)',
|
colorWarning: 'hsl(42 84% 61%)',
|
||||||
mode: 'dark',
|
mode: 'dark',
|
||||||
@ -140,8 +140,9 @@ const defaultPreferences: Preferences = {
|
|||||||
fullscreenButtonPosition: 'header',
|
fullscreenButtonPosition: 'header',
|
||||||
globalSearch: true,
|
globalSearch: true,
|
||||||
globalSearchButtonPosition: 'header',
|
globalSearchButtonPosition: 'header',
|
||||||
languageToggle: true,
|
// 移除国际化:不再显示语言切换按钮
|
||||||
languageToggleButtonPosition: 'header',
|
languageToggle: false,
|
||||||
|
languageToggleButtonPosition: 'none',
|
||||||
lockScreen: true,
|
lockScreen: true,
|
||||||
lockScreenButtonPosition: 'header',
|
lockScreenButtonPosition: 'header',
|
||||||
logoutButtonPosition: 'header',
|
logoutButtonPosition: 'header',
|
||||||
@ -158,7 +159,6 @@ const defaultPreferences: Preferences = {
|
|||||||
'globalSearch',
|
'globalSearch',
|
||||||
'preferences',
|
'preferences',
|
||||||
'themeToggle',
|
'themeToggle',
|
||||||
'languageToggle',
|
|
||||||
'timezone',
|
'timezone',
|
||||||
'fullscreen',
|
'fullscreen',
|
||||||
'refresh',
|
'refresh',
|
||||||
|
|||||||
@ -9,7 +9,7 @@ interface BuiltinThemePreset {
|
|||||||
|
|
||||||
const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
|
const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
|
||||||
{
|
{
|
||||||
color: 'hsl(212 100% 45%)',
|
color: 'hsl(235 100% 65%)',
|
||||||
type: 'default',
|
type: 'default',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { preferences } from '@vben/preferences';
|
|||||||
import {
|
import {
|
||||||
AuthenticationColorToggle,
|
AuthenticationColorToggle,
|
||||||
AuthenticationLayoutToggle,
|
AuthenticationLayoutToggle,
|
||||||
LanguageToggle,
|
|
||||||
ThemeToggle,
|
ThemeToggle,
|
||||||
} from '../widgets';
|
} from '../widgets';
|
||||||
|
|
||||||
@ -21,12 +20,12 @@ defineOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
toolbarList: () => ['color', 'language', 'layout', 'theme'],
|
// 移除国际化后,语言切换不再展示
|
||||||
|
toolbarList: () => ['color', 'layout', 'theme'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const showColor = computed(() => props.toolbarList.includes('color'));
|
const showColor = computed(() => props.toolbarList.includes('color'));
|
||||||
const showLayout = computed(() => props.toolbarList.includes('layout'));
|
const showLayout = computed(() => props.toolbarList.includes('layout'));
|
||||||
const showLanguage = computed(() => props.toolbarList.includes('language'));
|
|
||||||
const showTheme = computed(() => props.toolbarList.includes('theme'));
|
const showTheme = computed(() => props.toolbarList.includes('theme'));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -42,8 +41,7 @@ const showTheme = computed(() => props.toolbarList.includes('theme'));
|
|||||||
<AuthenticationColorToggle v-if="showColor" />
|
<AuthenticationColorToggle v-if="showColor" />
|
||||||
<AuthenticationLayoutToggle v-if="showLayout" />
|
<AuthenticationLayoutToggle v-if="showLayout" />
|
||||||
</div>
|
</div>
|
||||||
<!-- Always show Language and Theme toggles -->
|
<!-- 仅展示主题切换 -->
|
||||||
<LanguageToggle v-if="showLanguage && preferences.widget.languageToggle" />
|
|
||||||
<ThemeToggle v-if="showTheme && preferences.widget.themeToggle" />
|
<ThemeToggle v-if="showTheme && preferences.widget.themeToggle" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import { useMagicKeys, whenever } from '@vueuse/core';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
GlobalSearch,
|
GlobalSearch,
|
||||||
LanguageToggle,
|
|
||||||
LockScreenModal,
|
LockScreenModal,
|
||||||
Notification,
|
Notification,
|
||||||
PreferencesButton,
|
PreferencesButton,
|
||||||
@ -163,12 +162,6 @@ const rightSlots = computed(() => {
|
|||||||
preferences.widget.themeToggleButtonPosition === 'header',
|
preferences.widget.themeToggleButtonPosition === 'header',
|
||||||
slotName: 'theme-toggle',
|
slotName: 'theme-toggle',
|
||||||
},
|
},
|
||||||
languageToggle: {
|
|
||||||
visible:
|
|
||||||
preferences.widget.languageToggle &&
|
|
||||||
preferences.widget.languageToggleButtonPosition === 'header',
|
|
||||||
slotName: 'language-toggle',
|
|
||||||
},
|
|
||||||
timezone: {
|
timezone: {
|
||||||
visible:
|
visible:
|
||||||
preferences.widget.timezone &&
|
preferences.widget.timezone &&
|
||||||
@ -337,14 +330,6 @@ function clearPreferencesAndLogout() {
|
|||||||
{{ $t('preferences.theme.title') }}
|
{{ $t('preferences.theme.title') }}
|
||||||
</VbenTooltip>
|
</VbenTooltip>
|
||||||
</template>
|
</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'">
|
<template v-else-if="slot.name === 'fullscreen'">
|
||||||
<VbenFullScreen
|
<VbenFullScreen
|
||||||
class="mr-1"
|
class="mr-1"
|
||||||
|
|||||||
@ -279,7 +279,7 @@ useEventListener('mousemove', () => {
|
|||||||
"
|
"
|
||||||
:data-index="index"
|
:data-index="index"
|
||||||
:data-search-item="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"
|
@click="handleEnter"
|
||||||
@mouseenter="handleMouseenter"
|
@mouseenter="handleMouseenter"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -2,7 +2,6 @@ export { default as Breadcrumb } from './breadcrumb.vue';
|
|||||||
export * from './check-updates';
|
export * from './check-updates';
|
||||||
export { default as AuthenticationColorToggle } from './color-toggle.vue';
|
export { default as AuthenticationColorToggle } from './color-toggle.vue';
|
||||||
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 './notification';
|
export * from './notification';
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<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 { $t } from '@vben/locales';
|
||||||
import { useTimezoneStore } from '@vben/stores';
|
import { useTimezoneStore } from '@vben/stores';
|
||||||
|
|
||||||
@ -15,7 +12,6 @@ defineOptions({
|
|||||||
name: 'PreferenceGeneralConfig',
|
name: 'PreferenceGeneralConfig',
|
||||||
});
|
});
|
||||||
|
|
||||||
const appLocale = defineModel<string>('appLocale');
|
|
||||||
const appTimezone = defineModel<string>('appTimezone');
|
const appTimezone = defineModel<string>('appTimezone');
|
||||||
const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
|
const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
|
||||||
const appWatermark = defineModel<boolean>('appWatermark');
|
const appWatermark = defineModel<boolean>('appWatermark');
|
||||||
@ -26,12 +22,6 @@ const appEnableCopyPreferences = defineModel<boolean>(
|
|||||||
);
|
);
|
||||||
const timezoneStore = useTimezoneStore();
|
const timezoneStore = useTimezoneStore();
|
||||||
|
|
||||||
const languageList = ref<LanguageOption[]>([]);
|
|
||||||
const unsubscribe = onSupportLanguagesChange((langs) => {
|
|
||||||
languageList.value = [...langs];
|
|
||||||
});
|
|
||||||
onUnmounted(unsubscribe);
|
|
||||||
|
|
||||||
const timezoneOptionsRef = ref<
|
const timezoneOptionsRef = ref<
|
||||||
{
|
{
|
||||||
label: string;
|
label: string;
|
||||||
@ -50,9 +40,6 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SelectItem v-model="appLocale" :items="languageList">
|
|
||||||
{{ $t('preferences.language') }}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem v-model="appTimezone" :items="timezoneOptionsRef">
|
<SelectItem v-model="appTimezone" :items="timezoneOptionsRef">
|
||||||
{{ $t('preferences.timezone') }}
|
{{ $t('preferences.timezone') }}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
|
|||||||
@ -19,9 +19,6 @@ const widgetGlobalSearchButtonPosition = defineModel<string>(
|
|||||||
const widgetFullscreenButtonPosition = defineModel<string>(
|
const widgetFullscreenButtonPosition = defineModel<string>(
|
||||||
'widgetFullscreenButtonPosition',
|
'widgetFullscreenButtonPosition',
|
||||||
);
|
);
|
||||||
const widgetLanguageToggleButtonPosition = defineModel<string>(
|
|
||||||
'widgetLanguageToggleButtonPosition',
|
|
||||||
);
|
|
||||||
const widgetNotificationButtonPosition = defineModel<string>(
|
const widgetNotificationButtonPosition = defineModel<string>(
|
||||||
'widgetNotificationButtonPosition',
|
'widgetNotificationButtonPosition',
|
||||||
);
|
);
|
||||||
@ -91,7 +88,6 @@ const positionMap: Record<string, string> = {
|
|||||||
globalSearch: 'widgetGlobalSearchButtonPosition',
|
globalSearch: 'widgetGlobalSearchButtonPosition',
|
||||||
preferences: 'appPreferencesButtonPosition',
|
preferences: 'appPreferencesButtonPosition',
|
||||||
themeToggle: 'widgetThemeToggleButtonPosition',
|
themeToggle: 'widgetThemeToggleButtonPosition',
|
||||||
languageToggle: 'widgetLanguageToggleButtonPosition',
|
|
||||||
timezone: 'widgetTimezoneButtonPosition',
|
timezone: 'widgetTimezoneButtonPosition',
|
||||||
fullscreen: 'widgetFullscreenButtonPosition',
|
fullscreen: 'widgetFullscreenButtonPosition',
|
||||||
refresh: 'widgetRefreshButtonPosition',
|
refresh: 'widgetRefreshButtonPosition',
|
||||||
@ -104,7 +100,6 @@ const labelMap: Record<string, string> = {
|
|||||||
globalSearch: 'preferences.widget.globalSearch',
|
globalSearch: 'preferences.widget.globalSearch',
|
||||||
preferences: 'preferences.title',
|
preferences: 'preferences.title',
|
||||||
themeToggle: 'preferences.widget.themeToggle',
|
themeToggle: 'preferences.widget.themeToggle',
|
||||||
languageToggle: 'preferences.widget.languageToggle',
|
|
||||||
timezone: 'preferences.widget.timezone',
|
timezone: 'preferences.widget.timezone',
|
||||||
fullscreen: 'preferences.widget.fullscreen',
|
fullscreen: 'preferences.widget.fullscreen',
|
||||||
refresh: 'preferences.widget.refresh',
|
refresh: 'preferences.widget.refresh',
|
||||||
@ -131,7 +126,6 @@ function getPosition(
|
|||||||
const modelMap: Record<string, any> = {
|
const modelMap: Record<string, any> = {
|
||||||
widgetGlobalSearchButtonPosition,
|
widgetGlobalSearchButtonPosition,
|
||||||
widgetFullscreenButtonPosition,
|
widgetFullscreenButtonPosition,
|
||||||
widgetLanguageToggleButtonPosition,
|
|
||||||
widgetNotificationButtonPosition,
|
widgetNotificationButtonPosition,
|
||||||
widgetThemeToggleButtonPosition,
|
widgetThemeToggleButtonPosition,
|
||||||
widgetLockScreenButtonPosition,
|
widgetLockScreenButtonPosition,
|
||||||
@ -156,7 +150,6 @@ function handleUpdatePosition(
|
|||||||
const modelMap: Record<string, any> = {
|
const modelMap: Record<string, any> = {
|
||||||
widgetGlobalSearchButtonPosition,
|
widgetGlobalSearchButtonPosition,
|
||||||
widgetFullscreenButtonPosition,
|
widgetFullscreenButtonPosition,
|
||||||
widgetLanguageToggleButtonPosition,
|
|
||||||
widgetNotificationButtonPosition,
|
widgetNotificationButtonPosition,
|
||||||
widgetThemeToggleButtonPosition,
|
widgetThemeToggleButtonPosition,
|
||||||
widgetLockScreenButtonPosition,
|
widgetLockScreenButtonPosition,
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { SupportedLanguagesType } from '@vben/locales';
|
|
||||||
import type { CustomPreferencesRecord } from '@vben/preferences';
|
import type { CustomPreferencesRecord } from '@vben/preferences';
|
||||||
import type {
|
import type {
|
||||||
BreadcrumbStyleType,
|
BreadcrumbStyleType,
|
||||||
@ -64,7 +63,6 @@ const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
|
|||||||
|
|
||||||
const message = globalShareState.getMessage();
|
const message = globalShareState.getMessage();
|
||||||
|
|
||||||
const appLocale = defineModel<SupportedLanguagesType>('appLocale');
|
|
||||||
const appTimezone = defineModel<string>('appTimezone');
|
const appTimezone = defineModel<string>('appTimezone');
|
||||||
const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
|
const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
|
||||||
const appLayout = defineModel<LayoutType>('appLayout');
|
const appLayout = defineModel<LayoutType>('appLayout');
|
||||||
@ -180,9 +178,6 @@ const widgetGlobalSearchButtonPosition = defineModel<string>(
|
|||||||
const widgetFullscreenButtonPosition = defineModel<string>(
|
const widgetFullscreenButtonPosition = defineModel<string>(
|
||||||
'widgetFullscreenButtonPosition',
|
'widgetFullscreenButtonPosition',
|
||||||
);
|
);
|
||||||
const widgetLanguageToggleButtonPosition = defineModel<string>(
|
|
||||||
'widgetLanguageToggleButtonPosition',
|
|
||||||
);
|
|
||||||
const widgetNotificationButtonPosition = defineModel<string>(
|
const widgetNotificationButtonPosition = defineModel<string>(
|
||||||
'widgetNotificationButtonPosition',
|
'widgetNotificationButtonPosition',
|
||||||
);
|
);
|
||||||
@ -381,7 +376,6 @@ function handleCustomPreferencesUpdate(updates: CustomPreferencesRecord) {
|
|||||||
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-timezone="appTimezone"
|
v-model:app-timezone="appTimezone"
|
||||||
v-model:app-watermark="appWatermark"
|
v-model:app-watermark="appWatermark"
|
||||||
v-model:app-watermark-content="appWatermarkContent"
|
v-model:app-watermark-content="appWatermarkContent"
|
||||||
@ -508,9 +502,6 @@ function handleCustomPreferencesUpdate(updates: CustomPreferencesRecord) {
|
|||||||
v-model:widget-global-search-button-position="
|
v-model:widget-global-search-button-position="
|
||||||
widgetGlobalSearchButtonPosition
|
widgetGlobalSearchButtonPosition
|
||||||
"
|
"
|
||||||
v-model:widget-language-toggle-button-position="
|
|
||||||
widgetLanguageToggleButtonPosition
|
|
||||||
"
|
|
||||||
v-model:widget-lock-screen-button-position="
|
v-model:widget-lock-screen-button-position="
|
||||||
widgetLockScreenButtonPosition
|
widgetLockScreenButtonPosition
|
||||||
"
|
"
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
@ -58,9 +58,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 {
|
||||||
|
|||||||
@ -1,23 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
|
|
||||||
import type { LanguageOption } from '@vben/constants';
|
|
||||||
import type { AnyFunction } from '@vben/types';
|
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 { useHoverToggle, useRefresh } from '@vben/hooks';
|
||||||
import {
|
import {
|
||||||
createIconifyIcon,
|
createIconifyIcon,
|
||||||
Languages,
|
|
||||||
LockKeyhole,
|
LockKeyhole,
|
||||||
LogOut,
|
LogOut,
|
||||||
RotateCw,
|
RotateCw,
|
||||||
Search,
|
Search,
|
||||||
Settings,
|
Settings,
|
||||||
} from '@vben/icons';
|
} from '@vben/icons';
|
||||||
import { $t, loadLocaleMessages } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
import {
|
import {
|
||||||
preferences,
|
preferences,
|
||||||
updatePreferences,
|
updatePreferences,
|
||||||
@ -167,12 +164,6 @@ const showThemeToggleInDropdown = computed(
|
|||||||
preferences.widget.themeToggleButtonPosition === 'user-dropdown',
|
preferences.widget.themeToggleButtonPosition === 'user-dropdown',
|
||||||
);
|
);
|
||||||
|
|
||||||
const showLanguageToggleInDropdown = computed(
|
|
||||||
() =>
|
|
||||||
preferences.widget.languageToggle &&
|
|
||||||
preferences.widget.languageToggleButtonPosition === 'user-dropdown',
|
|
||||||
);
|
|
||||||
|
|
||||||
const showTimezoneInDropdown = computed(
|
const showTimezoneInDropdown = computed(
|
||||||
() =>
|
() =>
|
||||||
preferences.widget.timezone &&
|
preferences.widget.timezone &&
|
||||||
@ -203,7 +194,6 @@ const hasAnyInDropdown = computed(
|
|||||||
showLogoutInDropdown.value ||
|
showLogoutInDropdown.value ||
|
||||||
showGlobalSearchInDropdown.value ||
|
showGlobalSearchInDropdown.value ||
|
||||||
showThemeToggleInDropdown.value ||
|
showThemeToggleInDropdown.value ||
|
||||||
showLanguageToggleInDropdown.value ||
|
|
||||||
showTimezoneInDropdown.value ||
|
showTimezoneInDropdown.value ||
|
||||||
showFullscreenInDropdown.value ||
|
showFullscreenInDropdown.value ||
|
||||||
showNotificationInDropdown.value ||
|
showNotificationInDropdown.value ||
|
||||||
@ -277,26 +267,6 @@ function handleNotificationSelect(event?: Event) {
|
|||||||
refNotification.value?.toggle();
|
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) {
|
if (preferences.shortcutKeys.enable) {
|
||||||
const keys = useMagicKeys();
|
const keys = useMagicKeys();
|
||||||
const logoutKey = keys['Alt+KeyQ'];
|
const logoutKey = keys['Alt+KeyQ'];
|
||||||
@ -448,7 +418,6 @@ if (preferences.shortcutKeys.enable) {
|
|||||||
v-if="
|
v-if="
|
||||||
showGlobalSearchInDropdown ||
|
showGlobalSearchInDropdown ||
|
||||||
showThemeToggleInDropdown ||
|
showThemeToggleInDropdown ||
|
||||||
showLanguageToggleInDropdown ||
|
|
||||||
showTimezoneInDropdown ||
|
showTimezoneInDropdown ||
|
||||||
showFullscreenInDropdown ||
|
showFullscreenInDropdown ||
|
||||||
showNotificationInDropdown ||
|
showNotificationInDropdown ||
|
||||||
@ -474,32 +443,6 @@ if (preferences.shortcutKeys.enable) {
|
|||||||
<ThemeToggle class="mr-2" />
|
<ThemeToggle class="mr-2" />
|
||||||
{{ $t('preferences.theme.title') }}
|
{{ $t('preferences.theme.title') }}
|
||||||
</DropdownMenuItem>
|
</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
|
<DropdownMenuItem
|
||||||
v-if="showTimezoneInDropdown"
|
v-if="showTimezoneInDropdown"
|
||||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
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