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
5 changes: 5 additions & 0 deletions ui/src/api/API_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ API 类型统一在 `src/api` 范围内管理,相关规则由本文档统一
`import type { ... } from '@/api/types'` 导入,不写 `/index.ts`。
- `src/api/types/index.ts` 只负责导出各业务域类型,不直接声明类型。
- 新增类型前先搜索是否已有等价声明,优先复用或扩展已有类型。
- 后端字段的固定协议值与其类型放在同一个 `src/api/types/<domain>.ts` 中:使用
`as const` 对象声明运行时常量,并从该对象派生联合类型。协议常量是唯一数据源;新增、删除或
修改协议值只维护对应的 API 类型文件,业务判断不得重复使用裸字符串或另建同值常量。
- 同时导出协议常量的类型模块在 `src/api/types/index.ts` 中使用 `export *`;只有纯类型模块使用
`export type *`。
- 同一业务边界内的重复类型提取到最近共同目录的 `common.ts`;`common.ts` 不得成为无关类型
的集合。
- API 与 View 或 Component 使用同一业务类型时只保留一份声明,不得在两层分别定义。
Expand Down
13 changes: 0 additions & 13 deletions ui/src/api/admin/system/chat-user-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,8 @@ const postAuthSettingConnection = (setting: AuthProviderSetting) => {
const putAuthSetting = (authType: AuthProviderType, setting: AuthProviderSetting) => {
return put<AuthProviderSetting, boolean>(`${prefix}/${authType}/info`, setting)
}

/** 获取对话用户登录设置。 */
const getLoginSetting = () => {
return get<LoginAuthSetting>(`${prefix}/setting`)
}

/** 保存对话用户登录设置。 */
const putLoginSetting = (setting: LoginAuthSetting) => {
return put<LoginAuthSetting, boolean>(`${prefix}/setting`, setting)
}

export default {
getAuthSetting,
getLoginSetting,
postAuthSettingConnection,
putAuthSetting,
putLoginSetting,
}
70 changes: 70 additions & 0 deletions ui/src/api/admin/system/role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { del, get, post } from '../core/request'
import type { ParamsPage, ResponsePage } from '../core/types'
import type {
CreateRoleMembersRequest,
RequestParams,
RoleItem,
RoleMember,
RolePermissionModule,
SaveRoleRequest,
SaveRolePermissionRequest,
RoleType,
} from '@/api/types'

const prefix = '/system/role'

/** 获取内置角色与自定义角色列表。 */
const getRoleList = () => {
return get<{ internal_role: RoleItem[]; custom_role: RoleItem[] }>(prefix)
}

/** 创建或重命名自定义角色。 */
const postRole = (role: SaveRoleRequest) => {
return post<SaveRoleRequest, RoleItem>(prefix, role)
}

/** 删除自定义角色。 */
const deleteRole = (roleId: string) => {
return del<boolean>(`${prefix}/${roleId}`)
}


/** 获取指定角色的权限配置。 */
const getRolePermissionList = (roleId: string) => {
return get<RolePermissionModule[]>(`${prefix}/${roleId}/permission`)
}


/** 保存指定角色的权限配置。 */
const postRolePermissions = (roleId: string, permissions: SaveRolePermissionRequest[]) => {
return post<SaveRolePermissionRequest[], boolean>(`${prefix}/${roleId}/permission`, permissions)
}

/** 获取指定角色的成员分页列表。 */
const getRoleMemberList = (roleId: string, page: ParamsPage, query?: RequestParams) => {
return get<ResponsePage<RoleMember>>(
`${prefix}/${roleId}/user_list/${page.currentPage}/${page.pageSize}`,
query,
)
}

/** 为指定角色添加成员。 */
const postRoleMembers = (roleId: string, request: CreateRoleMembersRequest) => {
return post<CreateRoleMembersRequest, boolean>(`${prefix}/${roleId}/add_member`, request)
}

/** 从指定角色移除成员。 */
const deleteRoleMember = (roleId: string, userRelationId: string) => {
return del<boolean>(`${prefix}/${roleId}/remove_member/${userRelationId}`)
}

export default {
deleteRole,
deleteRoleMember,
getRoleList,
getRoleMemberList,
getRolePermissionList,
postRole,
postRoleMembers,
postRolePermissions,
}
3 changes: 2 additions & 1 deletion ui/src/api/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
export type * from './common'
export type * from './chat-user'
export type * from './chat-user-groups'
export type * from './login'
export * from './login'
export type * from './system-user'
export type * from './system-user-groups'
export type * from './system-workspace'
export type * from './system-authentication'
export type * from './system-operate-log'
export * from './system-role'
export type * from './system-email'
29 changes: 18 additions & 11 deletions ui/src/api/types/login.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
/** 登录 API 与登录页面共同使用的业务类型。 */

export type LoginMethod =
| 'CAS'
| 'LDAP'
| 'LOCAL'
| 'OAuth2'
| 'OIDC'
| 'SAML2'
| 'dingtalk'
| 'lark'
| 'wecom'
/** 后端登录方式协议值;新增或修改登录方式时以此处为唯一数据源。 */
export const LOGIN_METHOD = {
CAS: 'CAS',
DINGTALK: 'dingtalk',
LDAP: 'LDAP',
LARK: 'lark',
LOCAL: 'LOCAL',
OAUTH2: 'OAuth2',
OIDC: 'OIDC',
SAML2: 'SAML2',
WECOM: 'wecom',
} as const

export type LoginMethod = (typeof LOGIN_METHOD)[keyof typeof LOGIN_METHOD]

export interface LoginConfig {
default_value: LoginMethod
login_methods?: LoginMethod[]
max_attempts: number
}

export type QrCodeProvider = Extract<LoginMethod, 'dingtalk' | 'lark' | 'wecom'>
export type QrCodeProvider = Extract<
LoginMethod,
typeof LOGIN_METHOD.DINGTALK | typeof LOGIN_METHOD.LARK | typeof LOGIN_METHOD.WECOM
>

export interface QrCodeConfig {
agent_id?: string
Expand Down
65 changes: 65 additions & 0 deletions ui/src/api/types/system-role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/** 后端角色类型协议值;新增或修改角色类型时以此处为唯一数据源。 */
export const ROLE_TYPE = {
ADMIN: 'ADMIN',
USER: 'USER',
WORKSPACE_MANAGE: 'WORKSPACE_MANAGE',
} as const

export type RoleType = (typeof ROLE_TYPE)[keyof typeof ROLE_TYPE]

export interface RoleItem {
id: string
role_name: string
type: RoleType
create_user: string
internal: boolean
user_count?: number
}

export interface RolePermission {
id: string
name: string
enable: boolean
}

export interface RolePermissionFeature {
id: string
name: string
enable: boolean
permission: RolePermission[]
}

export interface RolePermissionModule {
id: string
name: string
children: RolePermissionFeature[]
}

export interface SaveRoleRequest {
role_id?: string
role_name: string
role_type?: RoleType
}

export interface SaveRolePermissionRequest {
id: string
enable: boolean
}

export interface RoleMember {
user_relation_id: string
user_id: string
username: string
nick_name: string
workspace_id: string
workspace_name: string
}

export interface CreateRoleMemberItem {
user_ids: string[]
workspace_ids?: string[]
}

export interface CreateRoleMembersRequest {
members: CreateRoleMemberItem[]
}
2 changes: 2 additions & 0 deletions ui/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
LayoutAside: typeof import('./components/global/mk-view-layout/layout-aside.vue')['default']
MkCollapse: typeof import('./components/global/mk-collapse/index.vue')['default']
MkComplexSearch: typeof import('./components/global/mk-complex-search/index.vue')['default']
MkDialog: typeof import('./components/global/mk-dialog/index.vue')['default']
MkDrawer: typeof import('./components/global/mk-drawer/index.vue')['default']
Expand All @@ -35,6 +36,7 @@ declare module 'vue' {
// For TSX support
declare global {
const LayoutAside: typeof import('./components/global/mk-view-layout/layout-aside.vue')['default']
const MkCollapse: typeof import('./components/global/mk-collapse/index.vue')['default']
const MkComplexSearch: typeof import('./components/global/mk-complex-search/index.vue')['default']
const MkDialog: typeof import('./components/global/mk-dialog/index.vue')['default']
const MkDrawer: typeof import('./components/global/mk-drawer/index.vue')['default']
Expand Down
16 changes: 15 additions & 1 deletion ui/src/components/COMPONENT_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ src/components/
├── global/ # 高频、稳定的基础组件,自动注册
│ ├── mk-complex-search/
│ │ └── index.vue # 字段选择与输入或枚举条件组合搜索框
│ ├── mk-collapse/
│ │ └── index.vue # 带标题和过渡动画的折叠内容区
│ ├── mk-dialog/
│ │ └── index.vue # 统一对话框关闭行为和内容滚动布局
│ ├── mk-dropdown/
Expand All @@ -61,7 +63,8 @@ src/components/
│ └── mk-tag-group/
│ └── index.vue # 标签折叠和剩余标签浮层
├── mk-search-list/
│ └── index.vue # 搜索框与剩余空间滚动列表,手动导入
│ ├── index.vue # 搜索框与剩余空间滚动列表,手动导入
│ └── mk-list-item.vue # 列表与业务分组列表复用的私有行结构
├── mk-workspace-dropdown/
│ └── index.vue # 工作空间选择下拉框,手动导入
└── mk-workspace-relation-tags/
Expand Down Expand Up @@ -117,6 +120,17 @@ Element Plus 使用 `ElOnlyChild` 处理浮层触发器。`el-tooltip`、`el-pop

## 自动注册组件

### MkCollapse

带标题触发器和展开过渡动画的内容折叠组件。通过 `v-model` 控制展开状态,默认展开;点击标题行
会切换状态,默认插槽放置折叠内容。

```vue
<MkCollapse v-model="expanded" title="系统管理员">
<div>折叠内容</div>
</MkCollapse>
```

### MkEmpty

全局空状态组件,基于 `el-empty` 统一图片和默认文案。`type` 默认为 `default`,展示“暂无数据”;
Expand Down
35 changes: 35 additions & 0 deletions ui/src/components/global/mk-collapse/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import { CaretBottom } from '@element-plus/icons-vue'

/* 目前折叠面板仅仅是基础功能。后期会增加 自定义插槽*/

defineOptions({ name: 'MkCollapse' })

defineProps<{
title: string
}>()

const expanded = defineModel<boolean>({ default: true })
</script>

<template>
<section>
<div
class="flex w-full cursor-pointer items-center gap-2 py-2 text-left text-N500"
@click="expanded = !expanded"
>
<MkIcon
:icon="CaretBottom"
:size="14"
class="transition-transform"
:class="{ '-rotate-90': !expanded }"
/>
<span>{{ title }}</span>
</div>
<el-collapse-transition>
<div v-if="expanded">
<slot />
</div>
</el-collapse-transition>
</section>
</template>
10 changes: 6 additions & 4 deletions ui/src/components/global/mk-table/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const props = withDefaults(
maxTableHeight?: number
paginationConfig?: PaginationConfig
resizable?: boolean
search?: boolean
isSearching?: boolean
}>(),
{
data: () => [],
Expand All @@ -42,7 +42,9 @@ const paginationPageSizes = computed(() => props.paginationConfig?.pageSizes ??
/** 表格高度 */
const tableHeight = ref(window.innerHeight - props.maxTableHeight)
function updateTableHeight() {
tableHeight.value = window.innerHeight - props.maxTableHeight
tableHeight.value = props.paginationConfig
? window.innerHeight - props.maxTableHeight
: window.innerHeight - props.maxTableHeight + 50
}

/** 选择操作栏 */
Expand Down Expand Up @@ -151,8 +153,8 @@ defineExpose({ clearSelection, tableRef })

<template>
<MkEmpty
v-if="!props.paginationConfig?.total"
:type="search ? 'search' : ''"
v-if="props.paginationConfig ? !props.paginationConfig?.total : props.data.length === 0"
:type="isSearching ? 'search' : ''"
class="flex-1"
/>
<div
Expand Down
7 changes: 4 additions & 3 deletions ui/src/components/global/mk-view-layout/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ defineSlots<{
const route = useRoute()
const title = computed(() => props.title || route.meta.title || '')
const slots = useSlots()
const LayoutHeader: FunctionalComponent = (_, { slots }) =>
h('header', { class: 'flex-between shrink-0 py-4' }, slots.default?.())

function flattenSlotNodes(children: unknown): VNode[] {
const childNodes = Array.isArray(children) ? children : [children]
Expand All @@ -37,6 +35,9 @@ function flattenSlotNodes(children: unknown): VNode[] {
})
}

const LayoutHeader: FunctionalComponent = (_, { slots }) =>
h('header', { class: 'flex-between shrink-0 py-4 gap-4' }, slots.default?.())

const LayoutContent: FunctionalComponent = () => {
const contentNodes = flattenSlotNodes(
slots.default?.({ Header: LayoutHeader, title: title.value }),
Expand All @@ -46,7 +47,7 @@ const LayoutContent: FunctionalComponent = () => {
return [
hasCustomHeader || !title.value
? null
: h('header', { class: 'flex-between shrink-0 py-4' }, [h('h4', title.value)]),
: h('header', { class: 'flex-between shrink-0 py-4 gap-4' }, [h('h4', title.value)]),
...contentNodes,
]
}
Expand Down
Loading
Loading