Compare commits
5 Commits
a569a63a85
...
b1d5b217d3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1d5b217d3 | ||
|
|
5f6b933172 | ||
|
|
0c291de182 | ||
|
|
61ed1510c2 | ||
|
|
0edc6a5e00 |
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
||||
|
||||
@@ -75,9 +75,11 @@ const BytedeskWidget = ({
|
||||
const rightVal = parseInt(style.right);
|
||||
const bottomVal = parseInt(style.bottom);
|
||||
if (rightVal >= 0 && rightVal < 100 && bottomVal >= 0 && bottomVal < 100) {
|
||||
// H5 端设置按钮尺寸为 48x48(只执行一次)
|
||||
// H5 端设置按钮尺寸为 48x48 并降低 z-index(只执行一次)
|
||||
if (isMobile && !el.dataset.bytedeskStyled) {
|
||||
el.dataset.bytedeskStyled = 'true';
|
||||
// 降低 z-index,避免遮挡页面内的发布按钮等交互元素
|
||||
el.style.zIndex = 10;
|
||||
const button = el.querySelector('button');
|
||||
if (button) {
|
||||
button.style.width = '48px';
|
||||
|
||||
@@ -161,7 +161,7 @@ export default function HomeNavbar() {
|
||||
borderColor={navbarBorder}
|
||||
py={{ base: 2, md: 3 }}
|
||||
>
|
||||
<Container maxW="container.xl" px={{ base: 3, md: 4 }}>
|
||||
<Container maxW="container.xl" px={{ base: 3, md: 4 }} style={{ paddingRight: 'max(16px, env(safe-area-inset-right))' }}>
|
||||
<Flex justify="space-between" align="center">
|
||||
{/* Logo - 价小前投研 */}
|
||||
<BrandLogo />
|
||||
|
||||
@@ -64,7 +64,7 @@ export const useWatchlist = () => {
|
||||
const handleAddToWatchlist = useCallback(async (stockCode, stockName) => {
|
||||
try {
|
||||
const base = getApiBase();
|
||||
const resp = await fetch(base + '/api/account/watchlist/add', {
|
||||
const resp = await fetch(base + '/api/account/watchlist', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
||||
@@ -159,7 +159,7 @@ export const accountHandlers = [
|
||||
}),
|
||||
|
||||
// 6. 添加自选股
|
||||
http.post('/api/account/watchlist/add', async ({ request }) => {
|
||||
http.post('/api/account/watchlist', async ({ request }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const currentUser = getCurrentUser();
|
||||
|
||||
@@ -1,36 +1,8 @@
|
||||
.event-detail-modal {
|
||||
top: 20% !important;
|
||||
margin: 0 auto !important;
|
||||
padding-bottom: 0 !important;
|
||||
|
||||
.ant-modal-content {
|
||||
border-radius: 24px !important;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
// 标题样式 - 深色文字(白色背景)
|
||||
.ant-modal-title {
|
||||
// 事件详情抽屉样式(从底部弹出)
|
||||
// 注意:大部分样式已在 TSX 的 styles 属性中配置,这里只保留必要的覆盖
|
||||
.event-detail-drawer {
|
||||
// 标题样式
|
||||
.ant-drawer-title {
|
||||
color: #1A202C;
|
||||
}
|
||||
|
||||
// 关闭按钮样式 - 深色(白色背景)
|
||||
.ant-modal-close {
|
||||
color: #4A5568;
|
||||
|
||||
&:hover {
|
||||
color: #1A202C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 自底向上滑入动画
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Modal } from 'antd';
|
||||
import { Drawer } from 'antd';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { selectIsMobile } from '@store/slices/deviceSlice';
|
||||
import DynamicNewsDetailPanel from './DynamicNewsDetail/DynamicNewsDetailPanel';
|
||||
import './EventDetailModal.less';
|
||||
@@ -15,7 +16,7 @@ interface EventDetailModalProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件详情弹窗组件
|
||||
* 事件详情抽屉组件(从底部弹出)
|
||||
*/
|
||||
const EventDetailModal: React.FC<EventDetailModalProps> = ({
|
||||
open,
|
||||
@@ -25,23 +26,35 @@ const EventDetailModal: React.FC<EventDetailModalProps> = ({
|
||||
const isMobile = useSelector(selectIsMobile);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
<Drawer
|
||||
open={open}
|
||||
onCancel={onClose}
|
||||
footer={null}
|
||||
onClose={onClose}
|
||||
placement="bottom"
|
||||
height={isMobile ? 'calc(100vh - 60px)' : 'calc(100vh - 100px)'}
|
||||
width={isMobile ? '100%' : '70vw'}
|
||||
title={event?.title || '事件详情'}
|
||||
width='100vw'
|
||||
destroyOnClose
|
||||
className="event-detail-modal"
|
||||
destroyOnHidden
|
||||
rootClassName="event-detail-drawer"
|
||||
closeIcon={null}
|
||||
extra={
|
||||
<CloseOutlined
|
||||
onClick={onClose}
|
||||
style={{ cursor: 'pointer', fontSize: 16, color: '#4A5568' }}
|
||||
/>
|
||||
}
|
||||
styles={{
|
||||
mask: { background: 'transparent' },
|
||||
content: { borderRadius: 24, padding: 0, maxWidth: 1400, background: 'transparent', margin: '0 auto', maxHeight: '80vh', display: 'flex', flexDirection: 'column' },
|
||||
header: { background: '#FFFFFF', borderBottom: '1px solid #E2E8F0', padding: '16px 24px', borderRadius: '24px 24px 0 0', margin: 0, flexShrink: 0 },
|
||||
body: { padding: 0, overflowY: 'auto', flex: 1 },
|
||||
wrapper: isMobile ? {} : {
|
||||
maxWidth: 1400,
|
||||
margin: '0 auto',
|
||||
borderRadius: '16px 16px 0 0',
|
||||
},
|
||||
content: { borderRadius: '16px 16px 0 0' },
|
||||
header: { background: '#FFFFFF', borderBottom: '1px solid #E2E8F0', padding: '16px 24px' },
|
||||
body: { padding: 0, background: '#FFFFFF' },
|
||||
}}
|
||||
>
|
||||
{event && <DynamicNewsDetailPanel event={event} showHeader={false} />}
|
||||
</Modal>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card, Badge, Tag, Empty, Carousel, Tooltip } from 'antd';
|
||||
import { ArrowUpOutlined, ArrowDownOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||
import { useDisclosure } from '@chakra-ui/react';
|
||||
import { useDisclosure, useBreakpointValue } from '@chakra-ui/react';
|
||||
import EventDetailModal from './EventDetailModal';
|
||||
import dayjs from 'dayjs';
|
||||
import './HotEvents.css';
|
||||
@@ -31,6 +31,8 @@ const HotEvents = ({ events, onPageChange, onEventClick }) => {
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
const { isOpen: isModalOpen, onOpen: onModalOpen, onClose: onModalClose } = useDisclosure();
|
||||
const [modalEvent, setModalEvent] = useState(null);
|
||||
// H5 端不显示 Tooltip(避免触摸触发后无法消除的黑色悬浮框)
|
||||
const isMobile = useBreakpointValue({ base: true, md: false });
|
||||
|
||||
const renderPriceChange = (value) => {
|
||||
if (value === null || value === undefined) {
|
||||
@@ -154,21 +156,33 @@ const HotEvents = ({ events, onPageChange, onEventClick }) => {
|
||||
>
|
||||
{/* Custom layout without Card.Meta */}
|
||||
<div className="event-header">
|
||||
<Tooltip title={event.title}>
|
||||
{isMobile ? (
|
||||
<span className="event-title">
|
||||
{event.title}
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title={event.title}>
|
||||
<span className="event-title">
|
||||
{event.title}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
<span className="event-tag">
|
||||
{renderPriceChange(event.related_avg_chg)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Tooltip title={event.description}>
|
||||
{isMobile ? (
|
||||
<div className="event-description">
|
||||
{event.description}
|
||||
</div>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title={event.description}>
|
||||
<div className="event-description">
|
||||
{event.description}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<div className="event-footer">
|
||||
<span className="creator">{event.creator?.username || 'Anonymous'}</span>
|
||||
|
||||
@@ -78,6 +78,7 @@ import {
|
||||
MenuList,
|
||||
MenuItem,
|
||||
Collapse,
|
||||
useBreakpointValue,
|
||||
} from '@chakra-ui/react';
|
||||
import { SearchIcon, ViewIcon, CalendarIcon, ExternalLinkIcon, StarIcon, ChevronDownIcon, InfoIcon, CloseIcon, ChevronRightIcon } from '@chakra-ui/icons';
|
||||
import { FaThLarge, FaList, FaTags, FaChartLine, FaRobot, FaTable, FaHistory, FaBrain, FaLightbulb, FaRocket, FaShieldAlt, FaCalendarAlt, FaArrowUp, FaArrowDown, FaNewspaper, FaFileAlt, FaExpand, FaCompress, FaClock, FaLock } from 'react-icons/fa';
|
||||
@@ -672,6 +673,10 @@ const ConceptCenter = () => {
|
||||
const changePercent = concept.price_info?.avg_change_pct;
|
||||
const changeColor = getChangeColor(changePercent);
|
||||
const hasChange = changePercent !== null && changePercent !== undefined;
|
||||
// H5 端使用更紧凑的尺寸
|
||||
const isMobile = useBreakpointValue({ base: true, md: false });
|
||||
const coverHeight = useBreakpointValue({ base: '100px', md: '180px' });
|
||||
const logoSize = useBreakpointValue({ base: '60px', md: '120px' });
|
||||
|
||||
// 生成随机涨幅数字背景
|
||||
const generateNumbersBackground = () => {
|
||||
@@ -705,7 +710,7 @@ const ConceptCenter = () => {
|
||||
boxShadow="0 4px 12px rgba(0, 0, 0, 0.1)"
|
||||
>
|
||||
{/* 毛玻璃涨幅数字背景 */}
|
||||
<Box position="relative" height="180px" overflow="hidden">
|
||||
<Box position="relative" height={coverHeight} overflow="hidden">
|
||||
{/* 渐变背景层 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
@@ -757,8 +762,8 @@ const ConceptCenter = () => {
|
||||
top="50%"
|
||||
left="50%"
|
||||
transform="translate(-50%, -50%)"
|
||||
width="120px"
|
||||
height="120px"
|
||||
width={logoSize}
|
||||
height={logoSize}
|
||||
opacity={0.15}
|
||||
>
|
||||
<Image
|
||||
@@ -849,11 +854,11 @@ const ConceptCenter = () => {
|
||||
</Badge>
|
||||
</Box>
|
||||
|
||||
<CardBody p={4}>
|
||||
<VStack align="start" spacing={2}>
|
||||
<CardBody p={{ base: 3, md: 4 }}>
|
||||
<VStack align="start" spacing={{ base: 1, md: 2 }}>
|
||||
{/* 概念名称 */}
|
||||
<Heading
|
||||
size="sm"
|
||||
size={{ base: 'xs', md: 'sm' }}
|
||||
color="gray.800"
|
||||
noOfLines={1}
|
||||
bgGradient="linear(to-r, purple.600, pink.600)"
|
||||
@@ -863,15 +868,15 @@ const ConceptCenter = () => {
|
||||
{concept.concept}
|
||||
</Heading>
|
||||
|
||||
{/* 描述信息 */}
|
||||
<Text color="gray.600" fontSize="xs" noOfLines={2} minH="32px">
|
||||
{/* 描述信息 - H5端显示1行 */}
|
||||
<Text color="gray.600" fontSize="xs" noOfLines={isMobile ? 1 : 2} minH={{ base: '16px', md: '32px' }}>
|
||||
{concept.description || '暂无描述信息'}
|
||||
</Text>
|
||||
|
||||
{concept.stocks && concept.stocks.length > 0 && (
|
||||
<Box
|
||||
width="100%"
|
||||
p={3}
|
||||
p={{ base: 2, md: 3 }}
|
||||
bg="linear-gradient(135deg, rgba(99, 102, 241, 0.05) 0%, rgba(168, 85, 247, 0.05) 100%)"
|
||||
borderRadius="lg"
|
||||
cursor="pointer"
|
||||
@@ -886,7 +891,7 @@ const ConceptCenter = () => {
|
||||
>
|
||||
<Flex align="center" justify="space-between">
|
||||
<Box flex={1}>
|
||||
<HStack spacing={2} mb={2}>
|
||||
<HStack spacing={2} mb={{ base: 1, md: 2 }}>
|
||||
<Icon as={FaChartLine} boxSize={3} color="purple.500" />
|
||||
<Text fontSize="xs" color="purple.700" fontWeight="bold">
|
||||
热门个股
|
||||
@@ -942,20 +947,20 @@ const ConceptCenter = () => {
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Divider borderColor="purple.100" />
|
||||
<Divider borderColor="purple.100" my={{ base: 1, md: 0 }} />
|
||||
|
||||
<Flex width="100%" justify="space-between" align="center">
|
||||
{formatAddedDate(concept)}
|
||||
|
||||
<Button
|
||||
size="sm"
|
||||
size={{ base: 'xs', md: 'sm' }}
|
||||
leftIcon={<FaHistory />}
|
||||
bgGradient="linear(to-r, purple.500, pink.500)"
|
||||
color="white"
|
||||
variant="solid"
|
||||
onClick={(e) => handleViewContent(e, concept.concept, concept.concept_id)}
|
||||
borderRadius="full"
|
||||
px={4}
|
||||
px={{ base: 2, md: 4 }}
|
||||
fontWeight="medium"
|
||||
boxShadow="0 4px 12px rgba(139, 92, 246, 0.3)"
|
||||
_hover={{
|
||||
@@ -1598,7 +1603,7 @@ const ConceptCenter = () => {
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={6}>
|
||||
<SimpleGrid columns={{ base: 2, md: 2, lg: 3 }} spacing={{ base: 3, md: 6 }}>
|
||||
{[...Array(12)].map((_, i) => (
|
||||
<SkeletonCard key={i} />
|
||||
))}
|
||||
@@ -1606,7 +1611,7 @@ const ConceptCenter = () => {
|
||||
) : concepts.length > 0 ? (
|
||||
<>
|
||||
{viewMode === 'grid' ? (
|
||||
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={6} className="concept-grid">
|
||||
<SimpleGrid columns={{ base: 2, md: 2, lg: 3 }} spacing={{ base: 3, md: 6 }} className="concept-grid">
|
||||
{concepts.map((concept, index) => (
|
||||
<Box key={concept.concept_id} className="concept-item" role="group">
|
||||
<ConceptCard concept={concept} position={index} />
|
||||
|
||||
Reference in New Issue
Block a user