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
36 changes: 36 additions & 0 deletions ui/src/components/COMPONENT_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ src/components/
├── mk-search-list/
│ ├── index.vue # 搜索框与剩余空间滚动列表,手动导入
│ └── mk-list-item.vue # 列表与业务分组列表复用的私有行结构
├── mk-form-list/
│ └── index.vue # 可动态增删的表单行列表,手动导入
├── mk-workspace-dropdown/
│ └── index.vue # 工作空间选择下拉框,手动导入
└── mk-workspace-relation-tags/
Expand All @@ -76,6 +78,7 @@ Vue 模板中使用,不需要手动导入。其他共享组件必须从具体

```ts
import MkSearchList from '@/components/mk-search-list/index.vue'
import MkFormList from '@/components/mk-form-list/index.vue'
import MkWorkspaceDropdown from '@/components/mk-workspace-dropdown/index.vue'
import MkWorkspaceRelationTags from '@/components/mk-workspace-relation-tags/index.vue'
```
Expand Down Expand Up @@ -396,6 +399,39 @@ const paginationConfig = ref({

## 手动导入组件

### MkFormList

用于多个业务字段组成的动态表单行,只负责重复行布局、添加和删除,不管理业务字段、校验规则或
选项请求。通过 `v-model` 传入行数据,`defaultItem` 创建新行,列表始终至少保留一行。默认插槽
提供 `item`、`index`,业务组件在插槽中继续声明
`el-form-item`、字段路径和校验规则。

```vue
<script setup lang="ts">
import MkFormList from '@/components/mk-form-list/index.vue'

const roleSettings = defineModel<{ roleId: string; workspaceIds: string[] }[]>({ required: true })
</script>

<MkFormList
v-model="roleSettings"
add-text="添加角色"
:default-item="{ roleId: '', workspaceIds: [] }"
>
<template #default="{ index, item }">
<el-form-item
class="flex-1"
:label="index===0 ? '角色' : ''"
:prop="`roleSettings.${index}.roleId`"
>
<el-select v-model="item.roleId" />
</el-form-item>
</template>
</MkFormList>
```

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

### MkSearchList

组合搜索框和占满剩余空间的滚动列表。传入 `data` 时,组件默认按 `name` 过滤并按
Expand Down
43 changes: 43 additions & 0 deletions ui/src/components/mk-form-list/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<script setup lang="ts" generic="T">
defineOptions({ name: 'MkFormList' })

const props = withDefaults(
defineProps<{
addText: string
defaultItem: T
}>(),
{
addText: '添加',
},
)
const formRows = defineModel<T[]>({ required: true })

defineSlots<{
default(props: { index: number; item: T }): unknown
}>()

function addRow() {
formRows.value = [...formRows.value, props.defaultItem]
}

function removeRow(index: number) {
if (formRows.value.length === 1) return
formRows.value = formRows.value.filter((_, rowIndex) => rowIndex !== index)
}
</script>

<template>
<div v-for="(item, index) in formRows" :key="index" class="flex w-full gap-2">
<slot :index="index" :item="item" />
<el-form-item class="shrink-0" :class="index === 0 ? 'mt-8' : 'mt-0.5'">
<el-button :disabled="formRows.length === 1" text @click="removeRow(index)">
<MkIcon name="icon_delete-trash_outlined" class="text-N600" />
</el-button>
</el-form-item>
</div>

<el-button class="-mt-1 mb-6" link type="primary" @click="addRow">
<MkIcon name="icon_add_outlined" />
<span>{{ addText }}</span>
</el-button>
</template>
7 changes: 7 additions & 0 deletions ui/src/styles/element-plus.scss
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@

/* select */
.el-select {
--el-select-disabled-border: var(--el-border-color);
&__caret {
color: var(--el-text-color-regular);
font-size: 16px;
Expand All @@ -576,6 +577,12 @@
&__wrapper {
padding: 4px 12px;
line-height: 22px;
&.is-disabled {
background-color: var(--mk-N200);
.el-select__caret {
color: var(--el-text-color-disabled);
}
}
}
&__input {
height: 22px;
Expand Down
5 changes: 2 additions & 3 deletions ui/src/views/VIEW_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ src/views/system/identity/users/
├── UserListView.vue # 用户列表路由页面
├── UserFromDrawer.vue # 用户表单抽屉
├── components/
│ ├── UserGroupSetting.vue # 用户组与工作空间异步联动设置
│ └── UserRoleSetting.vue # 用户角色设置子组件
│ └── UserGroupSetting.vue # 用户组与工作空间异步联动设置
└── dialog/
├── BatchSetUserRoleDialog.vue # 批量设置用户角色弹窗
└── UserPwdDialog.vue # 修改用户密码弹窗
Expand All @@ -72,7 +71,7 @@ src/views/system/identity/users/
| 路由页面 | `XxxView.vue` | `WorkspaceListView.vue` |
| 抽屉 | `XxxDrawer.vue` | `AddMemberDrawer.vue` |
| 弹窗 | `XxxDialog.vue` | `CreateOrUpdateWorkspaceDialog.vue` |
| 页面普通组件 | 按具体业务职责使用 `PascalCase.vue` | `MemberRoleSetting.vue` |
| 页面普通组件 | 按具体业务职责使用 `PascalCase.vue` | `UserGroupSetting.vue` |

不要使用缺少组件职责后缀的页面文件名,也不要把 Dialog 命名为 Drawer 或把 Drawer 命名为
Dialog。新增或重命名文件时,应同步更新所有导入和页面功能登记。
Expand Down
85 changes: 76 additions & 9 deletions ui/src/views/system/identity/roles/AddMemberDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '@/api/types'
import { useStore } from '@/stores'
import { MsgSuccess } from '@/utils/message'
import MemberWorkspaceSetting from './components/MemberWorkspaceSetting.vue'
import MkFormList from '@/components/mk-form-list/index.vue'

defineOptions({ name: 'AddRoleMemberDrawer' })

Expand Down Expand Up @@ -115,15 +115,82 @@ defineExpose({ open })
<template>
<MkDrawer v-model="visible" title="添加成员" @closed="resetData">
<el-form ref="formRef" :model="memberForm" label-position="top">
<MemberWorkspaceSetting
<MkFormList
v-model="memberForm.members"
:remote-user-method="loadUserOptions"
:show-workspace="showWorkspace"
:user-loading="userOptionsLoading"
:user-options="userOptions"
:workspace-loading="workspaceOptionsLoading"
:workspace-options="workspaceOptions"
/>
add-text="添加成员"
:default-item="{ user_ids: [], workspace_ids: [] }"
>
<template #default="{ index, item: memberSetting }">
<el-form-item
class="flex-1"
:label="index === 0 ? '成员' : ''"
:prop="`members.${index}.user_ids`"
:rules="{
required: true,
type: 'array',
min: 1,
message: '请选择成员',
trigger: 'change',
}"
>
<el-select
v-model="memberSetting.user_ids"
:loading="userOptionsLoading"
:remote-method="loadUserOptions"
collapse-tags
collapse-tags-tooltip
filterable
fit-input-width
multiple
placeholder="请选择成员"
remote
:reserve-keyword="false"
>
<el-option
v-for="userOption in userOptions"
:key="userOption.id"
:label="userOption.nick_name || userOption.username"
:title="userOption.nick_name || userOption.username"
:value="userOption.id"
/>
</el-select>
</el-form-item>

<el-form-item
v-if="showWorkspace"
class="flex-1"
:label="index === 0 ? '工作空间' : ''"
:prop="`members.${index}.workspace_ids`"
:rules="{
required: true,
type: 'array',
min: 1,
message: '请选择工作空间',
trigger: 'change',
}"
>
<el-select
v-model="memberSetting.workspace_ids"
:loading="workspaceOptionsLoading"
collapse-tags
collapse-tags-tooltip
filterable
fit-input-width
multiple
placeholder="请选择工作空间"
:reserve-keyword="false"
>
<el-option
v-for="workspaceOption in workspaceOptions"
:key="workspaceOption.id"
:label="workspaceOption.name"
:title="workspaceOption.name"
:value="workspaceOption.id"
/>
</el-select>
</el-form-item>
</template>
</MkFormList>
</el-form>

<template #footer>
Expand Down

This file was deleted.

Loading
Loading