refactor(Community): 题材模式筛选控件移至搜索框 + 修复展开/折叠
- MainlineTimelineView: 通过 onControlsChange 暴露控件状态 - MainlineTimelineView: 修复 toggleAll 使用错误的 groupId 键 - CompactSearchBox: mainline 模式下渲染概念/排序选择器和操作按钮 - DynamicNewsCard: 管理 mainlineControls 状态并传递给子组件 - EventScrollList: 传递 onMainlineControlsChange 回调 布局变更: 搜索行:[搜索框] | [时间筛选] | [按三级概念▼] [按事件数量▼] [展开] [折叠] [刷新] 统计行:📈 51条主线 · 132个事件 · 15个未归类(小字) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -90,6 +90,14 @@ const DynamicNewsCardComponent = forwardRef(({
|
|||||||
const cardBodyRef = useRef(null);
|
const cardBodyRef = useRef(null);
|
||||||
const mainlineRef = useRef(null); // MainlineTimelineView 的 ref
|
const mainlineRef = useRef(null); // MainlineTimelineView 的 ref
|
||||||
|
|
||||||
|
// 主线模式控件状态(用于传递给 CompactSearchBox)
|
||||||
|
const [mainlineControls, setMainlineControls] = useState(null);
|
||||||
|
|
||||||
|
// 主线模式控件变化回调
|
||||||
|
const handleMainlineControlsChange = useCallback((controls) => {
|
||||||
|
setMainlineControls(controls);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 从 Redux 读取关注状态
|
// 从 Redux 读取关注状态
|
||||||
const eventFollowStatus = useSelector(selectEventFollowStatus);
|
const eventFollowStatus = useSelector(selectEventFollowStatus);
|
||||||
|
|
||||||
@@ -747,6 +755,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
|||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
trackingFunctions={trackingFunctions}
|
trackingFunctions={trackingFunctions}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
|
mainlineControls={mainlineControls}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</VStack>
|
</VStack>
|
||||||
@@ -808,6 +817,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
|||||||
onToggleFollow={handleToggleFollow}
|
onToggleFollow={handleToggleFollow}
|
||||||
onVoteChange={handleVoteChange}
|
onVoteChange={handleVoteChange}
|
||||||
mainlineRef={mainlineRef}
|
mainlineRef={mainlineRef}
|
||||||
|
onMainlineControlsChange={handleMainlineControlsChange}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import VerticalModeLayout from "./layouts/VerticalModeLayout";
|
|||||||
* @param {Function} onToggleFollow - 关注按钮回调
|
* @param {Function} onToggleFollow - 关注按钮回调
|
||||||
* @param {Function} onVoteChange - 投票变化回调 { eventId, voteType: 'bullish' | 'bearish' }
|
* @param {Function} onVoteChange - 投票变化回调 { eventId, voteType: 'bullish' | 'bearish' }
|
||||||
* @param {React.Ref} mainlineRef - MainlineTimelineView 的 ref
|
* @param {React.Ref} mainlineRef - MainlineTimelineView 的 ref
|
||||||
|
* @param {Function} onMainlineControlsChange - 主线模式控件状态变化回调
|
||||||
*/
|
*/
|
||||||
const EventScrollList = React.memo(
|
const EventScrollList = React.memo(
|
||||||
({
|
({
|
||||||
@@ -43,6 +44,7 @@ const EventScrollList = React.memo(
|
|||||||
onToggleFollow,
|
onToggleFollow,
|
||||||
onVoteChange,
|
onVoteChange,
|
||||||
mainlineRef,
|
mainlineRef,
|
||||||
|
onMainlineControlsChange,
|
||||||
}) => {
|
}) => {
|
||||||
const scrollContainerRef = useRef(null);
|
const scrollContainerRef = useRef(null);
|
||||||
|
|
||||||
@@ -82,6 +84,7 @@ const EventScrollList = React.memo(
|
|||||||
eventFollowStatus={eventFollowStatus}
|
eventFollowStatus={eventFollowStatus}
|
||||||
onToggleFollow={onToggleFollow}
|
onToggleFollow={onToggleFollow}
|
||||||
borderColor={borderColor}
|
borderColor={borderColor}
|
||||||
|
onControlsChange={onMainlineControlsChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 纵向分栏模式 */}
|
{/* 纵向分栏模式 */}
|
||||||
|
|||||||
@@ -20,10 +20,8 @@ import {
|
|||||||
Spinner,
|
Spinner,
|
||||||
Center,
|
Center,
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { ChevronDown, ChevronUp, RefreshCw, TrendingUp, Zap } from "lucide-react";
|
import { RefreshCw, TrendingUp, Zap } from "lucide-react";
|
||||||
import { Select } from "antd";
|
|
||||||
import { getApiBase } from "@utils/apiConfig";
|
import { getApiBase } from "@utils/apiConfig";
|
||||||
import "../../SearchFilters/CompactSearchBox.css";
|
import "../../SearchFilters/CompactSearchBox.css";
|
||||||
|
|
||||||
@@ -44,6 +42,7 @@ const MainlineTimelineViewComponent = forwardRef(
|
|||||||
eventFollowStatus = {},
|
eventFollowStatus = {},
|
||||||
onToggleFollow,
|
onToggleFollow,
|
||||||
borderColor,
|
borderColor,
|
||||||
|
onControlsChange, // 控件状态变化回调
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
@@ -112,21 +111,6 @@ const MainlineTimelineViewComponent = forwardRef(
|
|||||||
}
|
}
|
||||||
}, [display, filters.start_date, filters.end_date, filters.recent_days, groupBy]);
|
}, [display, filters.start_date, filters.end_date, filters.recent_days, groupBy]);
|
||||||
|
|
||||||
// 初始加载 & 筛选变化时刷新
|
|
||||||
useEffect(() => {
|
|
||||||
fetchMainlineData();
|
|
||||||
}, [fetchMainlineData]);
|
|
||||||
|
|
||||||
// 暴露方法给父组件
|
|
||||||
useImperativeHandle(
|
|
||||||
ref,
|
|
||||||
() => ({
|
|
||||||
refresh: fetchMainlineData,
|
|
||||||
getScrollPosition: () => null,
|
|
||||||
}),
|
|
||||||
[fetchMainlineData]
|
|
||||||
);
|
|
||||||
|
|
||||||
// 切换分组展开/折叠
|
// 切换分组展开/折叠
|
||||||
const toggleGroup = useCallback((lv2Id) => {
|
const toggleGroup = useCallback((lv2Id) => {
|
||||||
setExpandedGroups((prev) => ({
|
setExpandedGroups((prev) => ({
|
||||||
@@ -141,13 +125,53 @@ const MainlineTimelineViewComponent = forwardRef(
|
|||||||
if (!mainlineData?.mainlines) return;
|
if (!mainlineData?.mainlines) return;
|
||||||
const newState = {};
|
const newState = {};
|
||||||
mainlineData.mainlines.forEach((mainline) => {
|
mainlineData.mainlines.forEach((mainline) => {
|
||||||
newState[mainline.lv2_id] = expand;
|
const groupId = mainline.group_id || mainline.lv2_id || mainline.lv1_id || "ungrouped";
|
||||||
|
newState[groupId] = expand;
|
||||||
});
|
});
|
||||||
setExpandedGroups(newState);
|
setExpandedGroups(newState);
|
||||||
},
|
},
|
||||||
[mainlineData]
|
[mainlineData]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 初始加载 & 筛选变化时刷新
|
||||||
|
useEffect(() => {
|
||||||
|
fetchMainlineData();
|
||||||
|
}, [fetchMainlineData]);
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
refresh: fetchMainlineData,
|
||||||
|
getScrollPosition: () => null,
|
||||||
|
// 暴露控件状态和方法
|
||||||
|
getControls: () => ({
|
||||||
|
groupBy,
|
||||||
|
sortBy,
|
||||||
|
hierarchyOptions,
|
||||||
|
}),
|
||||||
|
setGroupBy,
|
||||||
|
setSortBy,
|
||||||
|
toggleAll,
|
||||||
|
}),
|
||||||
|
[fetchMainlineData, groupBy, sortBy, hierarchyOptions, toggleAll]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 控件状态变化时通知父组件
|
||||||
|
useEffect(() => {
|
||||||
|
if (onControlsChange) {
|
||||||
|
onControlsChange({
|
||||||
|
groupBy,
|
||||||
|
sortBy,
|
||||||
|
hierarchyOptions,
|
||||||
|
setGroupBy,
|
||||||
|
setSortBy,
|
||||||
|
toggleAll,
|
||||||
|
refresh: fetchMainlineData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [groupBy, sortBy, hierarchyOptions, onControlsChange, toggleAll, fetchMainlineData]);
|
||||||
|
|
||||||
// 根据排序方式排序主线列表
|
// 根据排序方式排序主线列表
|
||||||
const sortedMainlines = useMemo(() => {
|
const sortedMainlines = useMemo(() => {
|
||||||
const rawMainlines = mainlineData?.mainlines;
|
const rawMainlines = mainlineData?.mainlines;
|
||||||
@@ -219,132 +243,30 @@ const MainlineTimelineViewComponent = forwardRef(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box display={display} w="100%" bg={COLORS.containerBg}>
|
<Box display={display} w="100%" bg={COLORS.containerBg}>
|
||||||
{/* 顶部统计栏 */}
|
{/* 顶部统计栏 - 筛选控件已移至搜索框 */}
|
||||||
<Flex
|
<Flex
|
||||||
justify="space-between"
|
|
||||||
align="center"
|
align="center"
|
||||||
px={4}
|
px={4}
|
||||||
py={2}
|
py={1.5}
|
||||||
bg={COLORS.statBarBg}
|
bg={COLORS.statBarBg}
|
||||||
borderBottomWidth="1px"
|
borderBottomWidth="1px"
|
||||||
borderBottomColor={COLORS.cardBorderColor}
|
borderBottomColor={COLORS.cardBorderColor}
|
||||||
position="sticky"
|
position="sticky"
|
||||||
left={0}
|
left={0}
|
||||||
>
|
>
|
||||||
<HStack spacing={4}>
|
{/* 统计信息(小字) */}
|
||||||
<HStack spacing={2}>
|
<HStack spacing={3} fontSize="xs" color={COLORS.secondaryTextColor}>
|
||||||
<Icon as={TrendingUp} color="blue.400" />
|
<HStack spacing={1}>
|
||||||
<Text fontWeight="bold" color={COLORS.textColor} fontSize="sm">
|
<Icon as={TrendingUp} color="blue.400" boxSize={3} />
|
||||||
{mainline_count} 条主线
|
<Text fontWeight="medium">{mainline_count} 条主线</Text>
|
||||||
</Text>
|
|
||||||
</HStack>
|
</HStack>
|
||||||
<Text fontSize="sm" color={COLORS.secondaryTextColor}>
|
<Text>· {total_events} 个事件</Text>
|
||||||
共 {total_events} 个事件
|
|
||||||
</Text>
|
|
||||||
{ungrouped_count > 0 && (
|
{ungrouped_count > 0 && (
|
||||||
<Badge colorScheme="orange" fontSize="xs">
|
<Badge colorScheme="orange" fontSize="2xs" px={1}>
|
||||||
{ungrouped_count} 个未归类
|
{ungrouped_count} 个未归类
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
<HStack spacing={2}>
|
|
||||||
{/* 概念级别选择器 */}
|
|
||||||
<Select
|
|
||||||
value={groupBy}
|
|
||||||
onChange={setGroupBy}
|
|
||||||
size="small"
|
|
||||||
style={{ width: 180, backgroundColor: "transparent" }}
|
|
||||||
popupClassName="dark-select-dropdown"
|
|
||||||
dropdownStyle={{ backgroundColor: "#252a34", borderColor: "#3a3f4b", maxHeight: 400 }}
|
|
||||||
showSearch
|
|
||||||
optionFilterProp="label"
|
|
||||||
options={[
|
|
||||||
{
|
|
||||||
label: "分组方式",
|
|
||||||
options: [
|
|
||||||
{ value: "lv1", label: "按一级概念(大类)" },
|
|
||||||
{ value: "lv2", label: "按二级概念(细分)" },
|
|
||||||
{ value: "lv3", label: "按三级概念(更细)" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
...(hierarchyOptions.lv1?.length > 0
|
|
||||||
? [{
|
|
||||||
label: "一级概念(展开)",
|
|
||||||
options: hierarchyOptions.lv1.map((opt) => ({
|
|
||||||
value: opt.id,
|
|
||||||
label: opt.name,
|
|
||||||
})),
|
|
||||||
}]
|
|
||||||
: []),
|
|
||||||
...(hierarchyOptions.lv2?.length > 0
|
|
||||||
? [{
|
|
||||||
label: "二级概念(展开)",
|
|
||||||
options: hierarchyOptions.lv2.map((opt) => ({
|
|
||||||
value: opt.id,
|
|
||||||
label: `${opt.name}`,
|
|
||||||
})),
|
|
||||||
}]
|
|
||||||
: []),
|
|
||||||
...(hierarchyOptions.lv3?.length > 0
|
|
||||||
? [{
|
|
||||||
label: "三级概念(展开)",
|
|
||||||
options: hierarchyOptions.lv3.map((opt) => ({
|
|
||||||
value: opt.id,
|
|
||||||
label: `${opt.name}`,
|
|
||||||
})),
|
|
||||||
}]
|
|
||||||
: []),
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
{/* 排序方式选择器 */}
|
|
||||||
<Select
|
|
||||||
value={sortBy}
|
|
||||||
onChange={setSortBy}
|
|
||||||
size="small"
|
|
||||||
style={{ width: 140, backgroundColor: "transparent" }}
|
|
||||||
popupClassName="dark-select-dropdown"
|
|
||||||
dropdownStyle={{ backgroundColor: "#252a34", borderColor: "#3a3f4b" }}
|
|
||||||
options={[
|
|
||||||
{ value: "event_count", label: "按事件数量" },
|
|
||||||
{ value: "change_desc", label: "按涨幅↓" },
|
|
||||||
{ value: "change_asc", label: "按跌幅↓" },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<Tooltip label="全部展开">
|
|
||||||
<IconButton
|
|
||||||
icon={<ChevronDown size={16} />}
|
|
||||||
size="sm"
|
|
||||||
variant="ghost"
|
|
||||||
color={COLORS.secondaryTextColor}
|
|
||||||
onClick={() => toggleAll(true)}
|
|
||||||
aria-label="全部展开"
|
|
||||||
_hover={{ bg: COLORS.headerHoverBg }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label="全部折叠">
|
|
||||||
<IconButton
|
|
||||||
icon={<ChevronUp size={16} />}
|
|
||||||
size="sm"
|
|
||||||
variant="ghost"
|
|
||||||
color={COLORS.secondaryTextColor}
|
|
||||||
onClick={() => toggleAll(false)}
|
|
||||||
aria-label="全部折叠"
|
|
||||||
_hover={{ bg: COLORS.headerHoverBg }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label="刷新">
|
|
||||||
<IconButton
|
|
||||||
icon={<RefreshCw size={16} />}
|
|
||||||
size="sm"
|
|
||||||
variant="ghost"
|
|
||||||
color={COLORS.secondaryTextColor}
|
|
||||||
onClick={fetchMainlineData}
|
|
||||||
aria-label="刷新"
|
|
||||||
_hover={{ bg: COLORS.headerHoverBg }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</HStack>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* 横向滚动容器 */}
|
{/* 横向滚动容器 */}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import {
|
|||||||
SortAscendingOutlined,
|
SortAscendingOutlined,
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
ThunderboltOutlined,
|
ThunderboltOutlined,
|
||||||
|
DownOutlined,
|
||||||
|
UpOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
@@ -58,6 +60,7 @@ const CompactSearchBox = ({
|
|||||||
pageSize,
|
pageSize,
|
||||||
trackingFunctions = {},
|
trackingFunctions = {},
|
||||||
isMobile = false,
|
isMobile = false,
|
||||||
|
mainlineControls = null, // 主线模式控件状态
|
||||||
}) => {
|
}) => {
|
||||||
// 状态
|
// 状态
|
||||||
const [stockOptions, setStockOptions] = useState([]);
|
const [stockOptions, setStockOptions] = useState([]);
|
||||||
@@ -451,7 +454,7 @@ const CompactSearchBox = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 筛选控件 */}
|
{/* 筛选控件 - 列表模式 */}
|
||||||
{mode !== "mainline" && (
|
{mode !== "mainline" && (
|
||||||
<>
|
<>
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
@@ -545,6 +548,115 @@ const CompactSearchBox = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 筛选控件 - 题材模式 */}
|
||||||
|
{mode === "mainline" && mainlineControls && (
|
||||||
|
<>
|
||||||
|
{!isMobile && (
|
||||||
|
<Divider
|
||||||
|
type="vertical"
|
||||||
|
style={{ height: 24, margin: "0 4px", borderColor: "rgba(255,255,255,0.15)" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 概念级别选择器 */}
|
||||||
|
<AntSelect
|
||||||
|
value={mainlineControls.groupBy}
|
||||||
|
onChange={mainlineControls.setGroupBy}
|
||||||
|
style={{ minWidth: isMobile ? 100 : 160 }}
|
||||||
|
popupClassName="dark-select-dropdown"
|
||||||
|
dropdownStyle={{ backgroundColor: "#252a34", borderColor: "#3a3f4b", maxHeight: 400 }}
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
className="bracket-select"
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
label: "分组方式",
|
||||||
|
options: [
|
||||||
|
{ value: "lv1", label: "按一级概念" },
|
||||||
|
{ value: "lv2", label: "按二级概念" },
|
||||||
|
{ value: "lv3", label: "按三级概念" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...(mainlineControls.hierarchyOptions?.lv1?.length > 0
|
||||||
|
? [{
|
||||||
|
label: "一级概念",
|
||||||
|
options: mainlineControls.hierarchyOptions.lv1.map((opt) => ({
|
||||||
|
value: opt.id,
|
||||||
|
label: opt.name,
|
||||||
|
})),
|
||||||
|
}]
|
||||||
|
: []),
|
||||||
|
...(mainlineControls.hierarchyOptions?.lv2?.length > 0
|
||||||
|
? [{
|
||||||
|
label: "二级概念",
|
||||||
|
options: mainlineControls.hierarchyOptions.lv2.map((opt) => ({
|
||||||
|
value: opt.id,
|
||||||
|
label: opt.name,
|
||||||
|
})),
|
||||||
|
}]
|
||||||
|
: []),
|
||||||
|
...(mainlineControls.hierarchyOptions?.lv3?.length > 0
|
||||||
|
? [{
|
||||||
|
label: "三级概念",
|
||||||
|
options: mainlineControls.hierarchyOptions.lv3.map((opt) => ({
|
||||||
|
value: opt.id,
|
||||||
|
label: opt.name,
|
||||||
|
})),
|
||||||
|
}]
|
||||||
|
: []),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 排序方式选择器 */}
|
||||||
|
<AntSelect
|
||||||
|
value={mainlineControls.sortBy}
|
||||||
|
onChange={mainlineControls.setSortBy}
|
||||||
|
style={{ minWidth: isMobile ? 80 : 120 }}
|
||||||
|
popupClassName="dark-select-dropdown"
|
||||||
|
dropdownStyle={{ backgroundColor: "#252a34", borderColor: "#3a3f4b" }}
|
||||||
|
className="bracket-select"
|
||||||
|
options={[
|
||||||
|
{ value: "event_count", label: "按事件数量" },
|
||||||
|
{ value: "change_desc", label: "按涨幅↓" },
|
||||||
|
{ value: "change_asc", label: "按跌幅↓" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 全部展开 */}
|
||||||
|
<Tooltip title="全部展开">
|
||||||
|
<Button
|
||||||
|
icon={<DownOutlined />}
|
||||||
|
onClick={() => mainlineControls.toggleAll(true)}
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
style={{ color: PROFESSIONAL_COLORS.text.secondary, flexShrink: 0 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
{/* 全部折叠 */}
|
||||||
|
<Tooltip title="全部折叠">
|
||||||
|
<Button
|
||||||
|
icon={<UpOutlined />}
|
||||||
|
onClick={() => mainlineControls.toggleAll(false)}
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
style={{ color: PROFESSIONAL_COLORS.text.secondary, flexShrink: 0 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
{/* 刷新 */}
|
||||||
|
<Tooltip title="刷新">
|
||||||
|
<Button
|
||||||
|
icon={<ReloadOutlined />}
|
||||||
|
onClick={mainlineControls.refresh}
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
style={{ color: PROFESSIONAL_COLORS.text.secondary, flexShrink: 0 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user