2025-12-20 11:18:59 +00:00
|
|
|
|
import { useState, useEffect } from 'react'
|
2026-01-13 13:21:47 +00:00
|
|
|
|
import { Card, Row, Col, Calendar, List, Badge, Empty, Typography, Spin } from 'antd'
|
|
|
|
|
|
import { FileTextOutlined, ClockCircleOutlined } from '@ant-design/icons'
|
|
|
|
|
|
import { useNavigate } from 'react-router-dom'
|
|
|
|
|
|
import { getDocumentActivityDates, getDocumentActivity } from '@/api/dashboard'
|
2025-12-20 11:18:59 +00:00
|
|
|
|
import Toast from '@/components/Toast/Toast'
|
2026-01-13 13:21:47 +00:00
|
|
|
|
import dayjs from 'dayjs'
|
|
|
|
|
|
import './Desktop.css'
|
|
|
|
|
|
|
|
|
|
|
|
const { Text } = Typography
|
2025-12-20 11:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
function Desktop() {
|
2026-01-13 13:21:47 +00:00
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
|
|
const [activityDates, setActivityDates] = useState([])
|
|
|
|
|
|
const [selectedDate, setSelectedDate] = useState(dayjs())
|
|
|
|
|
|
const [activityLogs, setActivityLogs] = useState([])
|
|
|
|
|
|
const [currentMonth, setCurrentMonth] = useState(dayjs())
|
2025-12-20 11:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-01-13 13:21:47 +00:00
|
|
|
|
loadActivityDates(currentMonth.year(), currentMonth.month() + 1)
|
|
|
|
|
|
loadActivityLogs(selectedDate.format('YYYY-MM-DD'))
|
2025-12-20 11:18:59 +00:00
|
|
|
|
}, [])
|
|
|
|
|
|
|
2026-01-13 13:21:47 +00:00
|
|
|
|
// 加载指定月份有活动的日期
|
|
|
|
|
|
const loadActivityDates = async (year, month) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await getDocumentActivityDates(year, month)
|
|
|
|
|
|
if (res.data && res.data.dates) {
|
|
|
|
|
|
setActivityDates(res.data.dates)
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Load activity dates error:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载指定日期的活动日志
|
|
|
|
|
|
const loadActivityLogs = async (date) => {
|
|
|
|
|
|
setLoading(true)
|
2025-12-20 11:18:59 +00:00
|
|
|
|
try {
|
2026-01-13 13:21:47 +00:00
|
|
|
|
const res = await getDocumentActivity(date)
|
|
|
|
|
|
if (res.data && res.data.logs) {
|
|
|
|
|
|
setActivityLogs(res.data.logs)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setActivityLogs([])
|
2025-12-20 11:18:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
2026-01-13 13:21:47 +00:00
|
|
|
|
console.error('Load activity logs error:', error)
|
|
|
|
|
|
Toast.error('加载失败', '获取文档活动记录失败')
|
2025-12-20 11:18:59 +00:00
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-13 13:21:47 +00:00
|
|
|
|
// 日历单元格渲染
|
|
|
|
|
|
const dateCellRender = (value) => {
|
|
|
|
|
|
const dateStr = value.format('YYYY-MM-DD')
|
|
|
|
|
|
const activity = activityDates.find(item => item.date === dateStr)
|
|
|
|
|
|
|
|
|
|
|
|
if (activity && activity.count > 0) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div style={{ textAlign: 'center' }}>
|
|
|
|
|
|
<Badge
|
|
|
|
|
|
count={activity.count}
|
|
|
|
|
|
style={{ backgroundColor: '#1890ff' }}
|
|
|
|
|
|
overflowCount={99}
|
|
|
|
|
|
/>
|
2025-12-20 11:18:59 +00:00
|
|
|
|
</div>
|
2026-01-13 13:21:47 +00:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 日期选择事件
|
|
|
|
|
|
const onSelect = (date) => {
|
|
|
|
|
|
setSelectedDate(date)
|
|
|
|
|
|
loadActivityLogs(date.format('YYYY-MM-DD'))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 月份切换事件
|
|
|
|
|
|
const onPanelChange = (date) => {
|
|
|
|
|
|
setCurrentMonth(date)
|
|
|
|
|
|
loadActivityDates(date.year(), date.month() + 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 点击文档打开
|
|
|
|
|
|
const handleDocumentClick = (log) => {
|
|
|
|
|
|
// 如果文件不存在,提示无法打开
|
|
|
|
|
|
if (!log.file_exists) {
|
|
|
|
|
|
Toast.warning('无法打开', '文件不存在或已被删除/移动/重命名')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有项目ID,提示无法打开
|
|
|
|
|
|
if (!log.project_id) {
|
|
|
|
|
|
Toast.error('无法打开', '找不到对应的项目')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 跳转到文档页面
|
|
|
|
|
|
navigate(`/projects/${log.project_id}/documents?path=${encodeURIComponent(log.file_path)}`)
|
2025-12-20 11:18:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2026-01-13 13:21:47 +00:00
|
|
|
|
<div className="desktop-page">
|
|
|
|
|
|
<h1 className="page-title">个人桌面</h1>
|
|
|
|
|
|
|
|
|
|
|
|
<Row gutter={24}>
|
|
|
|
|
|
{/* 左侧日历 */}
|
|
|
|
|
|
<Col xs={24} lg={12}>
|
|
|
|
|
|
<Card className="calendar-card">
|
|
|
|
|
|
<Calendar
|
|
|
|
|
|
fullscreen={false}
|
|
|
|
|
|
value={selectedDate}
|
|
|
|
|
|
onSelect={onSelect}
|
|
|
|
|
|
onPanelChange={onPanelChange}
|
|
|
|
|
|
cellRender={dateCellRender}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 右侧活动列表 */}
|
|
|
|
|
|
<Col xs={24} lg={12}>
|
|
|
|
|
|
<Card
|
|
|
|
|
|
className="activity-card"
|
|
|
|
|
|
title={
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<FileTextOutlined style={{ marginRight: 8 }} />
|
|
|
|
|
|
{selectedDate.format('YYYY年MM月DD日')} 的文档活动
|
|
|
|
|
|
</div>
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Spin spinning={loading}>
|
|
|
|
|
|
{activityLogs.length > 0 ? (
|
|
|
|
|
|
<List
|
|
|
|
|
|
dataSource={activityLogs}
|
|
|
|
|
|
renderItem={(item) => (
|
|
|
|
|
|
<List.Item
|
|
|
|
|
|
key={item.id}
|
|
|
|
|
|
onClick={() => handleDocumentClick(item)}
|
|
|
|
|
|
className={item.file_exists ? 'activity-item-clickable' : 'activity-item-disabled'}
|
|
|
|
|
|
style={{ cursor: item.file_exists ? 'pointer' : 'default' }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<List.Item.Meta
|
|
|
|
|
|
avatar={
|
|
|
|
|
|
<ClockCircleOutlined
|
|
|
|
|
|
style={{
|
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
|
color: item.file_exists ? '#1890ff' : '#d9d9d9'
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
}
|
|
|
|
|
|
title={
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Text strong style={{ color: item.file_exists ? undefined : '#999' }}>
|
|
|
|
|
|
{item.project_name}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<Text
|
|
|
|
|
|
type="secondary"
|
|
|
|
|
|
style={{ marginLeft: 8, color: item.file_exists ? undefined : '#bbb' }}
|
|
|
|
|
|
>
|
|
|
|
|
|
{item.operation_type}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
{!item.file_exists && (
|
|
|
|
|
|
<Text type="danger" style={{ marginLeft: 8, fontSize: 12 }}>
|
|
|
|
|
|
(已失效)
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
}
|
|
|
|
|
|
description={
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div style={{ marginBottom: 4 }}>
|
|
|
|
|
|
<Text type="secondary">文件:</Text>
|
|
|
|
|
|
<Text code style={{ color: item.file_exists ? undefined : '#999' }}>
|
|
|
|
|
|
{item.file_path}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: 12 }}>
|
|
|
|
|
|
{new Date(item.created_at).toLocaleTimeString('zh-CN')}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</List.Item>
|
|
|
|
|
|
)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<Empty
|
|
|
|
|
|
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
|
|
|
|
description="该日期暂无文档活动记录"
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Spin>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
</div>
|
2025-12-20 11:18:59 +00:00
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default Desktop
|