feat: 事件详情权限加上权限校验
This commit is contained in:
@@ -17,6 +17,7 @@ import { getImportanceConfig } from '../../../../constants/importanceLevels';
|
||||
import { eventService } from '../../../../services/eventService';
|
||||
import { useEventStocks } from '../StockDetailPanel/hooks/useEventStocks';
|
||||
import { toggleEventFollow, selectEventFollowStatus } from '../../../../store/slices/communityDataSlice';
|
||||
import { useAuth } from '../../../../contexts/AuthContext';
|
||||
import EventHeaderInfo from './EventHeaderInfo';
|
||||
import EventDescriptionSection from './EventDescriptionSection';
|
||||
import RelatedConceptsSection from './RelatedConceptsSection';
|
||||
@@ -24,6 +25,8 @@ import RelatedStocksSection from './RelatedStocksSection';
|
||||
import CollapsibleSection from './CollapsibleSection';
|
||||
import HistoricalEvents from '../../../EventDetail/components/HistoricalEvents';
|
||||
import TransmissionChainAnalysis from '../../../EventDetail/components/TransmissionChainAnalysis';
|
||||
import SubscriptionBadge from '../../../../components/SubscriptionBadge';
|
||||
import SubscriptionUpgradeModal from '../../../../components/SubscriptionUpgradeModal';
|
||||
|
||||
/**
|
||||
* 动态新闻详情面板主组件
|
||||
@@ -32,16 +35,34 @@ import TransmissionChainAnalysis from '../../../EventDetail/components/Transmiss
|
||||
*/
|
||||
const DynamicNewsDetailPanel = ({ event }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { user } = useAuth();
|
||||
const cardBg = useColorModeValue('white', 'gray.800');
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
||||
const textColor = useColorModeValue('gray.600', 'gray.400');
|
||||
const toast = useToast();
|
||||
|
||||
// 获取用户会员等级(修复:字段名从 subscription_tier 改为 subscription_type)
|
||||
const userTier = user?.subscription_type || 'free';
|
||||
|
||||
// 从 Redux 读取关注状态
|
||||
const eventFollowStatus = useSelector(selectEventFollowStatus);
|
||||
const isFollowing = event?.id ? (eventFollowStatus[event.id]?.isFollowing || false) : false;
|
||||
const followerCount = event?.id ? (eventFollowStatus[event.id]?.followerCount || event.follower_count || 0) : 0;
|
||||
|
||||
// 权限判断函数
|
||||
const hasAccess = useCallback((requiredTier) => {
|
||||
const tierLevel = { free: 0, pro: 1, max: 2 };
|
||||
const result = tierLevel[userTier] >= tierLevel[requiredTier];
|
||||
return result;
|
||||
}, [userTier]);
|
||||
|
||||
// 升级弹窗状态
|
||||
const [upgradeModal, setUpgradeModal] = useState({
|
||||
isOpen: false,
|
||||
requiredLevel: 'pro',
|
||||
featureName: ''
|
||||
});
|
||||
|
||||
// 使用 Hook 获取实时数据(禁用自动加载,改为手动触发)
|
||||
const {
|
||||
stocks,
|
||||
@@ -55,10 +76,19 @@ const DynamicNewsDetailPanel = ({ event }) => {
|
||||
loadChainAnalysis
|
||||
} = useEventStocks(event?.id, event?.created_at, { autoLoad: false });
|
||||
|
||||
// 子区块折叠状态管理(默认折叠)+ 加载追踪
|
||||
const [isStocksOpen, setIsStocksOpen] = useState(false);
|
||||
// 相关股票、相关概念、历史事件和传导链的权限
|
||||
const canAccessStocks = hasAccess('pro');
|
||||
const canAccessConcepts = hasAccess('pro');
|
||||
const canAccessHistorical = hasAccess('pro');
|
||||
const canAccessTransmission = hasAccess('max');
|
||||
|
||||
// 子区块折叠状态管理 + 加载追踪
|
||||
// PRO 会员的相关股票默认展开
|
||||
const [isStocksOpen, setIsStocksOpen] = useState(canAccessStocks && userTier === 'pro');
|
||||
const [hasLoadedStocks, setHasLoadedStocks] = useState(false);
|
||||
|
||||
const [isConceptsOpen, setIsConceptsOpen] = useState(false);
|
||||
|
||||
const [isHistoricalOpen, setIsHistoricalOpen] = useState(false);
|
||||
const [hasLoadedHistorical, setHasLoadedHistorical] = useState(false);
|
||||
|
||||
@@ -75,7 +105,25 @@ const DynamicNewsDetailPanel = ({ event }) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 相关股票 - 展开时加载
|
||||
// 锁定点击处理 - 弹出升级弹窗
|
||||
const handleLockedClick = useCallback((featureName, requiredLevel) => {
|
||||
setUpgradeModal({
|
||||
isOpen: true,
|
||||
requiredLevel,
|
||||
featureName
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 关闭升级弹窗
|
||||
const handleCloseUpgradeModal = useCallback(() => {
|
||||
setUpgradeModal({
|
||||
isOpen: false,
|
||||
requiredLevel: 'pro',
|
||||
featureName: ''
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 相关股票 - 展开时加载(需要 PRO 权限)
|
||||
const handleStocksToggle = useCallback(() => {
|
||||
const newState = !isStocksOpen;
|
||||
setIsStocksOpen(newState);
|
||||
@@ -87,6 +135,11 @@ const DynamicNewsDetailPanel = ({ event }) => {
|
||||
}
|
||||
}, [isStocksOpen, hasLoadedStocks, loadStocksData, event?.id]);
|
||||
|
||||
// 相关概念 - 展开/收起(无需加载)
|
||||
const handleConceptsToggle = useCallback(() => {
|
||||
setIsConceptsOpen(!isConceptsOpen);
|
||||
}, [isConceptsOpen]);
|
||||
|
||||
// 历史事件对比 - 展开时加载
|
||||
const handleHistoricalToggle = useCallback(() => {
|
||||
const newState = !isHistoricalOpen;
|
||||
@@ -114,13 +167,25 @@ const DynamicNewsDetailPanel = ({ event }) => {
|
||||
// 事件切换时重置所有子模块状态
|
||||
useEffect(() => {
|
||||
console.log('%c🔄 [事件切换] 重置所有子模块状态', 'color: #F59E0B; font-weight: bold;', { eventId: event?.id });
|
||||
setIsStocksOpen(false);
|
||||
|
||||
// PRO 会员的相关股票默认展开,其他情况收起
|
||||
const shouldOpenStocks = canAccessStocks && userTier === 'pro';
|
||||
setIsStocksOpen(shouldOpenStocks);
|
||||
setHasLoadedStocks(false);
|
||||
|
||||
// PRO 会员默认展开时,自动加载股票数据
|
||||
if (shouldOpenStocks) {
|
||||
console.log('%c📊 [PRO会员] 自动加载相关股票数据', 'color: #10B981; font-weight: bold;', { eventId: event?.id });
|
||||
loadStocksData();
|
||||
setHasLoadedStocks(true);
|
||||
}
|
||||
|
||||
setIsConceptsOpen(false);
|
||||
setIsHistoricalOpen(false);
|
||||
setHasLoadedHistorical(false);
|
||||
setIsTransmissionOpen(false);
|
||||
setHasLoadedTransmission(false);
|
||||
}, [event?.id]);
|
||||
}, [event?.id, canAccessStocks, userTier, loadStocksData]);
|
||||
|
||||
// 切换关注状态
|
||||
const handleToggleFollow = useCallback(async () => {
|
||||
@@ -196,12 +261,20 @@ const DynamicNewsDetailPanel = ({ event }) => {
|
||||
{/* 事件描述 */}
|
||||
<EventDescriptionSection description={event.description} />
|
||||
|
||||
{/* 相关股票(可折叠) - 懒加载 */}
|
||||
{/* 相关股票(可折叠) - 懒加载 - 需要 PRO 权限 */}
|
||||
<CollapsibleSection
|
||||
title="相关股票"
|
||||
isOpen={isStocksOpen}
|
||||
onToggle={handleStocksToggle}
|
||||
count={stocks?.length || 0}
|
||||
subscriptionBadge={(() => {
|
||||
if (!canAccessStocks) {
|
||||
return <SubscriptionBadge tier="pro" size="sm" />;
|
||||
}
|
||||
return null;
|
||||
})()}
|
||||
isLocked={!canAccessStocks}
|
||||
onLockedClick={() => handleLockedClick('相关股票', 'pro')}
|
||||
>
|
||||
{loading.stocks || loading.quotes ? (
|
||||
<Center py={4}>
|
||||
@@ -219,19 +292,27 @@ const DynamicNewsDetailPanel = ({ event }) => {
|
||||
)}
|
||||
</CollapsibleSection>
|
||||
|
||||
{/* 相关概念 */}
|
||||
{/* 相关概念(可折叠) - 需要 PRO 权限 */}
|
||||
<RelatedConceptsSection
|
||||
eventTitle={event.title}
|
||||
effectiveTradingDate={event.trading_date || event.created_at}
|
||||
eventTime={event.created_at}
|
||||
isOpen={isConceptsOpen}
|
||||
onToggle={handleConceptsToggle}
|
||||
subscriptionBadge={!canAccessConcepts ? <SubscriptionBadge tier="pro" size="sm" /> : null}
|
||||
isLocked={!canAccessConcepts}
|
||||
onLockedClick={() => handleLockedClick('相关概念', 'pro')}
|
||||
/>
|
||||
|
||||
{/* 历史事件对比(可折叠) - 懒加载 */}
|
||||
{/* 历史事件对比(可折叠) - 懒加载 - 需要 PRO 权限 */}
|
||||
<CollapsibleSection
|
||||
title="历史事件对比"
|
||||
isOpen={isHistoricalOpen}
|
||||
onToggle={handleHistoricalToggle}
|
||||
count={historicalEvents?.length || 0}
|
||||
subscriptionBadge={!canAccessHistorical ? <SubscriptionBadge tier="pro" size="sm" /> : null}
|
||||
isLocked={!canAccessHistorical}
|
||||
onLockedClick={() => handleLockedClick('历史事件对比', 'pro')}
|
||||
>
|
||||
{loading.historicalEvents ? (
|
||||
<Center py={4}>
|
||||
@@ -246,11 +327,14 @@ const DynamicNewsDetailPanel = ({ event }) => {
|
||||
)}
|
||||
</CollapsibleSection>
|
||||
|
||||
{/* 传导链分析(可折叠) - 懒加载 */}
|
||||
{/* 传导链分析(可折叠) - 懒加载 - 需要 MAX 权限 */}
|
||||
<CollapsibleSection
|
||||
title="传导链分析"
|
||||
isOpen={isTransmissionOpen}
|
||||
onToggle={handleTransmissionToggle}
|
||||
subscriptionBadge={!canAccessTransmission ? <SubscriptionBadge tier="max" size="sm" /> : null}
|
||||
isLocked={!canAccessTransmission}
|
||||
onLockedClick={() => handleLockedClick('传导链分析', 'max')}
|
||||
>
|
||||
<TransmissionChainAnalysis
|
||||
eventId={event.id}
|
||||
@@ -259,6 +343,17 @@ const DynamicNewsDetailPanel = ({ event }) => {
|
||||
</CollapsibleSection>
|
||||
</VStack>
|
||||
</CardBody>
|
||||
|
||||
{/* 升级弹窗 */}
|
||||
{upgradeModal.isOpen ? (
|
||||
<SubscriptionUpgradeModal
|
||||
isOpen={upgradeModal.isOpen}
|
||||
onClose={handleCloseUpgradeModal}
|
||||
requiredLevel={upgradeModal.requiredLevel}
|
||||
featureName={upgradeModal.featureName}
|
||||
currentLevel={userTier}
|
||||
/>
|
||||
): null }
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user