feat(doclib): 文档库支持多选文档后批量设置分享。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-11-12 17:43:16 +08:00
1 parent dd4fdf1e6e
commit 0baa6880f7
3 files changed
+55 -20

No files matched your search

@@ -11,11 +11,13 @@
:close-on-press-escape="!saving"
:show-close="!saving"
>
<p class="doclib-summary">{{ document?.name }}</p>
<p class="doclib-summary">{{ summaryText }}</p>
<el-form label-position="top">
<el-form-item>
<template #label
>分享给<span class="form-label-tip">可多选,取消选择即收回访问权限</span></template
>分享给<span class="form-label-tip"
>可多选,取消选择即收回访问权限;保存后对所选文档整份覆盖</span
></template
>
<el-select
v-model="userIds"
@@ -56,15 +58,15 @@
import { Close, Check } from '@element-plus/icons-vue'
import {
fetchDoclibUsers,
saveDoclibShares,
saveDoclibBatchShares,
type DoclibDocument,
type DoclibPerson
} from '@/api/doclib'
import { createLatestRequest } from './model'
const props = defineProps<{ modelValue: boolean; document: DoclibDocument | null }>()
import { createLatestRequest, shareUserIntersection } from './model'
const props = defineProps<{ modelValue: boolean; documents: DoclibDocument[] }>()
const emit = defineEmits<{
'update:modelValue': [value: boolean]
saved: [document: DoclibDocument]
saved: [documents: DoclibDocument[]]
}>()
const visible = computed({
get: () => props.modelValue,
@@ -77,17 +79,31 @@
const userIds = ref<string[]>([])
const people = ref<DoclibPerson[]>([])
const requests = createLatestRequest()
const ownerIds = computed(
() => new Set(props.documents.map((doc) => doc.ownerId).filter(Boolean))
)
const summaryText = computed(() => {
const names = props.documents.map((doc) => doc.name).filter(Boolean)
if (!names.length) return '未选择文档'
if (names.length === 1) return names[0]
const shown = names.slice(0, 5)
const more = names.length > 5 ? `等 ${names.length} 个` : ''
return `已选 ${names.length} 个文档:${shown.join('、')}${more ? ` ${more}` : ''}`
})
const options = computed(() => {
const candidates = new Map((props.document?.shares || []).map((person) => [person.id, person]))
const candidates = new Map<string, DoclibPerson>()
for (const doc of props.documents) {
for (const person of doc.shares || []) candidates.set(person.id, person)
}
people.value.forEach((person) => candidates.set(person.id, person))
return [...candidates.values()].filter((person) => person.id !== props.document?.ownerId)
return [...candidates.values()].filter((person) => !ownerIds.value.has(person.id))
})
watch(
() => props.modelValue,
(value) => {
requests.invalidate()
if (!value) return
userIds.value = (props.document?.shares || []).map((person) => person.id)
userIds.value = shareUserIntersection(props.documents)
people.value = []
void searchUsers('')
}
@@ -109,12 +125,17 @@
}
}
async function save() {
if (!props.document?.canManage || saving.value) return
if (!props.documents.length || props.documents.some((doc) => !doc.canManage) || saving.value) {
return
}
saving.value = true
try {
const updated = await saveDoclibShares(props.document.id, userIds.value)
const updated = await saveDoclibBatchShares(
props.documents.map((doc) => doc.id),
userIds.value
)
ElMessage.success('分享设置已保存')
emit('saved', updated)
emit('saved', updated || [])
emit('update:modelValue', false)
} catch {
/* 请求层统一展示错误 */
@@ -95,6 +95,12 @@ await context.route('**/api/**', async (route) => {
return ok({ list, total: list.length })
}
if (path === '/web/doclib/users') return ok([{ id: 'other', name: '同事', deptName: '生产部' }])
if (path === '/web/doclib/files/batch-shares' && req.method() === 'POST') {
const body = req.postDataJSON()
sharedUsers = body.userIds
file.shares = sharedUsers.map((id) => ({ id, name: '同事', deptName: '生产部' }))
return ok([file])
}
if (path.endsWith('/shares')) {
sharedUsers = req.postDataJSON().userIds
file.shares = sharedUsers.map((id) => ({ id, name: '同事', deptName: '生产部' }))
+16 -8
View File
@@ -78,7 +78,7 @@
>
<el-button
:disabled="!actions.share || loading || busy"
@click="openShare(selected[0])"
@click="openShare(selected)"
><el-icon><Share /></el-icon>设置分享</el-button
>
<el-button
@@ -195,7 +195,7 @@
@download="download([$event])"
@view="openDetail"
/>
<ShareDialog v-model="shareVisible" :document="shareDocument" @saved="shareSaved" />
<ShareDialog v-model="shareVisible" :documents="shareDocuments" @saved="shareSaved" />
<el-dialog
v-model="folderVisible"
:title="renameMode ? '重命名文件夹' : '新建文件夹'"
@@ -489,14 +489,22 @@
}
}
const shareVisible = ref(false)
const shareDocument = ref<DoclibDocument | null>(null)
function openShare(row: DoclibDocument) {
if (!row?.canManage) return
shareDocument.value = row
const shareDocuments = ref<DoclibDocument[]>([])
function openShare(rows: DoclibDocument | DoclibDocument[] | null | undefined) {
const list = (Array.isArray(rows) ? rows : rows ? [rows] : []).filter(
(row): row is DoclibDocument => !!row?.id && row.canManage
)
if (!list.length) return
shareDocuments.value = list
shareVisible.value = true
}
function shareSaved(row: DoclibDocument) {
if (detail.value?.id === row.id) detail.value = row
function shareSaved(rows: DoclibDocument[]) {
const updated = Array.isArray(rows) ? rows : []
const current = detail.value
if (current) {
const match = updated.find((row) => row.id === current.id)
if (match) detail.value = match
}
void refresh()
}
const detailVisible = ref(false)