修改故事列表
This commit is contained in:
parent
182a58d0db
commit
efd3f4a82c
|
@ -59,7 +59,9 @@
|
|||
"rc-util": "^5.38.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-fittext": "^1.0.0"
|
||||
"react-fittext": "^1.0.0",
|
||||
"react-virtualized-auto-sizer": "^1.0.26",
|
||||
"react-window": "^1.8.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ant-design/pro-cli": "^2.1.5",
|
||||
|
|
|
@ -11,7 +11,6 @@ const useFetchImageUrl = (imageInstanceId: string) => {
|
|||
{
|
||||
manual: true,
|
||||
onSuccess: (data) => {
|
||||
console.log(data);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
|
@ -35,6 +35,9 @@ export interface StoryType {
|
|||
status?: string;
|
||||
createTime?: string;
|
||||
ownerId?: string;
|
||||
ownerName?: string;
|
||||
updateName?: string;
|
||||
itemCount: number,
|
||||
updatedId?: string;
|
||||
updateTime?: string;
|
||||
logo?: string;
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
// src/pages/story/detail.tsx
|
||||
import AddTimeLineItemModal from '@/pages/story/components/AddTimeLineItemModal';
|
||||
import TimelineItem from '@/pages/story/components/TimelineItem/TimelineItem';
|
||||
import { StoryItem } from '@/pages/story/data';
|
||||
import { countStoryItem, queryStoryDetail, queryStoryItem } from '@/pages/story/service';
|
||||
import { queryStoryDetail, queryStoryItem } from '@/pages/story/service';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { history, useIntl, useRequest } from '@umijs/max';
|
||||
import { FloatButton, Spin, Timeline } from 'antd';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { FloatButton, Spin } from 'antd';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import './index.css';
|
||||
import useStyles from './style.style';
|
||||
|
||||
interface TimelineItemProps {
|
||||
children: React.ReactNode; // 修正:使用 ReactNode 更通用
|
||||
// label: string
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const Index = () => {
|
||||
|
@ -20,23 +22,25 @@ const Index = () => {
|
|||
const { styles } = useStyles();
|
||||
const intl = useIntl();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [items, setItems] = useState<TimelineItemProps[]>([]);
|
||||
|
||||
const [items, setItems] = useState<StoryItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [hasMoreNew, setHasMoreNew] = useState(true);
|
||||
const [hasMoreOld, setHasMoreOld] = useState(true);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [openAddItemModal, setOpenAddItemModal] = useState(false);
|
||||
const [currentItem, setCurrentItem] = useState<StoryItem>();
|
||||
const [currentOption, setCurrentOption] = useState<'add' | 'edit' | 'addSubItem' | 'editSubItem'>();
|
||||
const [currentOption, setCurrentOption] = useState<
|
||||
'add' | 'edit' | 'addSubItem' | 'editSubItem'
|
||||
>();
|
||||
const [pagination, setPagination] = useState({ current: 1, pageSize: 10 });
|
||||
|
||||
const { data: storyItemList, run } = useRequest(
|
||||
const { data: response, run } = useRequest(
|
||||
() => {
|
||||
return queryStoryItem(lineId ?? '');
|
||||
return queryStoryItem({ storyInstanceId: lineId, ...pagination });
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
},
|
||||
);
|
||||
|
||||
const {
|
||||
data: detail,
|
||||
run: queryDetail,
|
||||
|
@ -44,56 +48,153 @@ const Index = () => {
|
|||
} = useRequest(() => {
|
||||
return queryStoryDetail(lineId ?? '');
|
||||
});
|
||||
const { data: count, run: queryCount } = useRequest(() => {
|
||||
return countStoryItem(lineId ?? '');
|
||||
});
|
||||
|
||||
// 初始化加载数据
|
||||
useEffect(() => {
|
||||
setItems([]);
|
||||
setPagination({ current: 1, pageSize: 10 });
|
||||
setHasMore(true);
|
||||
run();
|
||||
}, [lineId]);
|
||||
useEffect(() => {
|
||||
if (!storyItemList?.length) return;
|
||||
console.log(storyItemList);
|
||||
let timelineItems = storyItemList; //handleStoryItemList(storyItemList);
|
||||
// 转换为 Timeline 组件需要的格式
|
||||
const formattedItems = timelineItems.map((item: StoryItem) => ({
|
||||
children: (
|
||||
<TimelineItem
|
||||
item={item}
|
||||
handleOption={(item: StoryItem, option: 'add' | 'edit' | 'addSubItem' | 'editSubItem') => {
|
||||
setCurrentItem(item);
|
||||
setCurrentOption(option)
|
||||
setOpenAddItemModal(true);
|
||||
}}
|
||||
refresh={() => {
|
||||
run();
|
||||
queryCount();
|
||||
queryDetail();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
setItems(formattedItems);
|
||||
}, [storyItemList]);
|
||||
// 处理响应数据
|
||||
useEffect(() => {
|
||||
if (!response) return;
|
||||
|
||||
if (pagination.current === 1) {
|
||||
// 首页数据
|
||||
setItems(response.list || []);
|
||||
} else {
|
||||
// 追加数据
|
||||
setItems(prev => [...prev, ...(response.list || [])]);
|
||||
}
|
||||
|
||||
// 检查是否还有更多数据
|
||||
setHasMore(response.list && response.list.length === pagination.pageSize);
|
||||
setLoading(false);
|
||||
}, [response, pagination]);
|
||||
|
||||
// 滚动到底部加载更多
|
||||
const loadMore = useCallback(() => {
|
||||
if (loading || !hasMore) return;
|
||||
|
||||
setLoading(true);
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
current: prev.current + 1
|
||||
}));
|
||||
}, [loading, hasMore]);
|
||||
|
||||
// 当分页变化时重新请求数据
|
||||
useEffect(() => {
|
||||
if (pagination.current > 1) {
|
||||
console.log('分页变化')
|
||||
run();
|
||||
}
|
||||
}, [pagination, run]);
|
||||
|
||||
// 渲染单个时间线项的函数
|
||||
const renderTimelineItem = useCallback(
|
||||
({ index, style }: { index: number; style: React.CSSProperties }) => {
|
||||
const item = items[index];
|
||||
if (!item) return null;
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<TimelineItem
|
||||
item={item}
|
||||
handleOption={(
|
||||
item: StoryItem,
|
||||
option: 'add' | 'edit' | 'addSubItem' | 'editSubItem',
|
||||
) => {
|
||||
setCurrentItem(item);
|
||||
setCurrentOption(option);
|
||||
setOpenAddItemModal(true);
|
||||
}}
|
||||
refresh={() => {
|
||||
// 刷新当前页数据
|
||||
setPagination(prev => ({ ...prev, current: 1 }));
|
||||
run();
|
||||
queryDetail();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[items, run, queryDetail],
|
||||
);
|
||||
|
||||
// 处理滚动事件
|
||||
const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
||||
// 当滚动到底部时加载更多
|
||||
if (scrollTop + clientHeight >= scrollHeight - 10) {
|
||||
loadMore();
|
||||
}
|
||||
}, [loadMore]);
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
onBack={() => history.push('/story')}
|
||||
title={queryDetailLoading ? '加载中' : `${detail?.title} ${count ? `共${count}个时刻` : ``}`}
|
||||
title={
|
||||
queryDetailLoading ? '加载中' : `${detail?.title} ${`共${detail?.itemCount ?? 0}个时刻`}`
|
||||
}
|
||||
>
|
||||
<div className="timeline" ref={containerRef}>
|
||||
{!hasMoreOld && <div style={{ textAlign: 'center', color: '#999' }}>没有更老的内容</div>}
|
||||
{loading && <Spin style={{ display: 'block', margin: '20px auto' }} />}
|
||||
<Timeline items={items} mode={'left'} />
|
||||
{loading && <Spin style={{ display: 'block', margin: '20px auto' }} />}
|
||||
{!hasMoreNew && <div style={{ textAlign: 'center', color: '#999' }}>没有更新的内容</div>}
|
||||
<div
|
||||
className="timeline"
|
||||
ref={containerRef}
|
||||
style={{
|
||||
height: 'calc(100vh - 200px)',
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
>
|
||||
{items.length > 0 ? (
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<div
|
||||
style={{ height, width, position: 'relative' }}
|
||||
onScroll={handleScroll}
|
||||
className="timeline-hide-scrollbar"
|
||||
>
|
||||
<List
|
||||
height={height}
|
||||
itemCount={items.length}
|
||||
itemSize={300} // 根据实际项高度调整
|
||||
width={width}
|
||||
>
|
||||
{renderTimelineItem}
|
||||
</List>
|
||||
|
||||
{/* 加载更多指示器 */}
|
||||
{loading && (
|
||||
<div style={{ textAlign: 'center', padding: '20px' }}>
|
||||
<Spin />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!hasMore && items.length > 0 && (
|
||||
<div style={{ textAlign: 'center', color: '#999', padding: '20px' }}>
|
||||
没有更多内容了
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
) : (
|
||||
<div style={{ textAlign: 'center', padding: '50px 0' }}>
|
||||
{loading ? <Spin /> : '暂无时间线数据'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<FloatButton onClick={() => {
|
||||
setCurrentOption('add');
|
||||
setCurrentItem();
|
||||
setOpenAddItemModal(true);
|
||||
}} />
|
||||
|
||||
<FloatButton
|
||||
onClick={() => {
|
||||
setCurrentOption('add');
|
||||
setCurrentItem();
|
||||
setOpenAddItemModal(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
<AddTimeLineItemModal
|
||||
visible={openAddItemModal}
|
||||
initialValues={currentItem}
|
||||
|
@ -103,7 +204,10 @@ const Index = () => {
|
|||
}}
|
||||
onOk={() => {
|
||||
setOpenAddItemModal(false);
|
||||
// 添加新项后刷新数据
|
||||
setPagination(prev => ({ ...prev, current: 1 }));
|
||||
run();
|
||||
queryDetail()
|
||||
}}
|
||||
storyId={lineId}
|
||||
/>
|
||||
|
|
|
@ -138,3 +138,11 @@
|
|||
color: #888;
|
||||
font-style: italic;
|
||||
}
|
||||
.timeline-hide-scrollbar::-webkit-scrollbar {
|
||||
display: none; /* Chrome Safari */
|
||||
}
|
||||
|
||||
.timeline-hide-scrollbar {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE 10+ */
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { DownOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { history, useRequest } from '@umijs/max';
|
||||
import { Avatar, Button, Card, Dropdown, Input, List, Modal, Radio } from 'antd';
|
||||
import { Avatar, Button, Card, Dropdown, Input, List, Modal } from 'antd';
|
||||
import type { FC } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import OperationModal from './components/OperationModal';
|
||||
|
@ -9,12 +9,11 @@ import type { StoryType } from './data.d';
|
|||
import { addStory, deleteStory, queryTimelineList, updateStory } from './service';
|
||||
import useStyles from './style.style';
|
||||
|
||||
/*const RadioButton = Radio.Button;
|
||||
const RadioGroup = Radio.Group;*/
|
||||
|
||||
const { Search } = Input;
|
||||
|
||||
const ListContent = ({
|
||||
data: { ownerId, updatedId, createTime, updateTime, status },
|
||||
data: { createTime, updateTime, updateName, ownerName, itemCount },
|
||||
}: {
|
||||
data: StoryType;
|
||||
}) => {
|
||||
|
@ -23,15 +22,15 @@ const ListContent = ({
|
|||
<div>
|
||||
<div className={styles.listContentItem}>
|
||||
<span>创建人</span>
|
||||
<p>{ownerId}</p>
|
||||
<p>{ownerName}</p>
|
||||
</div>
|
||||
<div className={styles.listContentItem}>
|
||||
<span>最近更新人</span>
|
||||
<p>{updatedId}</p>
|
||||
<p>{updateName}</p>
|
||||
</div>
|
||||
<div className={styles.listContentItem}>
|
||||
<span>节点数</span>
|
||||
<p>{111}</p>
|
||||
<p>{itemCount}</p>
|
||||
</div>
|
||||
<div className={styles.listContentItem}>
|
||||
<span>开始时间</span>
|
||||
|
@ -107,11 +106,19 @@ export const BasicList: FC = () => {
|
|||
};
|
||||
const extraContent = (
|
||||
<div>
|
||||
{/*<RadioGroup defaultValue="all">
|
||||
<RadioButton value="all">全部</RadioButton>
|
||||
<RadioButton value="progress">进行中</RadioButton>
|
||||
<RadioButton value="waiting">等待中</RadioButton>
|
||||
</RadioGroup>*/}
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
style={{
|
||||
marginBottom: 8,
|
||||
float: 'left'
|
||||
}}
|
||||
>
|
||||
<PlusOutlined />
|
||||
新建故事
|
||||
</Button>
|
||||
<Search
|
||||
className={styles.extraContentSearch}
|
||||
placeholder="请输入"
|
||||
|
@ -211,19 +218,6 @@ export const BasicList: FC = () => {
|
|||
</Card>
|
||||
</div>
|
||||
</PageContainer>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
style={{
|
||||
width: '100%',
|
||||
marginBottom: 8,
|
||||
}}
|
||||
>
|
||||
<PlusOutlined />
|
||||
添加
|
||||
</Button>
|
||||
<OperationModal
|
||||
done={done}
|
||||
open={open}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { request } from '@umijs/max';
|
||||
import { StoryItem, StoryType } from './data.d';
|
||||
import {CommonResponse} from "@/types/common";
|
||||
import {CommonListResponse, CommonResponse} from "@/types/common";
|
||||
|
||||
type ParamsType = {
|
||||
count?: number;
|
||||
instanceId?: string;
|
||||
storyName?: string;
|
||||
} & Partial<StoryType>;
|
||||
pageSize?: number;
|
||||
current?: number;
|
||||
} & Partial<StoryType> & Partial<StoryItem>;
|
||||
|
||||
export async function queryTimelineList(
|
||||
params: ParamsType,
|
||||
|
@ -55,12 +57,10 @@ export async function addStoryItem(params: FormData): Promise<any> {
|
|||
});
|
||||
}
|
||||
|
||||
export async function queryStoryItem(storyInstanceId: string): Promise<{ data: StoryItem[] }> {
|
||||
export async function queryStoryItem(params: ParamsType): Promise<{ data: CommonListResponse<StoryItem> }> {
|
||||
return request(`/story/item/list`, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
storyInstanceId,
|
||||
},
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,16 @@ export interface CommonResponse<T> {
|
|||
data?: T;
|
||||
}
|
||||
export type CommonListResponse<T> = {
|
||||
list?: T[];
|
||||
list: T[];
|
||||
total?: number;
|
||||
pageSize?: number;
|
||||
pageNumber?: number;
|
||||
pages?: number;
|
||||
}
|
||||
export interface PageParams {
|
||||
// query
|
||||
/** 当前的页码 */
|
||||
current?: number;
|
||||
/** 页面的容量 */
|
||||
pageSize?: number;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue