feat: 添加评论、反应、离线编辑及主题定制功能
All checks were successful
test/timeline-frontend/pipeline/head This commit looks good
All checks were successful
test/timeline-frontend/pipeline/head This commit looks good
- 实现评论系统,包括评论输入、列表展示和集成指南 - 添加反应功能组件(ReactionBar、ReactionButton、ReactionPicker) - 实现离线编辑支持,包括同步状态管理和冲突解决 - 添加主题定制功能,支持多种配色方案和主题预览 - 新增多视图布局选项(时间线、分组、砌体视图) - 实现个人资料编辑器,支持头像、简介和自定义字段编辑 - 添加统计页面,展示存储使用情况和上传趋势 - 新增相册管理功能,支持相册创建、编辑和照片管理 - 实现响应式设计和加载骨架屏组件 - 扩展国际化支持,新增孟加拉语、波斯语、印尼语、日语、葡萄牙语等语言 - 添加错误边界组件和离线指示器 - 更新配置文件、路由和依赖项 - 新增完整的文档、测试用例和集成指南
This commit is contained in:
@@ -1,17 +1,9 @@
|
||||
// src/pages/gallery\components\GridView.tsx
|
||||
import { ImageItem } from '@/pages/gallery/typings';
|
||||
import { formatDuration } from '@/utils/timelineUtils';
|
||||
import { getAuthization } from '@/utils/userUtils';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownloadOutlined,
|
||||
EyeOutlined,
|
||||
MoreOutlined,
|
||||
PlayCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Checkbox, Dropdown, Menu, Spin } from 'antd';
|
||||
import { Spin } from 'antd';
|
||||
import React, { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { useIsMobile } from '@/hooks/useIsMobile';
|
||||
import PhotoCard from './PhotoCard';
|
||||
import '../index.css';
|
||||
|
||||
interface GridViewProps {
|
||||
@@ -74,54 +66,6 @@ const GridView: FC<GridViewProps> = ({
|
||||
|
||||
const imageSize = getImageSize();
|
||||
|
||||
const getImageMenu = useCallback(
|
||||
(item: ImageItem) => (
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
key="preview"
|
||||
icon={<EyeOutlined />}
|
||||
onClick={() => {
|
||||
const index = imageList.findIndex((img) => img.instanceId === item.instanceId);
|
||||
onPreview(index);
|
||||
}}
|
||||
>
|
||||
预览
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key="download"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => onDownload(item.instanceId, item.imageName)}
|
||||
>
|
||||
下载
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
key="delete"
|
||||
icon={<DeleteOutlined />}
|
||||
danger
|
||||
onClick={() => onDelete(item.instanceId, item.imageName)}
|
||||
>
|
||||
删除
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
),
|
||||
[imageList, onPreview, onDownload, onDelete],
|
||||
);
|
||||
|
||||
// 根据视图模式确定图像 URL
|
||||
const getImageUrl = (item: ImageItem, isHighRes?: boolean) => {
|
||||
// 如果是视频,使用封面图
|
||||
if (item.thumbnailInstanceId) {
|
||||
return `/file/image-low-res/${item.thumbnailInstanceId}?Authorization=${getAuthization()}`;
|
||||
}
|
||||
// 小图模式使用低分辨率图像,除非明确要求高清
|
||||
if (viewMode === 'small' && !isHighRes) {
|
||||
return `/file/image-low-res/${item.instanceId}?Authorization=${getAuthization()}`;
|
||||
}
|
||||
// 其他模式使用原图
|
||||
return `/file/image/${item.instanceId}?Authorization=${getAuthization()}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={viewMode === 'small' ? 'small-grid-view' : 'large-grid-view'}
|
||||
@@ -134,63 +78,20 @@ const GridView: FC<GridViewProps> = ({
|
||||
} : {}}
|
||||
>
|
||||
{imageList.map((item: ImageItem, index: number) => (
|
||||
<div
|
||||
key={item.instanceId}
|
||||
className="image-card"
|
||||
style={isMobile ? { width: '100%', margin: 0 } : {}}
|
||||
>
|
||||
{batchMode && (
|
||||
<Checkbox
|
||||
className="image-checkbox"
|
||||
checked={selectedRowKeys.includes(item.instanceId)}
|
||||
onChange={(e) => onSelect(item.instanceId, e.target.checked)}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className="image-wrapper"
|
||||
style={{
|
||||
width: isMobile ? '100%' : imageSize.width,
|
||||
height: imageSize.height,
|
||||
backgroundImage: `url(${getImageUrl(item, false)})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
}}
|
||||
onClick={() => !batchMode && onPreview(index)}
|
||||
>
|
||||
{(item.duration || item.thumbnailInstanceId) && (
|
||||
<PlayCircleOutlined style={{ fontSize: isMobile ? '24px' : '32px', color: 'rgba(255,255,255,0.8)' }} />
|
||||
)}
|
||||
{item.duration && (
|
||||
<span
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 4,
|
||||
right: 4,
|
||||
background: 'rgba(0,0,0,0.5)',
|
||||
color: '#fff',
|
||||
padding: '2px 4px',
|
||||
borderRadius: 2,
|
||||
fontSize: 10,
|
||||
}}
|
||||
>
|
||||
{formatDuration(item.duration)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="image-info">
|
||||
<div className="image-title" title={item.imageName}>
|
||||
{item.imageName}
|
||||
</div>
|
||||
<Dropdown overlay={getImageMenu(item)} trigger={['click']}>
|
||||
<Button type="text" icon={<MoreOutlined />} size={isMobile ? "small" : "middle"} />
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<PhotoCard
|
||||
key={item.instanceId}
|
||||
item={item}
|
||||
index={index}
|
||||
viewMode={viewMode}
|
||||
batchMode={batchMode}
|
||||
isSelected={selectedRowKeys.includes(item.instanceId)}
|
||||
imageSize={imageSize}
|
||||
isMobile={isMobile}
|
||||
onPreview={onPreview}
|
||||
onSelect={onSelect}
|
||||
onDownload={onDownload}
|
||||
onDelete={onDelete}
|
||||
/>
|
||||
))}
|
||||
{loadingMore && (
|
||||
<div style={{ gridColumn: '1 / -1', textAlign: 'center', padding: '20px' }}>
|
||||
|
||||
Reference in New Issue
Block a user