diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index ef2165ab3c8..4df7d12914d 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -69,6 +69,12 @@ src/api/ `import WorkspaceApi from '@/api/admin/system/workspace'`;System 登录设置使用 `import AuthSettingApi from '@/api/admin/system/settings/auth-setting'`。 +### 当前账号密码 + +`admin/auth/current-user.ts` 的 `postCurrentUserPassword` 向 `/user/current/reset_password` +提交 RSA 加密的 `{ encryptedData }`。头像菜单的 `layout/avatar-dropdown/ChangePasswordDialog.vue` +负责新密码与确认密码校验、加密和提交,成功后清除本地登录凭据并跳转登录页。 + ### 四类特殊资源 API `application`、`knowledge`、`model`、`tool` 是需要同时考虑 Workspace、System 资源管理和 diff --git a/ui/src/api/admin/auth/current-user.ts b/ui/src/api/admin/auth/current-user.ts index f6ac52656c1..3e9043097ca 100644 --- a/ui/src/api/admin/auth/current-user.ts +++ b/ui/src/api/admin/auth/current-user.ts @@ -1,7 +1,8 @@ /** 提供 Admin 登录后的当前用户接口。 */ -import { get } from '../core/request' +import { get, post } from '../core/request' import type { ListItem } from '@/api/types' +import type { PasswordRequest } from '../core/types' import type { CurrentUserInfo } from './types' /** 获取当前登录用户、权限、语言及可用工作空间。 */ @@ -19,4 +20,9 @@ const getCurrentUserRoleList = () => { return get('/role_list/current_user') } -export default { getCurrentUserInfo, getCurrentUserRoleList, getCurrentUserWorkspaceList } +/** 修改当前登录用户的密码,成功后当前登录凭据失效。 */ +const postCurrentUserPassword = (password: PasswordRequest) => { + return post('/user/current/reset_password', password) +} + +export default { postCurrentUserPassword, getCurrentUserInfo, getCurrentUserRoleList, getCurrentUserWorkspaceList } diff --git a/ui/src/api/admin/auth/login.ts b/ui/src/api/admin/auth/login.ts index 05ce6944773..8b506b3577d 100644 --- a/ui/src/api/admin/auth/login.ts +++ b/ui/src/api/admin/auth/login.ts @@ -23,4 +23,6 @@ const getCaptcha = (username?: string) => { return get('/user/captcha', { username }) } + + export default { getCaptcha, postLdapLogin, postLogin, postLogout } diff --git a/ui/src/api/admin/auth/types.ts b/ui/src/api/admin/auth/types.ts index 8e4d2173e6a..5ab8bec417d 100644 --- a/ui/src/api/admin/auth/types.ts +++ b/ui/src/api/admin/auth/types.ts @@ -1,6 +1,6 @@ /** Admin 认证 API 及其 Store 消费方共同使用的类型。 */ -import type { LoginMethod, QrCodeConfig, ListItem } from '@/api/types' +import type { LoginMethod, QrCodeConfig, WorkspaceItem } from '@/api/types' export interface CurrentUserInfo { email: string @@ -14,7 +14,7 @@ export interface CurrentUserInfo { role_name?: string[] source?: string username: string - workspace_list?: ListItem[] + workspace_list?: WorkspaceItem[] } export interface LoginRequest { diff --git a/ui/src/api/types/system-workspace.ts b/ui/src/api/types/system-workspace.ts index d29061af0d5..d3c111607d2 100644 --- a/ui/src/api/types/system-workspace.ts +++ b/ui/src/api/types/system-workspace.ts @@ -3,6 +3,8 @@ export interface WorkspaceItem { name: string id?: string user_count?: number + /** 当前用户在工作空间中的角色名称;普通工作空间列表可能不返回。 */ + role_name?: string[] } export interface CreateWorkspaceMemberPayload { diff --git a/ui/src/assets/iconfont.js b/ui/src/assets/iconfont.js index c29f09bbfb4..c023724d41f 100644 --- a/ui/src/assets/iconfont.js +++ b/ui/src/assets/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_5200172='',(h=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var o,v,t,i,c,d=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}o=function(){var a,l=document.createElement("div");l.innerHTML=h._iconfont_svg_string_5200172,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?d(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),o()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=o,i=h.document,c=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,e())})}function e(){c||(c=!0,t())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}e()}})(window); \ No newline at end of file +window._iconfont_svg_string_5200172='',(h=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var o,v,t,i,c,d=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}o=function(){var a,l=document.createElement("div");l.innerHTML=h._iconfont_svg_string_5200172,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?d(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),o()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=o,i=h.document,c=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,e())})}function e(){c||(c=!0,t())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}e()}})(window); \ No newline at end of file diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index dbede148323..0a0b0433ef5 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -1,501 +1,142 @@ -# components 目录说明 - -本文档是项目公共组件规则的唯一依据。新增、修改、移动或使用公共组件前,应先阅读本文档。 - -`src/components` 存放跨页面或跨业务模块复用的 UI 组件与业务组件。仅在单个功能内使用的组件, -应放在对应的 `views//components` 中。 - -## 组件选型 - -按以下顺序选择组件: - -1. 首先检查 `src/components/global`,优先使用全局自动注册的 Mk 封装组件。 -2. 涉及固定跨页面业务时,检查 `src/components/business` 中手动导入的业务组件。 -3. 以上组件无法满足时,检查 `src/components` 直属目录中手动导入的共享 UI 组件。 -4. 没有对应 Mk 组件时,再使用 Element Plus 现成组件。 -5. 通过 Props、事件、插槽、公开方法、Tailwind class 或必要的样式覆盖适配设计稿。 -6. 现有组件无法满足必要的结构或行为时,再新增自定义组件。 - -已有全局 Mk 组件封装相同能力时,业务页面和业务组件必须使用该封装,不要绕过它直接使用 -底层 Element Plus 组件,也不要在功能目录重复封装。例如对话框使用 `MkDialog`、抽屉使用 -`MkDrawer`、下拉菜单使用 `MkDropdown`、图标使用 `MkIcon`、标准表格使用 `MkTable`。 - -Element Plus 已在 `src/main.ts` 和 `src/chat.ts` 中全局注册,Vue 模板可以直接使用。Element Plus -图标需要从 `@element-plus/icons-vue` 显式导入。 - -使用 Element Plus 前应先检查其原生 API,不重复实现已有能力。例如菜单跳转优先使用 -`el-menu` 的 `router` 属性,不要监听 `select` 后手动调用 Router。 - -没有明确需求时,不要引入新的 UI 组件库。 - -## 目录与注册方式 - -```text -src/components/ -├── COMPONENT_README.md -├── business/ # 跨页面复用的业务组件,不使用 Mk 前缀 -│ ├── folder-tree/ # Workspace 文件夹虚拟树及固定 CRUD 业务 -│ │ ├── index.vue -│ │ ├── FolderFormDialog.vue -│ │ ├── MoveToDialog.vue -│ │ ├── VirtualizedTree.vue -│ │ └── types.ts -│ ├── select-application-dialog/ -│ │ └── index.vue # 已发布智能体选择 -│ ├── select-tool-dialog/ -│ │ └── index.vue # 工具与 Skills 按类型选择 -│ ├── select-knowledge-dialog/ -│ │ └── index.vue # 关联知识库选择、文件夹与共享资源查询 -│ ├── select-model/ -│ │ ├── index.vue # 按供应商分组的模型选择器与可选参数按钮 -│ │ └── ModelParamsDialog.vue # 模型参数动态表单弹窗 -│ ├── workspace-dropdown/ -│ │ └── index.vue # 工作空间选择下拉框 -│ ├── related-resources-drawer/ # 关联资源查看抽屉 -│ │ ├── index.vue -│ │ ├── ResourceIcon.vue # 两个关系方向共用的资源图标 -│ │ └── types.ts # 抽屉目标与规范化表格行类型 -│ ├── resource-authorization-drawer/ # 资源用户组、用户授权抽屉与权限配置弹窗 -│ │ ├── index.vue -│ │ ├── UserAuthorization.vue # 按用户查询与授权 -│ │ ├── types.ts # 内部公共 Props 与权限选项类型 -│ │ ├── user-group/ -│ │ │ ├── UserGroupAuthorization.vue # 按用户组查询与授权 -│ │ │ └── UserGroupMembersDrawer.vue # 用户组成员查询抽屉 -│ │ └── PermissionConfigDialog.vue -│ └── workspace-relation-tags/ -│ └── index.vue # 标签及关联工作空间展示 -├── global/ # 高频、稳定的基础组件,自动注册 -│ ├── markdown-editor/ -│ │ ├── config.ts # 本地扩展、内容过滤和语言包的全局配置 -│ │ ├── MdEditor.vue # 支持多语言和自定义页脚的 Markdown 编辑器 -│ │ ├── MdEditorMagnify.vue # 支持弹窗放大编辑的 Markdown 编辑器 -│ │ ├── MdPreview.vue # 禁用代码折叠的 Markdown 预览器 -│ │ ├── md-editor.scss # Markdown 编辑器、预览区和上标说明浮层的共享样式 -│ │ └── sup-popover.ts # Markdown 上标说明悬浮层交互 -│ ├── mk-complex-search/ -│ │ └── index.vue # 字段选择与输入或枚举条件组合搜索框 -│ ├── mk-collapse/ -│ │ └── index.vue # 带标题和过渡动画的折叠内容区 -│ ├── mk-dialog/ -│ │ └── index.vue # 统一对话框关闭行为和内容滚动布局 -│ ├── mk-dropdown/ -│ │ ├── index.vue # 下拉容器 -│ │ ├── mk-dropdown-menu.vue # 下拉菜单容器 -│ │ └── mk-dropdown-item.vue # 菜单项布局和选中状态 -│ ├── mk-drawer/ -│ │ └── index.vue # 统一抽屉关闭行为和内容滚动布局 -│ ├── mk-empty/ -│ │ └── index.vue # 普通无数据与搜索无匹配的统一空状态 -│ ├── mk-form-list/ -│ │ └── index.vue # 可动态增删与排序的表单行列表,自动注册 -│ ├── mk-source-card/ -│ │ ├── index.vue # 来源资源的统一卡片结构 -│ │ ├── mk-source-card-action.vue # 内部卡片悬浮操作容器 -│ │ └── mk-source-card-action-dropdown.vue # 内部 More 下拉菜单 -│ ├── mk-icon/ -│ │ ├── ApplicationIcon.vue # 智能体头像与默认图标 -│ │ ├── KnowledgeIcon.vue # 知识库类型与自定义图标 -│ │ ├── TriggerIcon.vue # 触发器类型图标 -│ │ ├── ToolIcon.vue # 工具类型与自定义图标 -│ │ └── index.vue # SVG Symbol 与 Element Plus 图标统一入口 -│ ├── mk-infinite-scroll/ -│ │ └── index.vue # 分页列表滚动触底加载与结束状态 -│ ├── mk-list-item/ -│ │ └── index.vue # 列表与业务分组列表复用的行结构 -│ ├── mk-view-layout/ -│ │ ├── index.vue # 路由页面标题、操作区和内容区统一结构 -│ │ ├── layout-batch-footer.vue # 页面与表格共用的批量选择底栏 -│ │ └── layout-aside.vue # 页面可选左侧栏结构 -│ ├── mk-search-input/ -│ │ └── index.vue # 带默认搜索图标的输入框 -│ ├── mk-status-label/ -│ │ └── index.vue # 布尔状态图标和文案 -│ ├── mk-slider/ -│ │ └── index.vue # 滑块与右置控制按钮的数值输入框 -│ ├── mk-table/ -│ │ ├── index.vue # 表格、分页、列宽拖拽和批量操作 -│ │ ├── mk-table-filter.vue # 表头多选筛选器 -│ │ └── mk-table-more-dropdown.vue # 表格操作列 More 下拉菜单 -│ └── mk-tag-group/ -│ └── index.vue # 标签折叠和剩余标签浮层 -├── mk-filterable-dropdown/ -│ └── index.vue # 带搜索过滤和滚动列表的下拉选择,手动导入 -├── mk-card-checkbox/ -│ └── index.vue # 卡片式布尔选择,手动导入 -├── mk-date-range/ -│ ├── index.vue # 日期预设与自定义日期区间组合筛选器,手动导入 -│ └── types.ts # 日期筛选结果类型 -├── mk-drag-upload/ -│ └── index.vue # 拖拽上传与已选文件卡片,手动导入 -├── mk-dynamics-form/ -│ ├── index.ts # 动态表单、表单配置器及组件内类型的公开入口 -│ ├── index.vue # 根据字段配置渲染和校验动态表单 -│ ├── type.ts # 组件内部及调用方共享的字段配置类型 -│ ├── enums.ts # 字段类型和显隐比较选项 -│ ├── FormItem.vue # 字段组件调度层,仅供组件内部使用 -│ ├── items/ # 运行时字段实现,仅供组件内部使用 -│ └── constructor/ # 字段配置器、变量选择器及配置项实现 -├── mk-search-list/ -│ └── index.vue # 搜索框与剩余空间滚动列表,手动导入 -├── mk-tags-edit/ -│ └── index.vue # 标签增删与重复检查,手动导入 -├── mk-logo/ -│ ├── LogoFull.vue # 带产品名称的完整 Logo,手动导入 -│ └── LogoIcon.vue # 不带产品名称的图形 Logo,手动导入 -└── codemirror-editor/ - ├── python.vue # 内置 pylint 诊断和全屏编辑的 Python 编辑器 - ├── Json.vue # 支持格式化、语法诊断和全屏编辑的 JSON 输入框 - └── style.scss # Python 与 JSON 编辑器共享的 scoped 样式 -``` +# 公共组件使用约定 -Vite 的 `unplugin-vue-components` 只扫描 `src/components/global`。未被排除扫描的组件可以直接在 -Vue 模板中使用,不需要手动导入;`MkFormList`、`MkSourceCard` 均通过此方式使用。`MkSourceCard` 的两个内部操作组件通过 `globsExclude` -排除扫描,仅由卡片显式导入并通过插槽提供。其他共享组件必须从具体文件路径导入: +本文档维护公共 UI 与业务组件的选型、接口和使用约束。工作流专属规则见 +[WORKFLOW_README.md](../workflow-canvas/WORKFLOW_README.md),样式规则见 +[STYLE_README.md](../styles/STYLE_README.md)。 -```ts -import MkFilterableDropdown from '@/components/mk-filterable-dropdown/index.vue' -import MkSearchList from '@/components/mk-search-list/index.vue' -import MkDateRange from '@/components/mk-date-range/index.vue' -import MkDragUpload from '@/components/mk-drag-upload/index.vue' -import { MkDynamicsForm, MkDynamicsFormConstructor } from '@/components/mk-dynamics-form' -import LogoFull from '@/components/mk-logo/LogoFull.vue' -import LogoIcon from '@/components/mk-logo/LogoIcon.vue' -import PythonCodeEditor from '@/components/codemirror-editor/python.vue' -import JsonInput from '@/components/codemirror-editor/Json.vue' -import FolderTree from '@/components/business/folder-tree/index.vue' -import MoveToDialog from '@/components/business/folder-tree/MoveToDialog.vue' -import SelectModel from '@/components/business/select-model/index.vue' -import WorkspaceDropdown from '@/components/business/workspace-dropdown/index.vue' -import WorkspaceRelationTags from '@/components/business/workspace-relation-tags/index.vue' -``` +## 选型与目录 -自动注册只适用于 Vue 模板。脚本中的类型、常量和 Element Plus 图标仍需显式导入。自动生成 -的声明位于 `src/components.d.ts`,不要手动修改;通过 Vite 开发服务或生产构建刷新, -再运行类型检查验证。 - -## 组件约定 - -- 项目源码不编写 `aria-label` 属性。按钮前添加简短中文 HTML 备注说明实际用途, - 例如 ``;已有用途备注时不重复添加。此规则适用于公共组件、页面和工作流节点。 -- 高频、稳定、跨多数页面使用的基础组件放入 `global`。 -- 跨页面复用且依赖业务类型、固定业务接口或领域交互的组件放入 `business/`, - 由使用方手动导入。业务组件不使用 `Mk` 前缀,也不再按 Workspace 等上级领域增加额外目录。 -- 不依赖固定业务且尚未纳入全局基础组件的共享组合 UI 放在 `components/`, - 由使用方手动导入,例如 `MkFilterableDropdown`。 -- 一个公共组件默认使用一个 kebab-case 目录,入口统一为 `index.vue`;`codemirror-editor` 按语言 - 提供 `python.vue` 和 `Json.vue` 两个专用入口,`markdown-editor` 提供 `MdEditor.vue`、 - `MdEditorMagnify.vue` 和 `MdPreview.vue` 三个自动注册入口,`mk-logo` 按完整 Logo 与图形 Logo - 提供 `LogoFull.vue` 和 `LogoIcon.vue` 两个专用入口。 -- 组件名使用 PascalCase;目录名使用 kebab-case,例如 `MkIcon` 对应 - `mk-icon/index.vue`。 -- `index.vue` 必须通过 `defineOptions({ name: 'ComponentName' })` 声明多单词组件名,避免 - ESLint 将名称推断为 `index`。 -- Props、Emits 和 Slots 保持类型化。 -- 组件类型归属遵循 `src/api/API_README.md`;API 与组件共用的业务类型从 - `@/api/types` 导入。 -- 组件样式默认使用 `scoped`;明确的全局规则放入 `src/styles`。 -- `global` 不创建仅用于二次导出的 `components/index.ts`。 - -### 数字输入框 - -项目内所有设置 `controls-position="right"` 的 `el-input-number` 必须同时显式设置 -`align="left"`,统一将输入内容左对齐。页面、公共组件和工作流节点均遵循此规则。 +依次检查全局 Mk 组件、业务组件、手动导入的共享 UI,最后使用 Element Plus;已有同类封装时 +直接复用,例如 `MkDialog`、`MkDrawer`、`MkDropdown`、`MkIcon`、`MkTable`。先用现有 Props、 +事件、插槽和样式适配,无法满足必要行为时再新增组件;无明确需求不引入新 UI 库。 -```vue - -``` +| 位置 | 用途 | 使用方式 | +| -------------------------------------- | -------------------------------------- | ---------------- | +| `global/` | 高频、稳定的基础组件 | Vue 模板自动注册 | +| `business//` | 跨页面复用的固定业务组件,不加 Mk 前缀 | 显式导入 | +| `/` | 尚未纳入全局的共享组合 UI | 显式导入 | +| `views//`、`workflow-canvas/` | 功能或画布专属组件 | 留在所属功能内 | -### 浮层触发节点 +Vite 仅扫描 `src/components/global`,当前没有 `globsExclude` 配置;其中的内部组件文件也在 +扫描范围内,但使用方仍应通过父组件的插槽使用 `Action`、`ActionDropdown`、`Header`、`Footer`, +不直接依赖内部组件。自动注册仅适用于模板,脚本类型、常量及 Element Plus 图标需显式导入。 +`src/components.d.ts` 由 Vite 开发服务或构建生成,不手动修改。 -Element Plus 使用 `ElOnlyChild` 处理浮层触发器。`el-tooltip`、`el-popover`、 -`el-popconfirm`、`MkDropdown` 及其封装组件的触发插槽必须只渲染一个有效根节点。多个并列 -元素需要使用具有实际布局盒的 `span` 或 `div` 包裹;不要使用 `template` 或 -`display: contents`,否则浮层无法可靠计算触发区域。 +Element Plus 已在 Admin、Chat 入口注册。使用前先检查原生 API,避免重复实现已有能力。 -```vue - - -
浮层内容
-
+手动组件从具体入口导入;动态表单通过自己的公开入口导入: + +```ts +import SelectModel from '@/components/business/select-model/index.vue' +import MkSearchList from '@/components/mk-search-list/index.vue' +import { MkDynamicsForm, MkDynamicsFormConstructor } from '@/components/mk-dynamics-form' ``` -## 自动注册组件 +## 通用规则 -### MdEditor、MdEditorMagnify、MdPreview +- 目录使用 kebab-case,默认入口为 `index.vue`,通过 `defineOptions` 声明 PascalCase 多单词组件名。 + Markdown 编辑器、CodeMirror 和 Logo 按下文的专用入口使用。 +- Props、Emits、Slots 保持类型化。类型归属遵循 [API_README.md](../api/API_README.md), + API 与组件共用类型从 `@/api/types` 导入。 +- 样式默认 `scoped`;组件专属样式留在组件目录,应用级规则放在 `src/styles`。 +- 不编写 `aria-label`;按钮前添加简短中文用途注释,已有注释不重复添加。 +- `global` 不创建仅用于二次导出的聚合入口。 +- `el-input-number` 设置 `controls-position="right"` 时,同时设置 `align="left"`。 +- Tooltip、Popover、Popconfirm、Dropdown 的触发插槽只保留一个有实际布局盒的根节点; + 多个元素用 `span` 或 `div` 包裹,不使用 `template` 或 `display: contents` 代替。 +- 业务弹窗 `open()` 先重置再回填,关闭动画结束的 `closed` 统一清理表单、校验和临时状态。 + 父级负责校验或请求时,子组件提交数据后由父级成功调用 `close()`,失败保持打开。 +- 仅登录表单支持回车提交;其他业务表单使用 `@submit.prevent`,保存、添加由按钮触发。 + 自定义 `submit` 事件不等于原生表单提交。 -基于 `md-editor-v3` 的全局自动注册组件。三个组件统一使用 User Store 中的当前语言,并为繁体中文 -加载 `zh-TW` 语言包;底层组件的 Props 和事件通过 `$attrs` 透传。`MdEditor` 默认关闭 Prettier, -并透传 `defFooters` 插槽;`MdEditorMagnify` 在无工具栏编辑器的页脚提供放大按钮,弹窗中点击确定 -才会写回内容并触发 `submitDialog`,取消或关闭弹窗会丢弃本次弹窗编辑。组件位于配置了字段和规则的 -`el-form-item` 中时,会像 `el-input` 一样按 `change`、`blur` 触发规则校验;未配置字段或规则时跳过, -传入 `:validate-event="false"` 可关闭自动校验。`MdPreview` 默认关闭代码 -折叠。Admin 和 Chat 入口统一调用 -`configureMarkdownEditor()`,将代码高亮、全屏、KaTeX、图片裁剪、Mermaid、ECharts 和 Prettier -配置为本地实例,避免运行时加载 CDN 资源;`md-editor-v3` v6 的图标已随依赖打包,不再维护或加载 -旧版 `markdown-iconfont.js`。 - -编辑器、预览区和上标说明浮层的共享样式统一放在组件目录的 `md-editor.scss`,由 `config.ts` -引入。`MdEditor` 与 `MdPreview` 在底层组件根节点添加 `mk-markdown-editor`,编辑器内部样式 -仅在该范围生效;`MdEditorMagnify` 通过复用 `MdEditor` 继承样式。上标浮层由 `sup-popover.ts` -挂载到 `body`,使用独立的 `markdown-sup-popover` 类名,不依赖编辑器祖先或 Vue scoped 属性。 -组件专属样式不放入全局 `src/styles`;第三方基础 CSS 仍由全局 Sass 入口加载。 - -可编辑的 Markdown 输入框悬浮边框使用 `--el-input-hover-border-color`,编辑区获得焦点时 -使用 `--el-input-focus-border-color`,未提供这两个变量时分别回退到 Element Plus 的悬浮边框色 -和主题色。只读、禁用和独立预览不触发编辑边框状态。编辑器自带的滚动条统一使用 -`el-scrollbar` 的颜色变量、6px 宽度和 0.3/0.5 透明度,内容溢出时由编辑器控制显示,悬浮 -滑块时加深颜色;不再额外通过轨道透明度隐藏滚动条,保留原有滚动节点和拖动逻辑。 +## 全局 UI 组件 -```vue - - - - - -``` +### MdEditor、MdEditorMagnify、MdPreview -### MkCollapse +位于 `global/markdown-editor/`,底层 Props、事件通过 `$attrs` 透传,语言来自 User Store。 -带标题触发器和展开过渡动画的内容折叠组件。组件内部维护展开状态,`default-expanded` 设置初始 -状态且默认为 `true`;点击标题行会切换状态,不向外回传展开状态。默认插槽放置折叠内容,标题 -默认使用 `title` 属性,也可通过 `label` 插槽自定义;标题触发层可通过 `trigger-class` 和 -`trigger-style` 自定义 class 和行内样式。指示图标默认显示在标题前,通过 -`indicator-position="after"` 可改为显示在标题后的线性上、下箭头。 +- `MdEditor` 默认关闭 Prettier,提供 `defFooters` 插槽。 +- `MdEditorMagnify` 接收字符串 `v-model` 和 `title`,弹窗确认后回写并触发 `submitDialog`; + 关闭丢弃草稿。通过 `useFormItem()` 触发外层 `change` / `blur` 校验, + `:validate-event="false"` 可关闭;这一能力不适用于普通 `MdEditor` 或 `MdPreview`。 +- `MdPreview` 默认关闭代码折叠,用 `model-value` 传入只读内容。 -```vue - -
折叠内容
-
+Admin、Chat 入口调用 `configureMarkdownEditor()` 配置本地高亮、KaTeX、Mermaid、ECharts、 +图片裁剪、全屏和 Prettier,避免运行时加载 CDN;图标随依赖打包,不维护旧图标脚本。 +共享样式在 `md-editor.scss`,由 `config.ts` 引入;编辑器覆盖限定在 `mk-markdown-editor`, +挂载到 body 的上标浮层使用独立的 `markdown-sup-popover`。 +编辑边框复用 Element Plus Input 的悬浮、焦点变量,只读与禁用不触发;滚动条保留编辑器原有 +滚动节点,使用 Element Plus Scrollbar 变量和 6px 宽度,不额外隐藏轨道。 - - -
折叠内容
-
+### MkCollapse - - -
高级设置内容
-
-``` +标题折叠区,`title` 或 `label` 插槽提供标题,默认插槽提供内容。 +`defaultExpanded` 默认 `true`,仅设置初始状态;点击标题内部切换,当前不提供展开状态事件。 +`indicatorPosition` 默认 `before`,可设为 `after`;`triggerClass`、`triggerStyle` 调整触发区。 +内容使用 `v-if`,收起会卸载内部组件。 ### MkEmpty -全局空状态组件,基于 `el-empty` 统一图片和默认文案。`type` 默认为 `default`,展示“暂无数据”; -搜索无匹配时使用 `type="search"`。可通过 `description`、`image`、`image-size` 覆盖默认内容, -其余 Element Plus Empty 属性通过 `$attrs` 透传,默认、`image` 和 `description` 插槽保持可用。 - -```vue - - -``` +基于 `el-empty`。`type="default"` 显示“暂无数据”,搜索无匹配使用 `type="search"`。 +`description`、`image`、`image-size` 可覆盖默认值,其他属性及默认、`image`、`description` 插槽透传。 ### MkDialog -全局对话框组件,Header 高度统一为 `60px`,使用 `el-scrollbar` 包裹内容并为默认插槽提供 `p-6` -内边距。默认显示关闭按钮、关闭时销毁内容,同时禁止点击遮罩或按 Escape 关闭;这些默认行为可以 -通过同名 Props 覆盖。Element Plus Dialog 的其他属性和事件通过 `$attrs` 透传,`header`、 -`subtitle`、默认和 `footer` 插槽保持可用。`subtitle` 位于标题下方的 Header 区域,并统一使用 -`mt-2 text-N600` 样式;仅使用 `subtitle` 时,组件仍会按照 Element Plus 原生的标题 ID 和样式类 -渲染 `title`。内容区域超出最大高度后显示滚动条。 +布尔 `v-model` 控制显示,默认宽度 `600`、挂载到 body、显示关闭按钮、关闭后销毁, +禁止点击遮罩和 Escape 关闭。其余 Dialog 属性、事件通过 `$attrs` 透传。 +提供默认、`header`、`subtitle`、`footer` 插槽;`subtitle` 不替代标题,`header` 透出原生关闭与标题参数。 +内容由 `el-scrollbar` 包裹,容器为 `px-6 dialog-content`,通过 `contentClass` 调整; +最大高度由全局 Dialog 样式控制。 -Dialog 外壳仅在首次打开时挂载;默认在关闭动画结束、触发 `closed` 后整体卸载, -避免未打开的弹窗生成隐藏 DOM。设置 `destroy-on-close="false"` 时,首次打开后保留实例和内容。 -业务侧无需额外使用 `v-if="visible"`,以免跳过关闭动画及 `closed` 清理。 - -```vue - - - ... - - -``` +外壳首次打开才挂载,默认在 `closed` 后卸载。`:destroy-on-close="false"` 可保留首次打开后的实例。 +调用方不要用 `v-if="visible"` 提前卸载,避免跳过关闭动画和清理。 ### MkDrawer -全局抽屉组件,默认通过 Element Plus 的 `append-to-body` 挂载到 `body`,避免受到父级容器的定位、 -层级和隐藏状态影响;统一使用 `el-scrollbar` 包裹内容并为默认插槽提供 `p-6` 内边距。通过 -`content-class` 可以覆盖内容容器样式;全高布局可传入 `content-class="h-full p-0"`,再由 -`MkViewLayout` 管理内边距和滚动区域。默认显示关闭按钮、关闭时销毁内容,同时禁止点击遮罩或按 -Escape 关闭;这些默认行为可以通过同名 Props 覆盖。Element Plus Drawer 的其他属性和事件通过 -`$attrs` 透传,`header`、默认和 `footer` 插槽保持可用。 - -```vue - - ... - - -``` - -业务 Dialog 和 Drawer 的状态生命周期保持一致:`open()` 先调用 `resetData()`,再回填编辑数据并 -显示浮层;取消、提交成功或其他关闭操作通常直接将 `v-model` 绑定的可见状态设为 `false`。当浮层 -只负责收集数据,而提交数据的接收校验或异步请求由父组件负责时,可以向父组件暴露 `close()`,由 -父组件在数据接收或请求成功后关闭浮层,校验拒绝或请求失败时保持浮层打开。组件统一监听 `closed` -调用 `resetData()`,确保所有关闭路径都在关闭动画结束后完成清理。`resetData()` 应统一重置表单、 -提交状态、临时选项和表单校验,不把清理逻辑散落在 `open()`、取消按钮或提交成功回调中。 - -只有登录表单支持回车提交。其他业务表单使用 `@submit.prevent` 阻止默认提交,不绑定提交函数, -也不通过 Enter 键触发保存或添加;提交操作由按钮点击触发。组件的 `emit('submit', data)` 是向 -父组件传递数据的自定义事件,不代表支持回车提交。 +布尔 `v-model` 控制显示,默认宽度 `700`、挂载到 body、显示关闭按钮、关闭后销毁内容, +禁止点击遮罩和 Escape 关闭;原生属性、事件通过 `$attrs` 覆盖或透传。 +提供默认、`header`、`footer` 插槽,内容使用 `el-scrollbar` 和 `p-6`,可用 `contentClass` 调整。 +全高内容可传 `content-class="h-full p-0"` 并复用 `MkViewLayout`。 +Drawer 没有 `MkDialog` 的外壳挂载控制;需要按需挂载时,由业务在 `closed` 后卸载。 ### MkComplexSearch -用于在多个字段之间切换搜索条件。`fields` 中的基础字段使用 `@/api/types` 中的 -`OptionItem`;字段包含 `options` 时使用下拉选择,否则使用文本输入。选项字段配置 -`remoteMethod` 后自动启用远程搜索,并由该方法异步加载当前字段的选项。输入或选择完成时,`change` 返回 -`{ [field]: value }`;选项字段配置 `multiple: true` 时启用多选并返回值数组。切换字段或清空条件时 -返回 `undefined`。 - -```vue - - - -``` +`fields` 使用 `OptionItem`,附加 `multiple`、`remoteMethod`。 +有 `options` 时渲染选择器,否则渲染文本输入;`remoteMethod(query)` 由调用方更新选项,组件管理加载状态。 +`change` 返回 `{ [field]: value }`,多选返回数组;清空返回 `undefined`,切换字段仅在原条件非空时通知清空。 ### MkDropdown、MkDropdownMenu、MkDropdownItem -普通下拉菜单使用 `MkDropdown`,不要在业务代码中直接组合 `el-dropdown`、 -`el-dropdown-menu` 和 `el-dropdown-item`。`MkDropdown` 默认 `persistent: false`,其余 -Element Plus Dropdown 属性和事件通过 `$attrs` 传入,并暴露 `handleOpen()`、 -`handleClose()`。 - -`dropdown` 插槽内使用 `MkDropdownMenu` 和 `MkDropdownItem`。下拉触发器必须只有一个有效根 -节点。 - -```vue - - 打开菜单 - - -``` - -菜单项左侧为 Element Plus 图标时通过 `icon` 传入;使用 `MkIcon` 或自定义结构时使用 `icon` -插槽。可选中菜单项传入 `selectable`,通过 `selected` 控制选中状态;开启后所有同组菜单项都会 -预留右侧勾选位。 - -```vue - - {{ option.label }} - -``` +`MkDropdown` 默认 `persistent: false`,透传 Dropdown 属性、事件,公开 `handleOpen()`、`handleClose()`。 +`dropdown` 插槽内组合 `MkDropdownMenu`、`MkDropdownItem`,不在业务中重复组装底层组件。 +菜单项通过 `icon` Prop 传 Element Plus 图标,或使用 `icon` 插槽;`selectable` 预留勾选位, +`selected` 控制选中。触发器遵循单根节点规则。 ### MkViewLayout -路由页面及全高浮层的通用内容结构,统一提供满高弹性布局及可选左侧栏。标题优先使用 `title` -Prop,未传入时读取当前路由的 `meta.title`;显式传入 `title=""` 可隐藏默认标题。`aside` -作用域插槽提供 `title` 和对应区域的 `Header` 包装组件, -默认作用域插槽另外提供主内容区的 `Footer` 包装组件;插槽未渲染 `Header` 时,组件会自动显示当前 -标题,页面需要添加操作区或自定义标题时再显式渲染 `Header`。Header 固定在主内容区顶部,其余 -默认插槽内容由组件统一放入 -`el-scrollbar`,页面不要再为整个主内容区嵌套滚动容器;Tabs、树或其他局部区域需要独立滚动时 -仍可自行使用 `el-scrollbar`。渲染 `Footer` 后固定在主内容区底部,不占用左侧栏空间,适合放置页面 -或全高 Drawer 右侧内容的操作按钮。页面可以在同一个插槽中组织标题、内容、底栏和空状态,并只写 -一次业务状态判断。传入 `aside` 插槽后才会渲染左侧栏;左右结构上方的独立内容放入 `top` 插槽。 -页面加载状态通过 `loading` Prop 传入,由组件将 Element Plus Loading 遮罩绑定到整个布局根节点。 -主内容区滚动时通过 `scroll` 事件返回 `scrollTop` 和 `scrollLeft`;需要由分类导航等外部交互定位 -主内容时,通过组件 Ref 调用公开的 `setScrollTop()`。`getScrollContainer()` 返回主内容区实际滚动 -元素,可传给 `el-anchor` 等需要显式滚动容器的组件。 -默认插槽没有渲染 `Header` 时会自动显示当前标题;显式渲染后则由页面控制标题内容。`collapsible` -默认为 `false`;传入后,展开状态仅在鼠标移入侧栏或焦点进入侧栏时显示收起按钮,收起状态始终显示 -展开按钮。收起时释放侧栏宽度,页面不需要自行维护折叠状态。 - -主内容区固定保留 `px-6` 水平留白。内置滚动容器会抵消两侧留白,再为滚动内容补回 `24px` -水平间距,使滚动条贴齐主区域右边界,并允许 `MkTable` 的底部操作栏延伸至完整主区域宽度。 - -卡片等非表格列表需要批量操作时,页面在批量选择模式下渲染 `Footer`,通过 -`v-model:batch-selection` 绑定选中项唯一值数组,`batch-values` 传入当前列表全部唯一值,并提供 -`footer-batch-actions` 插槽中的业务按钮;布局会复用 `LayoutBatchFooter`,统一显示全选、半选、 -已选数量和取消按钮。点击取消会清空选择,并由 `Footer` 触发 `batch-cancel`。未提供 -`footer-batch-actions` 时,`Footer` 仍渲染普通底栏。`footer-batch-actions` 作用域同时提供 -`batchSelection`,业务按钮需要当前选择时可以直接读取。 +满高页面布局,`loading` 覆盖整个根节点。`title` 默认读取路由标题,传空字符串隐藏; +提供 `aside` 插槽时才渲染侧栏,`collapsible` 开启内部折叠,`top` 放左右区域上方的内容。 -```vue - - - - - - - - - -``` +- `aside` 插槽提供 `title`、`Header`;默认插槽提供 `title`、`Header`、`Footer`。 +- 未显式渲染 `Header` 时自动显示标题;`Header`、`Footer` 固定,正文由内置 `el-scrollbar` 滚动。 + 不重复包裹整个主内容区,局部树、Tabs 等可独立滚动。 +- `scroll` 返回 `{ scrollTop, scrollLeft }`;公开 `setScrollTop()`、`getScrollContainer()`。 +- 主区水平留白为 24px,滚动容器抵消外层留白后补回内容间距,滚动条贴右边缘。 +- `Footer` 无批量插槽时显示普通底栏;提供 `footer-batch-actions` 后复用批量底栏, + 用 `v-model:batch-selection` 绑定唯一值数组,`batch-values` 传当前列表全部值。 + 插槽提供 `batchSelection`;取消清空选择并触发 `batch-cancel`。 ```vue - -