Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ui/src/assets/iconfont.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions ui/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ declare module 'vue' {
MkFilterableDropdown: typeof import('./components/global/mk-filterable-dropdown/index.vue')['default']
MkIcon: typeof import('./components/global/mk-icon/index.vue')['default']
MkInfiniteScroll: typeof import('./components/global/mk-infinite-scroll/index.vue')['default']
MkListItem: typeof import('./components/global/mk-list-item/index.vue')['default']
MkSearchInput: typeof import('./components/global/mk-search-input/index.vue')['default']
MkStatusLabel: typeof import('./components/global/mk-status-label/index.vue')['default']
MkTable: typeof import('./components/global/mk-table/index.vue')['default']
Expand All @@ -49,6 +50,7 @@ declare global {
const MkFilterableDropdown: typeof import('./components/global/mk-filterable-dropdown/index.vue')['default']
const MkIcon: typeof import('./components/global/mk-icon/index.vue')['default']
const MkInfiniteScroll: typeof import('./components/global/mk-infinite-scroll/index.vue')['default']
const MkListItem: typeof import('./components/global/mk-list-item/index.vue')['default']
const MkSearchInput: typeof import('./components/global/mk-search-input/index.vue')['default']
const MkStatusLabel: typeof import('./components/global/mk-status-label/index.vue')['default']
const MkTable: typeof import('./components/global/mk-table/index.vue')['default']
Expand Down
55 changes: 28 additions & 27 deletions ui/src/components/COMPONENT_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ src/components/
│ │ └── index.vue # SVG Symbol 与 Element Plus 图标统一入口
│ ├── mk-infinite-scroll/
│ │ └── index.vue # 分页列表滚动触底加载与结束状态
│ ├── mk-list-item/
│ │ └── index.vue # 列表与业务分组列表复用的行结构
│ ├── mk-view-layout/
│ │ ├── index.vue # 路由页面标题、操作区和内容区统一结构
│ │ └── layout-aside.vue # 页面可选左侧栏结构
Expand All @@ -79,8 +81,7 @@ src/components/
│ └── mk-tag-group/
│ └── index.vue # 标签折叠和剩余标签浮层
├── mk-search-list/
│ ├── index.vue # 搜索框与剩余空间滚动列表,手动导入
│ └── mk-list-item.vue # 列表与业务分组列表复用的行结构
│ └── index.vue # 搜索框与剩余空间滚动列表,手动导入
├── mk-form-list/
│ └── index.vue # 可动态增删的表单行列表,手动导入
├── codemirror-editor/
Expand All @@ -97,7 +98,6 @@ Vue 模板中使用,不需要手动导入。其他共享组件必须从具体

```ts
import MkSearchList from '@/components/mk-search-list/index.vue'
import MkListItem from '@/components/mk-search-list/mk-list-item.vue'
import MkFormList from '@/components/mk-form-list/index.vue'
import CodemirrorEditor from '@/components/codemirror-editor/index.vue'
import MkSourceCard from '@/components/mk-source-card/index.vue'
Expand Down Expand Up @@ -201,10 +201,11 @@ ID 和样式类渲染 `title`。内容区域超出最大高度后显示滚动条

### MkDrawer

全局抽屉组件,统一使用 `el-scrollbar` 包裹内容并为默认插槽提供 `p-6` 内边距。默认显示关闭
按钮、关闭时销毁内容,同时禁止点击遮罩或按 Escape 关闭;这些默认行为可以通过同名 Props
覆盖。Element Plus Drawer 的其他属性和事件通过 `$attrs` 透传,`header`、默认和 `footer`
插槽保持可用。
全局抽屉组件,统一使用 `el-scrollbar` 包裹内容并为默认插槽提供 `p-6` 内边距。通过
`content-class` 可以覆盖内容容器样式;全高布局可传入 `content-class="h-full p-0"`,再由
`MkViewLayout` 管理内边距和滚动区域。默认显示关闭按钮、关闭时销毁内容,同时禁止点击遮罩或按
Escape 关闭;这些默认行为可以通过同名 Props 覆盖。Element Plus Drawer 的其他属性和事件通过
`$attrs` 透传,`header`、默认和 `footer` 插槽保持可用。

```vue
<MkDrawer v-model="visible" title="创建用户" size="600">
Expand Down Expand Up @@ -389,6 +390,26 @@ Element Plus 的 `v-infinite-scroll`。组件通过 `v-model` 管理已经加载
</MkInfiniteScroll>
```

### MkListItem

提供列表行的统一间距、悬停状态、选中状态和可选操作区。仅自定义默认插槽时,可以只传
`active` 并监听 `click`;不需要补充无实际用途的 `row`、`label-field` 或 `index`。

```vue
<MkListItem :active="active" @click="handleSelect()">
<MkIcon name="icon_assigned_outlined" :size="20" />
<span>共享模型</span>
</MkListItem>
```

数据驱动使用时传入 `row`。`label-field` 默认读取 `name`,`index` 默认为 `0`;未提供默认插槽时
组件显示对应字段文本。`action` 和 `action-dropdown` 插槽需要配合 `row` 使用,并接收 `row`、
`index`。

```vue
<MkListItem :active="currentRole?.id === role.id" :row="role" @click="selectRole(role)" />
```

### MkSearchInput

带默认搜索图标的输入框。`placeholder` 默认为“搜索”,组件固定支持清空;`v-model`、其他属性
Expand Down Expand Up @@ -580,26 +601,6 @@ const roleSettings = defineModel<{ roleId: string; workspaceIds: string[] }[]>({

`addText` 设置添加按钮文案。删除按钮及第一行与后续行的对齐由组件统一处理。

### MkListItem

提供列表行的统一间距、悬停状态、选中状态和可选操作区。仅自定义默认插槽时,可以只传
`active` 并监听 `click`;不需要补充无实际用途的 `row`、`label-field` 或 `index`。

```vue
<MkListItem :active="active" @click="handleSelect()">
<MkIcon name="icon_assigned_outlined" :size="20" />
<span>共享模型</span>
</MkListItem>
```

数据驱动使用时传入 `row`。`label-field` 默认读取 `name`,`index` 默认为 `0`;未提供默认插槽时
组件显示对应字段文本。`action` 和 `action-dropdown` 插槽需要配合 `row` 使用,并接收 `row`、
`index`。

```vue
<MkListItem :active="currentRole?.id === role.id" :row="role" @click="selectRole(role)" />
```

### MkSearchList

组合搜索框和占满剩余空间的滚动列表。传入 `data` 时,组件默认按 `name` 过滤并按
Expand Down
1 change: 0 additions & 1 deletion ui/src/components/business/folder-tree/VirtualizedTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { CaretBottom } from '@element-plus/icons-vue'
import { Draggable, dragContext } from '@he-tree/vue'
import '@he-tree/vue/style/default.css'
import type { FolderItem } from '@/api/types'
import MkListItem from '@/components/mk-search-list/mk-list-item.vue'
defineOptions({ name: 'FolderVirtualizedTree' })

interface FolderTreeNode extends Omit<FolderItem, 'children'> {
Expand Down
1 change: 0 additions & 1 deletion ui/src/components/business/folder-tree/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { FOLDER_SOURCE } from '@/api/enums'
import { FOLDER_SORT, type FolderSort } from './types'
import { FOLDER_ENTRIES, FOLDER_ENTRY_ID } from '@/constants'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import MkListItem from '@/components/mk-search-list/mk-list-item.vue'
import VirtualizedTree from './VirtualizedTree.vue'
import FolderFormDialog from './FolderFormDialog.vue'
import { useStore } from '@/stores'
Expand Down
8 changes: 6 additions & 2 deletions ui/src/components/global/mk-drawer/index.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<script setup lang="ts">
defineOptions({ name: 'MkDrawer', inheritAttrs: false })

const props = defineProps<{
contentClass?: string
}>()

const visible = defineModel<boolean>({ default: false })

defineSlots<{
Expand All @@ -24,8 +28,8 @@ defineSlots<{
<slot name="header" />
</template>

<el-scrollbar>
<div class="p-6"><slot /></div>
<el-scrollbar view-class="h-full">
<div class="p-6" :class="props.contentClass"><slot /></div>
</el-scrollbar>

<template v-if="$slots.footer" #footer>
Expand Down
1 change: 0 additions & 1 deletion ui/src/components/mk-search-list/index.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts" generic="T extends Record<string, unknown>">
import type { ScrollbarDirection, ScrollbarInstance } from 'element-plus'
import { computed, nextTick, ref, watch } from 'vue'
import MkListItem from './mk-list-item.vue'

defineOptions({ name: 'MkSearchList' })

Expand Down
32 changes: 24 additions & 8 deletions ui/src/styles/STYLE_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,23 @@ CSS 自定义属性同样按变量名排序。嵌套选择器、伪类和媒体
</div>
```

## 常用 Flex 布局工具类
## 常用布局工具类

稳定重复使用的 Flex 布局组合统一定义在 `tailwind.css`。当前提供:
稳定重复使用的布局组合统一定义在 `tailwind.css`。当前提供:

| 工具类 | 作用 |
| ----------------- | ------------------------------------ |
| `flex-between` | 横向排列,两侧贴边,并在垂直方向居中 |
| `flex-center` | 横向排列,在水平和垂直两个方向居中 |
| `flex-col-center` | 纵向排列,在水平和垂直两个方向居中 |
| `flex-wrap` | 横向排列,自动换行,行间距为 8px |
| 工具类 | 作用 |
| ----------------- | ------------------------------------------ |
| `absolute-center` | 绝对定位,相对最近的定位祖先水平和垂直居中 |
| `flex-between` | 横向排列,两侧贴边,并在垂直方向居中 |
| `flex-center` | 横向排列,在水平和垂直两个方向居中 |
| `flex-col-center` | 纵向排列,在水平和垂直两个方向居中 |
| `flex-wrap` | 横向排列,自动换行,行间距为 8px |

```vue
<div class="relative h-40">
<div class="absolute-center">居中内容</div>
</div>

<div class="flex-between">
<span>左侧</span>
<span>右侧</span>
Expand Down Expand Up @@ -335,6 +340,17 @@ Element Plus 所需的 `light-3` 至 `light-9` 和 `dark-2` 色阶使用 `color-
`.el-select__popper` 统一维护,业务组件不需要重复传入 `show-arrow`、`offset` 或
`popper-class`。

Steps 默认使用数字和标题横向排列,并保留 Element Plus 的 `active` 状态管理。Steps 放在
Drawer 的 `header` 插槽内时,组合 `absolute-center` 和宽度工具类即可相对完整 Header 居中,
不受左侧标题和右侧关闭按钮影响。

```vue
<el-steps :active="0" class="absolute-center w-75!">
<el-step title="选择供应商" />
<el-step title="添加模型" />
</el-steps>
```

### 普通 CSS 中叠加透明度

使用 `color-mix()`,只依赖一个主题色变量:
Expand Down
102 changes: 101 additions & 1 deletion ui/src/styles/element-plus.scss
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,9 @@
/* drawer */
.el-drawer {
&__header {
margin: 0;
position: relative;
border-bottom: 1px solid var(--el-border-color);
margin: 0;
padding: calc(var(--spacing) * 4) calc(var(--spacing) * 5) calc(var(--spacing) * 4)
calc(var(--spacing) * 6);
}
Expand All @@ -321,6 +322,9 @@
border-top: 1px solid var(--el-border-color);
padding: calc(var(--spacing) * 4) calc(var(--spacing) * 6);
}
&.btt {
height: calc(100% - 100px) !important;
}
}
/* dropdown */
.el-dropdown {
Expand Down Expand Up @@ -640,6 +644,102 @@
}
}

