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
6 changes: 3 additions & 3 deletions ui/src/api/admin/system/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { get } from '../core/request'
import type { RequestParams, SystemUserOption, WorkspaceMemberOption } from '@/api/types'
import type { RequestParams, SystemUserOption, CommonUserOption } from '@/api/types'

/** 获取默认密码。 */
const getDefaultPassword = () => {
Expand All @@ -11,9 +11,9 @@ const getAllUsers = (query?: RequestParams) => {
return get<SystemUserOption[]>('/user/list', query)
}

/** 获取指定工作空间的成员选项。 */
/** 获取指定工作空间的普通用户选项。 */
const getWorkspaceMembers = (workspaceId: string, query?: RequestParams) => {
return get<WorkspaceMemberOption[]>(`/workspace/${workspaceId}/user_member`, query)
return get<CommonUserOption[]>(`/workspace/${workspaceId}/user_member`, query)
}

export default {
Expand Down
15 changes: 9 additions & 6 deletions ui/src/api/admin/system/resource-authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,31 @@ import type {
ResourcePermissionPayload,
} from '@/api/types'

const prefix = (workspaceId: string, userId: string, resource: ResourceAuthorizationType) =>
`/workspace/${workspaceId}/user_resource_permission/user/${userId}/resource/${resource}`
/** 系统管理资源授权 */
const prefix = (workspaceId: string) => `/workspace/${workspaceId}/user_resource_permission`

/** 获取工作空间成员对指定类型资源的权限列表。 */
/** 获取指定空间、指定用户、指定资源类型的权限列表。 */
const getUserResourcePermissions = (
workspaceId: string,
userId: string,
resource: ResourceAuthorizationType,
query?: RequestParams,
) => {
return get<ResourcePermissionItem[]>(prefix(workspaceId, userId, resource), query)
return get<ResourcePermissionItem[]>(
`${prefix(workspaceId)}/user/${userId}/resource/${resource}`,
query,
)
}

/** 更新工作空间成员对指定类型资源的权限。 */
/** 更新指定空间、指定用户、指定资源类型的权限列表。 */
const putUserResourcePermissions = (
workspaceId: string,
userId: string,
resource: ResourceAuthorizationType,
permissions: ResourcePermissionPayload[],
) => {
return put<ResourcePermissionPayload[], ResourcePermissionPayload[]>(
prefix(workspaceId, userId, resource),
`${prefix(workspaceId)}/user/${userId}/resource/${resource}`,
permissions,
)
}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/api/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface ExportError {
}
}

export interface WorkspaceMemberOption {
export interface CommonUserOption {
id: string
nick_name: string
roles?: string[]
Expand Down
11 changes: 7 additions & 4 deletions ui/src/components/COMPONENT_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,16 +220,18 @@ Escape 关闭;这些默认行为可以通过同名 Props 覆盖。Element Plus
```

业务 Dialog 和 Drawer 的状态生命周期保持一致:`open()` 先调用 `resetData()`,再回填编辑数据并
显示浮层;`close()` 先关闭浮层,再调用 `resetData()`;组件同时监听 `closed` 调用
`resetData()`,确保点击右上角关闭按钮时也完成清理。`resetData()` 应统一重置表单、提交状态、
临时选项和表单校验,不把清理逻辑散落在 `open()`、取消按钮或提交成功回调中。
显示浮层;取消、提交成功或其他关闭操作直接将 `v-model` 绑定的可见状态设为 `false`,不额外定义或
向父组件暴露 `close()`;组件统一监听 `closed` 调用 `resetData()`,确保所有关闭路径都在关闭动画
结束后完成清理。`resetData()` 应统一重置表单、提交状态、临时选项和表单校验,不把清理逻辑散落在
`open()`、取消按钮或提交成功回调中。

### MkComplexSearch

用于在多个字段之间切换搜索条件。`fields` 中的基础字段使用 `@/api/types` 中的
`OptionItem<string>`;字段包含 `options` 时使用下拉选择,否则使用文本输入。选项字段配置
`remoteMethod` 后自动启用远程搜索,并由该方法异步加载当前字段的选项。输入或选择完成时,`change` 返回
`{ [field]: value }`;切换字段或清空条件时返回 `undefined`。
`{ [field]: value }`;选项字段配置 `multiple: true` 时启用多选并返回值数组。切换字段或清空条件时
返回 `undefined`。

```vue
<MkComplexSearch
Expand All @@ -238,6 +240,7 @@ Escape 关闭;这些默认行为可以通过同名 Props 覆盖。Element Plus
{
label: '状态',
value: 'enabled',
multiple: true,
options: [
{ label: '启用', value: true },
{ label: '禁用', value: false },
Expand Down
8 changes: 2 additions & 6 deletions ui/src/components/business/folder-tree/FolderFormDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,6 @@ function open(id: string, folder?: FolderItem) {
visible.value = true
}

function close() {
visible.value = false
resetData()
}
function resetData() {
editId.value = ''
folderForm.desc = ''
Expand All @@ -84,7 +80,7 @@ function resetData() {
folderFormRef.value?.clearValidate()
}

defineExpose({ close, open })
defineExpose({ open })
</script>

<template>
Expand Down Expand Up @@ -120,7 +116,7 @@ defineExpose({ close, open })
</el-form>

<template #footer>
<el-button :disabled="loading" @click="close">取消</el-button>
<el-button :disabled="loading" @click="visible = false">取消</el-button>
<el-button type="primary" :loading="loading" @click="handleSubmit">
{{ editId ? '保存' : '创建' }}
</el-button>
Expand Down
10 changes: 3 additions & 7 deletions ui/src/components/business/folder-tree/MoveToDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function handleSubmit() {
// })
// .then((updatedFolder) => {
// MsgSuccess('移动成功')
// moveFolderDialogRef.value?.close()
// visible.value = false
// return loadFolders().then(() => emit('moved', updatedFolder))
// })
// .finally(() => {
Expand All @@ -82,17 +82,13 @@ function open(folder: WorkspaceFolder) {
visible.value = true
}

function close() {
visible.value = false
resetData()
}
function resetData() {
movingFolder.value = undefined
searchKeyword.value = ''
selectedTargetId.value = ''
}

defineExpose({ close, open })
defineExpose({ open })
</script>

<template>
Expand All @@ -103,7 +99,7 @@ defineExpose({ close, open })
@select="handleTargetSelect"
/>
<template #footer>
<el-button :disabled="loading" @click="close">取消</el-button>
<el-button :disabled="loading" @click="visible = false">取消</el-button>
<el-button type="primary" :disabled="!canSubmit" :loading="loading" @click="handleSubmit">
确定
</el-button>
Expand Down
18 changes: 13 additions & 5 deletions ui/src/components/global/mk-complex-search/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ import type { OptionItem } from '@/api/types'
defineOptions({ name: 'MkComplexSearch' })

interface ComplexSearchField extends OptionItem<string> {
multiple?: boolean
remoteMethod?: (query: string) => Promise<unknown> | void
}

type SearchValue = boolean | number | string

const props = withDefaults(
defineProps<{
fields: ComplexSearchField[]
}>(),
{},
)
const emit = defineEmits<{
change: [value: Record<string, boolean | number | string> | undefined]
change: [value: Record<string, SearchValue | SearchValue[]> | undefined]
}>()

// 异步搜索
Expand All @@ -37,21 +40,23 @@ function handleRemoteSearch(query: string) {
)
}

const searchValue = ref('')
const searchField = ref(props.fields[0]?.value ?? '')
const searchValue = ref<SearchValue | SearchValue[]>(props.fields[0]?.multiple ? [] : '')

const activeField = computed(() => props.fields.find(({ value }) => value === searchField.value))

function handleFieldChange() {
const shouldClearSearch = searchValue.value !== '' && searchValue.value !== undefined
const shouldClearSearch = Array.isArray(searchValue.value)
? searchValue.value.length > 0
: searchValue.value !== ''
remoteRequestId += 1
remoteLoading.value = false
searchValue.value = ''
searchValue.value = activeField.value?.multiple ? [] : ''
if (shouldClearSearch) emit('change', undefined)
}

function handleChange() {
if (searchValue.value === '' || searchValue.value === undefined) {
if (searchValue.value === '' || (Array.isArray(searchValue.value) && !searchValue.value.length)) {
emit('change', undefined)
} else {
emit('change', { [searchField.value]: searchValue.value })
Expand Down Expand Up @@ -82,8 +87,11 @@ function handleChange() {
class="mk-complex-search__value w-50!"
clearable
:loading="remoteLoading"
:multiple="activeField.multiple"
placeholder="请选择"
filterable
collapse-tags
collapse-tags-tooltip
reserve-keyword
:remote="Boolean(activeField.remoteMethod)"
:remote-method="handleRemoteSearch"
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/global/mk-table/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ defineExpose({ clearSelection, tableRef })
</div>

<footer
v-if="selectedRows.length > 0"
v-if="selectedRows.length > 0 && $slots.footerBatchActions"
class="sticky -mb-6 -ml-6 bottom-0 z-10 mt-auto flex shrink-0 items-center border-t bg-white px-6 py-4"
>
<div class="mr-4 flex items-center gap-3">
Expand Down
4 changes: 2 additions & 2 deletions ui/src/layout/LayoutHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { useRouter } from 'vue-router'

import AvatarDropdown from './avatar-dropdown/index.vue'
import WorkspaceDropdown from './workspace-dropdown/index.vue'
import HeaderWorkspaceDropdown from './header-workspace-dropdown/index.vue'
import { isWorkspace, isSystem } from '@/router/admin/utils'
import type { LayoutMode } from './types'
const router = useRouter()
Expand Down Expand Up @@ -30,7 +30,7 @@ function switchMode() {
<div class="flex items-center gap-5 ml-5">
<el-divider direction="vertical" />
<!-- 企业版: 工作空间下拉框 -->
<WorkspaceDropdown v-if="isWorkspace(mode)" />
<HeaderWorkspaceDropdown v-if="isWorkspace(mode)" />
<span v-if="isSystem(mode)" class="text-lg">系统管理</span>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import WorkspaceDropdown from '@/components/business/workspace-dropdown/index.vu
import { useStore } from '@/stores'
import type { WorkspaceItem } from '@/api/types'

defineOptions({ name: 'WorkspaceDropdown' })
defineOptions({ name: 'HeaderWorkspaceDropdown' })
const router = useRouter()
const route = useRoute()
const { user } = useStore()
Expand Down
101 changes: 64 additions & 37 deletions ui/src/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -60,42 +60,7 @@ h6 {
line-height: var(--mk-line-height-base);
}

/*
标题前带竖线样式
*/
.mk-title-decoration {
position: relative;
padding-left: 10px;
&:before {
position: absolute;
left: 2px;
top: 50%;
transform: translate(-50%, -50%);
width: 2px;
height: 67%;
content: '';
background: var(--el-color-primary);
}
}

/*
必填文字的星号
*/
.mk-required {
&:after {
content: '*';
color: var(--el-color-danger);
margin-left: 4px;
}
}

// 资源的card grid布局
.mk-resource-card-grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
}

// Codemirror 编辑器
.cm-editor {
--el-scrollbar-bg-color: var(--el-text-color-secondary);
--el-scrollbar-hover-bg-color: var(--el-text-color-secondary);
Expand Down Expand Up @@ -146,6 +111,68 @@ h6 {
border: none;
}
&.cm-focused {
outline: none!important;
outline: none !important;
}
}

/*
标题前带竖线样式
*/
.mk-title-decoration {
position: relative;
padding-left: 10px;
&:before {
position: absolute;
left: 2px;
top: 50%;
transform: translate(-50%, -50%);
width: 2px;
height: 67%;
content: '';
background: var(--el-color-primary);
}
}

/*
必填文字的星号
*/
.mk-required {
&:after {
content: '*';
color: var(--el-color-danger);
margin-left: 4px;
}
}

// 资源的card grid布局
.mk-resource-card-grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
}

// radio-group 垂直排列
.vertical-radio-group {
align-items: stretch;
display: flex;
flex-direction: column;
gap: calc(var(--spacing) * 3);
width: 100%;

:deep(.el-radio) {
align-items: flex-start;
height: auto;
margin-right: 0;
white-space: normal;
}

:deep(.el-radio__input) {
margin-top: 3px;
}

:deep(.el-radio__label) {
line-height: var(--mk-line-height-base);
min-width: 0;
padding-left: calc(var(--spacing) * 2);
}
}
Loading
Loading