修正了部分页面显示问题
parent
e2211fbc9b
commit
e6794a1952
|
|
@ -2,6 +2,8 @@ import { useEffect } from 'react'
|
||||||
import { BrowserRouter, Routes, Route, Navigate, useParams, Outlet } from 'react-router-dom'
|
import { BrowserRouter, Routes, Route, Navigate, useParams, Outlet } from 'react-router-dom'
|
||||||
import { ConfigProvider, theme } from 'antd'
|
import { ConfigProvider, theme } from 'antd'
|
||||||
import zhCN from 'antd/locale/zh_CN'
|
import zhCN from 'antd/locale/zh_CN'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import 'dayjs/locale/zh-cn'
|
||||||
import useThemeStore from '@/stores/themeStore'
|
import useThemeStore from '@/stores/themeStore'
|
||||||
import Login from '@/pages/Login/Login'
|
import Login from '@/pages/Login/Login'
|
||||||
import ProjectList from '@/pages/ProjectList/ProjectList'
|
import ProjectList from '@/pages/ProjectList/ProjectList'
|
||||||
|
|
@ -21,6 +23,8 @@ import ProtectedRoute from '@/components/ProtectedRoute'
|
||||||
import MainLayout from '@/components/MainLayout/MainLayout'
|
import MainLayout from '@/components/MainLayout/MainLayout'
|
||||||
import '@/App.css'
|
import '@/App.css'
|
||||||
|
|
||||||
|
dayjs.locale('zh-cn')
|
||||||
|
|
||||||
// 重定向到文档页面的组件
|
// 重定向到文档页面的组件
|
||||||
function RedirectToDocs() {
|
function RedirectToDocs() {
|
||||||
const { projectId } = useParams()
|
const { projectId } = useParams()
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,51 @@
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
color: #333;
|
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 日历卡片 */
|
||||||
.calendar-card {
|
.calendar-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-card .ant-picker-calendar {
|
.calendar-card .ant-picker-calendar {
|
||||||
padding: 12px;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 日历头部选择器 */
|
||||||
|
.calendar-card .ant-picker-calendar-header {
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 日历单元格 - 迷你模式 */
|
||||||
|
.calendar-card .ant-picker-cell {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-card .ant-picker-cell .ant-picker-cell-inner {
|
||||||
|
min-width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Badge 相对于日期本身定位 */
|
||||||
|
.calendar-card .ant-badge {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-card .ant-badge-count {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
right: -2px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 活动卡片 */
|
||||||
.activity-card {
|
.activity-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
|
|
@ -58,17 +89,17 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 日历单元格样式 */
|
/* 暗色模式适配 */
|
||||||
.ant-picker-calendar-date {
|
body.dark .activity-item-clickable:hover {
|
||||||
position: relative;
|
background-color: rgba(24, 144, 255, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-picker-calendar-date-today {
|
body.dark .activity-item-clickable:active {
|
||||||
border-color: #1890ff;
|
background-color: rgba(24, 144, 255, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-picker-calendar-date-selected {
|
body.dark .activity-item-disabled:hover {
|
||||||
background-color: #e6f7ff;
|
background-color: rgba(255, 255, 255, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式调整 */
|
/* 响应式调整 */
|
||||||
|
|
|
||||||
|
|
@ -53,22 +53,31 @@ function Desktop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 日历单元格渲染
|
// 日历单元格渲染
|
||||||
const dateCellRender = (value) => {
|
const dateCellRender = (value, info) => {
|
||||||
|
if (info.type !== 'date') return info.originNode
|
||||||
|
|
||||||
const dateStr = value.format('YYYY-MM-DD')
|
const dateStr = value.format('YYYY-MM-DD')
|
||||||
const activity = activityDates.find(item => item.date === dateStr)
|
const activity = activityDates.find(item => item.date === dateStr)
|
||||||
|
|
||||||
if (activity && activity.count > 0) {
|
if (activity && activity.count > 0) {
|
||||||
return (
|
return (
|
||||||
<div style={{ textAlign: 'center' }}>
|
<Badge
|
||||||
<Badge
|
count={activity.count}
|
||||||
count={activity.count}
|
style={{
|
||||||
style={{ backgroundColor: '#1890ff' }}
|
backgroundColor: '#ff4d4f',
|
||||||
overflowCount={99}
|
fontSize: '10px',
|
||||||
/>
|
height: '16px',
|
||||||
</div>
|
lineHeight: '16px',
|
||||||
|
minWidth: '16px',
|
||||||
|
padding: '0 4px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{info.originNode}
|
||||||
|
</Badge>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
|
return info.originNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// 日期选择事件
|
// 日期选择事件
|
||||||
|
|
@ -114,7 +123,7 @@ function Desktop() {
|
||||||
value={selectedDate}
|
value={selectedDate}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onPanelChange={onPanelChange}
|
onPanelChange={onPanelChange}
|
||||||
cellRender={dateCellRender}
|
fullCellRender={dateCellRender}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import {
|
||||||
FilePdfOutlined,
|
FilePdfOutlined,
|
||||||
FileTextOutlined,
|
FileTextOutlined,
|
||||||
UndoOutlined,
|
UndoOutlined,
|
||||||
|
CloseOutlined,
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { Editor } from '@bytemd/react'
|
import { Editor } from '@bytemd/react'
|
||||||
import gfm from '@bytemd/plugin-gfm'
|
import gfm from '@bytemd/plugin-gfm'
|
||||||
|
|
@ -889,7 +890,11 @@ function DocumentEditor() {
|
||||||
className="document-sider"
|
className="document-sider"
|
||||||
>
|
>
|
||||||
<div className="sider-header">
|
<div className="sider-header">
|
||||||
<h2>{projectName}</h2>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
|
||||||
|
<h2 style={{ margin: 0, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={projectName}>
|
||||||
|
{projectName}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
<div className="sider-actions">
|
<div className="sider-actions">
|
||||||
<Space size={8}>
|
<Space size={8}>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -75,3 +75,46 @@
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--text-color-secondary);
|
color: var(--text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 圆点分页指示器样式 */
|
||||||
|
.dot-pagination.ant-pagination {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot-pagination .ant-pagination-item {
|
||||||
|
border: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
min-width: 16px !important;
|
||||||
|
height: 16px !important;
|
||||||
|
line-height: 16px !important;
|
||||||
|
margin: 0 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot-pagination .ant-pagination-item a {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot-pagination .pagination-dot {
|
||||||
|
display: block;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--text-color-secondary);
|
||||||
|
opacity: 0.3;
|
||||||
|
margin: 4px auto;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot-pagination .ant-pagination-item-active .pagination-dot {
|
||||||
|
background-color: var(--link-color);
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot-pagination .ant-pagination-prev,
|
||||||
|
.dot-pagination .ant-pagination-next {
|
||||||
|
min-width: 24px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
line-height: 24px !important;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Card, Empty, Modal, Form, Input, Row, Col, Space, Button, Switch, message, Select, Table, Tag } from 'antd'
|
import { Card, Empty, Modal, Form, Input, Row, Col, Space, Button, Switch, message, Select, Table, Tag, Pagination } from 'antd'
|
||||||
import { PlusOutlined, FolderOutlined, TeamOutlined, EyeOutlined, ShareAltOutlined, CopyOutlined, DeleteOutlined, EditOutlined, FileOutlined, GithubOutlined, CheckOutlined, SwapOutlined } from '@ant-design/icons'
|
import { PlusOutlined, FolderOutlined, TeamOutlined, EyeOutlined, ShareAltOutlined, CopyOutlined, DeleteOutlined, EditOutlined, FileOutlined, GithubOutlined, CheckOutlined, SwapOutlined } from '@ant-design/icons'
|
||||||
import { getMyProjects, getOwnedProjects, getSharedProjects, createProject, deleteProject, updateProject, getProjectMembers, addProjectMember, removeProjectMember, getGitRepos, createGitRepo, updateGitRepo, deleteGitRepo, transferProject } from '@/api/project'
|
import { getMyProjects, getOwnedProjects, getSharedProjects, createProject, deleteProject, updateProject, getProjectMembers, addProjectMember, removeProjectMember, getGitRepos, createGitRepo, updateGitRepo, deleteGitRepo, transferProject } from '@/api/project'
|
||||||
import { getProjectShareInfo, updateShareSettings } from '@/api/share'
|
import { getProjectShareInfo, updateShareSettings } from '@/api/share'
|
||||||
|
|
@ -36,11 +36,18 @@ function ProjectList({ type = 'my' }) {
|
||||||
const [transferModalVisible, setTransferModalVisible] = useState(false)
|
const [transferModalVisible, setTransferModalVisible] = useState(false)
|
||||||
const [transferForm] = Form.useForm()
|
const [transferForm] = Form.useForm()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const [currentPage, setCurrentPage] = useState(1)
|
||||||
|
const pageSize = 8
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchProjects()
|
fetchProjects()
|
||||||
|
setCurrentPage(1)
|
||||||
}, [type])
|
}, [type])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentPage(1)
|
||||||
|
}, [searchKeyword])
|
||||||
|
|
||||||
// ... (fetchProjects code)
|
// ... (fetchProjects code)
|
||||||
|
|
||||||
const handleOpenTransfer = async () => {
|
const handleOpenTransfer = async () => {
|
||||||
|
|
@ -472,6 +479,8 @@ function ProjectList({ type = 'my' }) {
|
||||||
)
|
)
|
||||||
: projects
|
: projects
|
||||||
|
|
||||||
|
const paginatedProjects = filteredProjects.slice((currentPage - 1) * pageSize, currentPage * pageSize)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="project-list-container">
|
<div className="project-list-container">
|
||||||
<ListActionBar
|
<ListActionBar
|
||||||
|
|
@ -545,55 +554,75 @@ function ProjectList({ type = 'my' }) {
|
||||||
|
|
||||||
{/* 正常项目列表 */}
|
{/* 正常项目列表 */}
|
||||||
{!hasSearched && (
|
{!hasSearched && (
|
||||||
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
|
<>
|
||||||
{filteredProjects.map((project) => (
|
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
|
||||||
<Col xs={24} sm={12} md={8} lg={6} key={project.id}>
|
{paginatedProjects.map((project) => (
|
||||||
<Card
|
<Col xs={24} sm={12} md={8} lg={6} key={project.id}>
|
||||||
hoverable
|
<Card
|
||||||
className="project-card"
|
hoverable
|
||||||
onClick={() => handleOpenProject(project.id)}
|
className="project-card"
|
||||||
actions={type === 'my' ? [
|
onClick={() => handleOpenProject(project.id)}
|
||||||
<EditOutlined key="edit" onClick={(e) => handleEdit(e, project)} />,
|
actions={type === 'my' ? [
|
||||||
<GithubOutlined key="git" onClick={(e) => handleGitSettings(e, project)} />,
|
<EditOutlined key="edit" onClick={(e) => handleEdit(e, project)} />,
|
||||||
<ShareAltOutlined key="share" onClick={(e) => handleShare(e, project)} />,
|
<GithubOutlined key="git" onClick={(e) => handleGitSettings(e, project)} />,
|
||||||
<TeamOutlined key="members" onClick={(e) => handleMembers(e, project)} />,
|
<ShareAltOutlined key="share" onClick={(e) => handleShare(e, project)} />,
|
||||||
] : [
|
<TeamOutlined key="members" onClick={(e) => handleMembers(e, project)} />,
|
||||||
<EyeOutlined key="view" />,
|
] : [
|
||||||
<ShareAltOutlined key="share" onClick={(e) => handleShare(e, project)} />,
|
<EyeOutlined key="view" />,
|
||||||
]}
|
<ShareAltOutlined key="share" onClick={(e) => handleShare(e, project)} />,
|
||||||
>
|
]}
|
||||||
{/* 公开项目标识 */}
|
>
|
||||||
{project.is_public === 1 && (
|
{/* 公开项目标识 */}
|
||||||
<div className="project-card-public-badge">公开</div>
|
{project.is_public === 1 && (
|
||||||
)}
|
<div className="project-card-public-badge">公开</div>
|
||||||
<div className="project-card-icon">
|
|
||||||
<FolderOutlined style={{ fontSize: 48, color: '#1890ff' }} />
|
|
||||||
</div>
|
|
||||||
<h3>{project.name}</h3>
|
|
||||||
<p className="project-description">{project.description || '暂无描述'}</p>
|
|
||||||
<div className="project-meta">
|
|
||||||
<span>文档数: {project.doc_count || 0}</span>
|
|
||||||
{type === 'share' && project.owner_name && (
|
|
||||||
<span style={{ marginLeft: 12 }}>
|
|
||||||
所有者: {project.owner_nickname || project.owner_name}
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
{type === 'share' && project.user_role && (
|
<div className="project-card-icon">
|
||||||
<span style={{ marginLeft: 12 }}>
|
<FolderOutlined style={{ fontSize: 48, color: '#1890ff' }} />
|
||||||
角色: {project.user_role === 'admin' ? '管理者' : project.user_role === 'editor' ? '编辑者' : '查看者'}
|
</div>
|
||||||
</span>
|
<h3>{project.name}</h3>
|
||||||
)}
|
<p className="project-description">{project.description || '暂无描述'}</p>
|
||||||
</div>
|
<div className="project-meta">
|
||||||
</Card>
|
<span>文档数: {project.doc_count || 0}</span>
|
||||||
</Col>
|
{type === 'share' && project.owner_name && (
|
||||||
))}
|
<span style={{ marginLeft: 12 }}>
|
||||||
|
所有者: {project.owner_nickname || project.owner_name}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{type === 'share' && project.user_role && (
|
||||||
|
<span style={{ marginLeft: 12 }}>
|
||||||
|
角色: {project.user_role === 'admin' ? '管理者' : project.user_role === 'editor' ? '编辑者' : '查看者'}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
|
||||||
{filteredProjects.length === 0 && !loading && (
|
{filteredProjects.length === 0 && !loading && (
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Empty description={type === 'my' ? "还没有项目,创建一个开始吧" : "还没有参与的项目"} />
|
<Empty description={type === 'my' ? "还没有项目,创建一个开始吧" : "还没有参与的项目"} />
|
||||||
</Col>
|
</Col>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
{filteredProjects.length > 0 && (
|
||||||
|
<div style={{ marginTop: 24, display: 'flex', justifyContent: 'center' }}>
|
||||||
|
<Pagination
|
||||||
|
current={currentPage}
|
||||||
|
pageSize={pageSize}
|
||||||
|
total={filteredProjects.length}
|
||||||
|
onChange={setCurrentPage}
|
||||||
|
showSizeChanger={false}
|
||||||
|
className="dot-pagination"
|
||||||
|
itemRender={(page, type, originalElement) => {
|
||||||
|
if (type === 'page') {
|
||||||
|
return <span className="pagination-dot" />
|
||||||
|
}
|
||||||
|
return originalElement
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Row>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 搜索无结果提示 */}
|
{/* 搜索无结果提示 */}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue