nex_docus/frontend/src/pages/Desktop.jsx

212 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { useState, useEffect } from 'react'
import { Card, Calendar, List, Badge, Empty, Typography, Spin } from 'antd'
import { DesktopOutlined, FileTextOutlined, ClockCircleOutlined } from '@ant-design/icons'
import { useNavigate } from 'react-router-dom'
import { getDocumentActivityDates, getDocumentActivity } from '@/api/dashboard'
import PageHeader from '@/components/PageHeader/PageHeader'
import Toast from '@/components/Toast/Toast'
import dayjs from 'dayjs'
import './Desktop.css'
import '@/pages/System/AdminPages.css'
const { Text } = Typography
function Desktop() {
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())
useEffect(() => {
loadActivityDates(currentMonth.year(), currentMonth.month() + 1)
loadActivityLogs(selectedDate.format('YYYY-MM-DD'))
}, [])
// 加载指定月份有活动的日期
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)
try {
const res = await getDocumentActivity(date)
if (res.data && res.data.logs) {
setActivityLogs(res.data.logs)
} else {
setActivityLogs([])
}
} catch (error) {
console.error('Load activity logs error:', error)
Toast.error('加载失败', '获取文档活动记录失败')
} finally {
setLoading(false)
}
}
// 日历单元格渲染
const dateCellRender = (value, info) => {
if (info.type !== 'date') return info.originNode
const dateStr = value.format('YYYY-MM-DD')
const activity = activityDates.find(item => item.date === dateStr)
if (activity && activity.count > 0) {
return (
<Badge
count={activity.count}
style={{
backgroundColor: '#ff4d4f',
fontSize: '10px',
height: '16px',
lineHeight: '16px',
minWidth: '16px',
padding: '0 4px'
}}
>
{info.originNode}
</Badge>
)
}
return info.originNode
}
// 日期选择事件
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}/docs?file=${encodeURIComponent(log.file_path)}`)
}
return (
<div className="admin-page">
<PageHeader
title="个人桌面"
description="查看文档活动日历与每天的编辑轨迹,快速回到最近处理过的内容。"
icon={<DesktopOutlined />}
/>
<div className="admin-grid-2 desktop-grid">
<Card className="admin-card calendar-card">
<Calendar
fullscreen={false}
value={selectedDate}
onSelect={onSelect}
onPanelChange={onPanelChange}
fullCellRender={dateCellRender}
/>
</Card>
<Card
className="admin-card 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 ? 'var(--link-color)' : 'var(--border-color-strong)'
}}
/>
)}
title={(
<div>
<Text strong style={{ color: item.file_exists ? undefined : 'var(--text-color-secondary)' }}>
{item.project_name}
</Text>
<Text
type="secondary"
style={{ marginLeft: 8, color: item.file_exists ? undefined : 'var(--text-color-secondary)' }}
>
{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 : 'var(--text-color-secondary)' }}>
{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>
</div>
</div>
)
}
export default Desktop