-
diff --git a/apps/web/src/pages/workspace/WorkspaceDetail.tsx b/apps/web/src/pages/workspace/WorkspaceDetail.tsx
deleted file mode 100644
index 0d95e4cc6..000000000
--- a/apps/web/src/pages/workspace/WorkspaceDetail.tsx
+++ /dev/null
@@ -1,211 +0,0 @@
-/**
- * 工作空间详情页面 - V21 UI
- */
-import React, { useState } from 'react';
-import { useParams, useNavigate } from 'react-router-dom';
-import { Tabs, Button, Descriptions, Card, List, Modal, Form, Input, message, Alert } from 'antd';
-import { PlusOutlined, TeamOutlined } from '@ant-design/icons';
-import { useWorkspace } from '@/hooks/useWorkspace';
-import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
-import { createProject, getProjects } from '@/api/projects';
-import MemberList from '@/components/business/MemberList';
-import InviteMemberModal from '@/components/business/InviteMemberModal';
-import PermissionMatrix from '@/components/business/PermissionMatrix';
-import '@/components/business/business.css';
-
-const WorkspaceDetail: React.FC = () => {
- const { id } = useParams<{ id: string }>();
- const navigate = useNavigate();
- const { data: workspace, isLoading } = useWorkspace(id!);
- const queryClient = useQueryClient();
- const [inviteModalOpen, setInviteModalOpen] = useState(false);
- const [createProjectOpen, setCreateProjectOpen] = useState(false);
- const [projectForm] = Form.useForm<{ name: string; description?: string }>();
-
- const projectsQuery = useQuery({
- queryKey: ['projects', id],
- queryFn: () => getProjects(id!),
- enabled: !!id,
- });
-
- const createProjectMutation = useMutation({
- mutationFn: createProject,
- onSuccess: (project) => {
- message.success('项目创建成功');
- setCreateProjectOpen(false);
- projectForm.resetFields();
- queryClient.invalidateQueries({ queryKey: ['projects', id] });
- navigate(`/projects/${project.id}/assets`, { state: { workspaceId: id } });
- },
- onError: (error: any) => {
- message.error(error.response?.data?.detail || '项目创建失败,请稍后重试');
- },
- });
-
- const handleCreateProject = async () => {
- const values = await projectForm.validateFields();
- createProjectMutation.mutate({
- workspace_id: id!,
- name: values.name,
- description: values.description || '',
- });
- };
-
- if (isLoading) return ;
- if (!workspace) return ;
-
- const items = [
- {
- key: 'overview',
- label: '概览',
- children: (
-
-
setCreateProjectOpen(true)}>
- 创建项目
-
- }
- >
-
- {workspace.name}
- {workspace.subscription_plan}
- {workspace.subscription_status}
-
- {workspace.created_at
- ? new Date(workspace.created_at).toLocaleDateString()
- : '暂未返回'}
-
-
-
-
-
- {projectsQuery.isError && (
-
- )}
- (
- navigate(`/projects/${project.id}/assets`, { state: { workspaceId: id } })}>
- 素材管理
- ,
- navigate(`/projects/${project.id}/titles`, { state: { workspaceId: id } })}>
- 标题库
- ,
- navigate(`/projects/${project.id}/generation`, { state: { workspaceId: id } })}>
- 视频生成
- ,
- navigate(`/projects/${project.id}/tasks`, { state: { workspaceId: id } })}>
- 任务中心
- ,
- navigate(`/projects/${project.id}/results`, { state: { workspaceId: id } })}>
- 生成结果
- ,
- ]}
- >
-
-
- )}
- />
-
-
-
-
- ),
- },
- {
- key: 'members',
- label: (
- <>
- 成员管理
- >
- ),
- children: (
-
-
-
-
工作空间成员
-
管理有权访问此工作空间的成员
-
-
}
- onClick={() => setInviteModalOpen(true)}
- >
- 邀请成员
-
-
-
-
-
-
-
- ),
- },
- {
- key: 'settings',
- label: '设置',
- children: ,
- },
- ];
-
- return (
-
-
-
-
{workspace.name}
-
工作空间详情与成员管理
-
-
-
-
setCreateProjectOpen(false)}
- className="xx-modal"
- >
-
-
-
-
-
-
-
-
-
setInviteModalOpen(false)}
- />
-
- );
-};
-
-export default WorkspaceDetail;
-export const Component = WorkspaceDetail;
diff --git a/apps/web/src/pages/workspace/WorkspaceList.css b/apps/web/src/pages/workspace/WorkspaceList.css
deleted file mode 100644
index 92599a760..000000000
--- a/apps/web/src/pages/workspace/WorkspaceList.css
+++ /dev/null
@@ -1,202 +0,0 @@
-/* V21 工作空间列表页面 */
-.xx-workspace-list {
- max-width: 1200px;
- margin: 0 auto;
-}
-
-/* Hero 区域 - V21 设计 */
-.xx-hero {
- text-align: center;
- padding: 60px 24px 80px;
- position: relative;
-}
-
-.xx-hero::before {
- content: '';
- position: absolute;
- top: 0;
- left: 50%;
- transform: translateX(-50%);
- width: 300px;
- height: 300px;
- background: radial-gradient(circle, rgba(79,70,229,0.15), transparent 70%);
- border-radius: 50%;
- filter: blur(40px);
-}
-
-.xx-hero h1 {
- font-size: 42px;
- font-weight: 900;
- color: var(--slate, #0f172a);
- margin: 0 0 16px;
- letter-spacing: -0.02em;
- position: relative;
-}
-
-.xx-hero p {
- font-size: 18px;
- color: var(--muted, #64748b);
- margin: 0 0 32px;
- max-width: 560px;
- margin-left: auto;
- margin-right: auto;
- line-height: 1.6;
-}
-
-/* 功能卡片网格 - V21 */
-.xx-features-grid {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 20px;
- margin-bottom: 60px;
-}
-
-@media (max-width: 1200px) {
- .xx-features-grid {
- grid-template-columns: repeat(2, 1fr);
- }
-}
-
-@media (max-width: 768px) {
- .xx-features-grid {
- grid-template-columns: 1fr;
- }
-}
-
-.xx-feature-card {
- background: rgba(255,255,255,0.94);
- border: 1px solid rgba(226,232,240,0.95);
- border-radius: 24px;
- padding: 28px;
- text-align: center;
- transition: all 0.25s;
- cursor: pointer;
-}
-
-.xx-feature-card:hover {
- transform: translateY(-4px);
- box-shadow: 0 26px 64px rgba(15,23,42,0.14);
- border-color: var(--indigo, #4f46e5);
-}
-
-.xx-feature-icon {
- width: 56px;
- height: 56px;
- margin: 0 auto 16px;
- background: linear-gradient(135deg, #6366f1, #4f46e5);
- border-radius: 16px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 24px;
-}
-
-.xx-feature-card h3 {
- font-size: 16px;
- font-weight: 850;
- color: var(--slate, #0f172a);
- margin: 0 0 8px;
-}
-
-.xx-feature-card p {
- font-size: 13px;
- color: var(--muted, #64748b);
- margin: 0;
-}
-
-/* 页面头部 */
-.xx-page-head {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- gap: 18px;
- margin-bottom: 22px;
-}
-
-.xx-page-head h2 {
- font-size: 28px;
- margin: 0 0 6px;
- color: var(--slate, #0f172a);
-}
-
-.xx-page-head p {
- margin: 0;
- color: var(--muted, #64748b);
- font-size: 14px;
-}
-
-.xx-grid4 {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 16px;
-}
-
-@media (max-width: 1200px) {
- .xx-grid4 {
- grid-template-columns: repeat(3, 1fr);
- }
-}
-
-@media (max-width: 768px) {
- .xx-grid4 {
- grid-template-columns: repeat(2, 1fr);
- }
-}
-
-@media (max-width: 480px) {
- .xx-grid4 {
- grid-template-columns: 1fr;
- }
-}
-
-.xx-workspace-card {
- border: 1px solid var(--line, #e2e8f0);
- background: rgba(255, 255, 255, 0.94);
- border-radius: 24px;
- padding: 18px;
- cursor: pointer;
- transition: all 0.25s;
-}
-
-.xx-workspace-card:hover {
- border-color: var(--indigo, #4f46e5);
- box-shadow: 0 26px 64px rgba(15,23,42,0.14);
- transform: translateY(-2px);
-}
-
-.xx-workspace-name {
- font-size: 16px;
- font-weight: 850;
- color: var(--slate, #0f172a);
- margin-bottom: 12px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.xx-workspace-plan {
- display: inline-block;
- font-size: 12px;
- font-weight: 850;
- padding: 4px 10px;
- border-radius: 10px;
- background: var(--indigo-soft, #eef2ff);
- color: var(--indigo-dark, #4338ca);
- margin-bottom: 12px;
-}
-
-.xx-workspace-date {
- font-size: 12px;
- color: #94a3b8;
-}
-
-.xx-empty-state {
- text-align: center;
- padding: 60px 24px;
- color: var(--muted, #64748b);
-}
-
-.xx-empty-state p {
- font-size: 16px;
- margin: 12px 0;
-}
diff --git a/apps/web/src/pages/workspace/WorkspaceList.tsx b/apps/web/src/pages/workspace/WorkspaceList.tsx
deleted file mode 100644
index 717b4fb25..000000000
--- a/apps/web/src/pages/workspace/WorkspaceList.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * 工作空间列表页面 - V21 UI
- */
-import React from 'react';
-import { Button, Modal, Form, Input, message } from 'antd';
-import { useNavigate } from 'react-router-dom';
-import { useCreateWorkspace, useWorkspaces } from '@/hooks/useWorkspace';
-import './WorkspaceList.css';
-
-const WorkspaceList: React.FC = () => {
- const navigate = useNavigate();
- const { data: workspaces, isLoading } = useWorkspaces();
- const createWorkspaceMutation = useCreateWorkspace();
- const [form] = Form.useForm<{ name: string }>();
- const [createOpen, setCreateOpen] = React.useState(false);
-
- const openCreateModal = () => setCreateOpen(true);
-
- const handleCreateWorkspace = async () => {
- const values = await form.validateFields();
- try {
- const workspace = await createWorkspaceMutation.mutateAsync({ name: values.name });
- message.success('创建工作空间成功');
- setCreateOpen(false);
- form.resetFields();
- navigate(`/workspaces/${workspace.id}`);
- } catch (createError: any) {
- message.error(createError.response?.data?.detail || '创建失败');
- }
- };
-
- // 功能卡片数据
- const features = [
- { id: 'assets', icon: '🎬', title: '素材管理', desc: '上传管理视频素材' },
- { id: 'titles', icon: '✍️', title: '标题库', desc: '创作吸引眼球的标题' },
- { id: 'voices', icon: '🎙️', title: '配音库', desc: '选择优质配音音色' },
- { id: 'generation', icon: '✨', title: '剪辑计划', desc: 'AI智能生成成片' },
- ];
-
- return (
-
- {/* Hero 区域 - V21 设计 */}
-
-
小虾剪辑 SaaS
-
专业的AI视频剪辑平台,一站式管理素材、标题、配音,轻松创作优质内容
-
- + 创建工作空间
-
-
-
- {/* 功能卡片 - V21 设计 */}
-
- {features.map((feature) => (
-
-
{feature.icon}
-
{feature.title}
-
{feature.desc}
-
- ))}
-
-
- {/* 工作空间列表 */}
-
-
-
我的工作空间
-
选择或创建一个工作空间开始创作
-
-
-
-
- {workspaces?.map((workspace) => (
-
navigate(`/workspaces/${workspace.id}`)}
- >
-
{workspace.name}
-
- {workspace.subscription_plan === 'enterprise' ? '企业版' : workspace.subscription_plan === 'pro' ? 'Pro' : 'Free'}
-
-
- {workspace.created_at ? new Date(workspace.created_at).toLocaleDateString() : ''}
-
-
- ))}
-
-
- {!isLoading && workspaces?.length === 0 && (
-
-
🚀
-
还没有工作空间
-
- + 创建你的第一个工作空间
-
-
- )}
-
-
setCreateOpen(false)}
- >
-
-
-
-
-
-
- );
-};
-
-export default WorkspaceList;
-export const Component = WorkspaceList;
diff --git a/apps/web/src/router/index.tsx b/apps/web/src/router/index.tsx
index 5f9727e2a..ae63b22dc 100644
--- a/apps/web/src/router/index.tsx
+++ b/apps/web/src/router/index.tsx
@@ -50,15 +50,7 @@ export const router = createBrowserRouter([
children: [
{
index: true,
- element: ,
- },
- {
- path: 'workspaces',
- lazy: () => import('@/pages/workspace/WorkspaceList'),
- },
- {
- path: 'workspaces/:id',
- lazy: () => import('@/pages/workspace/WorkspaceDetail'),
+ element: ,
},
{
path: 'projects/:id/assets',
@@ -89,7 +81,7 @@ export const router = createBrowserRouter([
lazy: () => import('@/pages/subscription/Plans'),
},
{
- path: 'subscription/upgrade/:workspaceId',
+ path: 'subscription/upgrade',
lazy: () => import('@/pages/subscription/UpgradeSubscription'),
},
{
diff --git a/apps/web/src/store/workspaceStore.ts b/apps/web/src/store/workspaceStore.ts
deleted file mode 100644
index eab0d2188..000000000
--- a/apps/web/src/store/workspaceStore.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * 工作空间状态管理
- */
-import { create } from 'zustand';
-import { Workspace } from '@/api/workspace';
-
-interface WorkspaceState {
- currentWorkspace: Workspace | null;
- workspaces: Workspace[];
-
- // Actions
- setCurrentWorkspace: (workspace: Workspace | null) => void;
- setWorkspaces: (workspaces: Workspace[]) => void;
- addWorkspace: (workspace: Workspace) => void;
- removeWorkspace: (workspaceId: string) => void;
-}
-
-export const useWorkspaceStore = create((set) => ({
- currentWorkspace: null,
- workspaces: [],
-
- setCurrentWorkspace: (workspace) => {
- set({ currentWorkspace: workspace });
- },
-
- setWorkspaces: (workspaces) => {
- set({ workspaces });
- },
-
- addWorkspace: (workspace) => {
- set((state) => ({
- workspaces: [...state.workspaces, workspace],
- }));
- },
-
- removeWorkspace: (workspaceId) => {
- set((state) => ({
- workspaces: state.workspaces.filter((w) => w.id !== workspaceId),
- currentWorkspace:
- state.currentWorkspace?.id === workspaceId ? null : state.currentWorkspace,
- }));
- },
-}));
diff --git a/apps/web/src/test/components/WorkspaceList.test.tsx b/apps/web/src/test/components/WorkspaceList.test.tsx
deleted file mode 100644
index 466bad0b5..000000000
--- a/apps/web/src/test/components/WorkspaceList.test.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * WorkspaceList 组件单元测试
- */
-import { render, screen, waitFor } from '@testing-library/react';
-import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { BrowserRouter } from 'react-router-dom';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import WorkspaceList from '@/pages/workspace/WorkspaceList';
-import * as workspaceApi from '@/api/workspace';
-
-vi.mock('@/api/workspace');
-
-const renderWorkspaceList = () => {
- const queryClient = new QueryClient({
- defaultOptions: {
- queries: { retry: false },
- },
- });
-
- return render(
-
-
-
-
-
- );
-};
-
-describe('WorkspaceList', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- });
-
- it('should render workspace list', async () => {
- const mockWorkspaces: workspaceApi.Workspace[] = [
- {
- id: '1',
- name: 'Test Workspace',
- owner_user_id: 'user-1',
- subscription_plan: 'free',
- subscription_status: 'active',
- created_at: '2024-01-01',
- },
- ];
-
- vi.mocked(workspaceApi.getWorkspaces).mockResolvedValue(mockWorkspaces);
-
- renderWorkspaceList();
-
- await waitFor(() => {
- expect(screen.getByText('Test Workspace')).toBeInTheDocument();
- });
- });
-
- it('should show create workspace button', async () => {
- vi.mocked(workspaceApi.getWorkspaces).mockResolvedValue([]);
-
- renderWorkspaceList();
-
- await waitFor(() => {
- expect(screen.getByText('创建工作空间')).toBeInTheDocument();
- });
- });
-});