feat(mindspace): add per-user OA cloud drive with fds-style file manager
Expose authenticated /api/mindspace/v1/oa/drive APIs over each user's oa/ workspace and wire a MindSpace entry at /space/oa/drive. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -512,6 +512,7 @@ type MindSpaceRouteSync = {
|
||||
pushWechatConfig: () => void;
|
||||
pushCategory: (code: MindSpaceSaveCategory | MindSpaceCategory['code']) => void;
|
||||
pushPage: (pageId: string) => void;
|
||||
pushOaDrive?: () => void;
|
||||
};
|
||||
|
||||
function MindSpaceBalanceRing() {
|
||||
@@ -2862,15 +2863,26 @@ export function MindSpaceView({
|
||||
</p>
|
||||
<h2>{selectedCategory.name}</h2>
|
||||
</div>
|
||||
{['oa', 'public', 'health'].includes(selectedCategory.code) && (
|
||||
<button
|
||||
type="button"
|
||||
className="mindspace-primary"
|
||||
onClick={() => openUpload(selectedCategory)}
|
||||
>
|
||||
{selectedCategory.code === 'health' ? '上传报告' : '上传资料'}
|
||||
</button>
|
||||
)}
|
||||
<div className="mindspace-category-actions">
|
||||
{selectedCategory.code === 'oa' && routeSync?.pushOaDrive && (
|
||||
<button
|
||||
type="button"
|
||||
className="mindspace-secondary"
|
||||
onClick={() => routeSync.pushOaDrive?.()}
|
||||
>
|
||||
文件管理
|
||||
</button>
|
||||
)}
|
||||
{['oa', 'public', 'health'].includes(selectedCategory.code) && (
|
||||
<button
|
||||
type="button"
|
||||
className="mindspace-primary"
|
||||
onClick={() => openUpload(selectedCategory)}
|
||||
>
|
||||
{selectedCategory.code === 'health' ? '上传报告' : '上传资料'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedCategory.code === 'health' ? (
|
||||
|
||||
@@ -12898,3 +12898,31 @@ body,
|
||||
max-width: 58%;
|
||||
}
|
||||
}
|
||||
|
||||
.mindspace-category-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.oa-drive-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100dvh;
|
||||
background: #f4f1ea;
|
||||
}
|
||||
|
||||
.oa-drive-toolbar {
|
||||
flex: 0 0 auto;
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid rgba(24, 33, 29, 0.08);
|
||||
background: #fffaf0;
|
||||
}
|
||||
|
||||
.oa-drive-frame {
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { matchPath, useLocation, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { MindSpaceView } from '../components/MindSpaceView';
|
||||
import { OaDriveRoute } from './OaDriveRoute';
|
||||
import type { MindSpaceSaveCategory, PortalUser } from '../types';
|
||||
|
||||
const CATEGORY_CODES = new Set<MindSpaceSaveCategory | 'health'>(['draft', 'oa', 'private', 'public', 'health']);
|
||||
@@ -28,9 +29,14 @@ export function MindSpaceRoute({
|
||||
const pageMatch = matchPath('/space/page/:pageId', location.pathname);
|
||||
const achievementsMatch = matchPath('/space/achievements', location.pathname);
|
||||
const wechatConfigMatch = matchPath('/space/wechat-config', location.pathname);
|
||||
const oaDriveMatch = matchPath('/space/oa/drive', location.pathname);
|
||||
const pageId = pageMatch?.params.pageId ?? null;
|
||||
const categoryCode = parseCategory(searchParams.get('category'), healthEnabled);
|
||||
|
||||
if (oaDriveMatch) {
|
||||
return <OaDriveRoute user={user} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<MindSpaceView
|
||||
user={user}
|
||||
@@ -48,6 +54,7 @@ export function MindSpaceRoute({
|
||||
pushWechatConfig: () => navigate('/space/wechat-config'),
|
||||
pushCategory: (code) => navigate(`/space?category=${code}`),
|
||||
pushPage: (id) => navigate(`/space/page/${id}`),
|
||||
pushOaDrive: () => navigate('/space/oa/drive'),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Navigate, useNavigate } from 'react-router-dom';
|
||||
import type { PortalUser } from '../types';
|
||||
|
||||
export function OaDriveRoute({
|
||||
user,
|
||||
}: {
|
||||
user: PortalUser | null;
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (!user) {
|
||||
return <Navigate to="/?return_to=/space/oa/drive" replace />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="oa-drive-shell">
|
||||
<div className="oa-drive-toolbar">
|
||||
<button type="button" className="mindspace-inline-back" onClick={() => navigate('/space?category=oa')}>
|
||||
返回 OA 工作区
|
||||
</button>
|
||||
</div>
|
||||
<iframe
|
||||
className="oa-drive-frame"
|
||||
title="OA 网盘"
|
||||
src="/oa-drive/index.html"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user