/* steps */
.el-steps {
align-items: center;

.el-step {
display: flex;
flex: 1;
height: 24px;
min-width: 0;
position: relative;

&:last-child {
flex: none;
}

&__head {
flex: none;
height: 24px;
position: absolute;
width: 100%;
z-index: 1;

&.is-process {
.el-step__icon {
background-color: var(--el-color-primary);
border-color: var(--el-color-primary);
color: white;
}
}
&.is-success {
color: var(--el-color-primary);
border-color: var(--el-color-primary);
}
}

&.is-horizontal .el-step__line {
background-color: var(--el-color-primary);
height: 1px !important;
left: 43px;
right: 8px;
top: 12px;
}

&__icon {
&.is-text {
border: 1px solid;
}
&-inner {
font-weight: 400;
}
}

&__main {
background-color: var(--el-bg-color);
flex: none;
margin-left: calc(var(--spacing) * 8);
padding: 0 calc(var(--spacing) * 2) 0 0;
position: relative;
z-index: 2;
}

&__title {
font-size: var(--mk-font-size-base);
line-height: 24px;
white-space: nowrap;

&.is-process {
color: var(--el-text-color-primary);
font-weight: 500;
}

&.is-wait {
color: var(--mk-N500);
font-weight: 400;
}
&.is-success {
color: var(--mk-N900);
}
}

&__description {
font-size: var(--mk-font-size-sm);
line-height: 20px;
white-space: nowrap;

&.is-process {
color: var(--el-text-color-primary);
font-weight: 400;
}

&.is-wait {
}
}
}
}

/* switch */
.el-switch {
--el-switch-off-color: var(--mk-N400);
Expand Down
10 changes: 9 additions & 1 deletion ui/src/styles/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,15 @@
}
}

/* flex layout utilities */
/* layout utilities */
/* 绝对定位,相对最近的定位祖先水平和垂直居中 */
@utility absolute-center {
left: 50%;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
}

/* 横向排列,两侧贴边并垂直居中 */
@utility flex-between {
align-items: center;
Expand Down
Loading
Loading