- Enhance LlmClient to handle malformed URLs and HTML error responses - Improve logging in report-generator-service worker - Update frontend API routes and hooks for analysis - Update various service configurations and persistence logic
149 lines
4.1 KiB
TypeScript
149 lines
4.1 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
|
|
interface TradingViewWidgetProps {
|
|
symbol: string;
|
|
market?: string;
|
|
height?: number;
|
|
width?: string;
|
|
}
|
|
|
|
declare global {
|
|
interface Window {
|
|
TradingView: any;
|
|
}
|
|
}
|
|
|
|
export function TradingViewWidget({
|
|
symbol,
|
|
market = 'china',
|
|
height = 400,
|
|
width = '100%'
|
|
}: TradingViewWidgetProps) {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
// 将中国股票代码转换为TradingView格式
|
|
const getTradingViewSymbol = (symbol: string, market: string) => {
|
|
if (market === 'china' || market === 'cn') {
|
|
// 处理中国股票代码
|
|
if (symbol.includes('.')) {
|
|
const [code, exchange] = symbol.split('.');
|
|
if (exchange === 'SH') {
|
|
return `SSE:${code}`;
|
|
} else if (exchange === 'SZ') {
|
|
return `SZSE:${code}`;
|
|
}
|
|
}
|
|
// 如果没有后缀,尝试推断
|
|
const onlyDigits = symbol.replace(/\D/g, '');
|
|
if (onlyDigits.length === 6) {
|
|
const first = onlyDigits[0];
|
|
if (first === '6') {
|
|
return `SSE:${onlyDigits}`;
|
|
} else if (first === '0' || first === '3') {
|
|
return `SZSE:${onlyDigits}`;
|
|
}
|
|
}
|
|
return symbol;
|
|
}
|
|
return symbol;
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (typeof window === 'undefined') return;
|
|
if (!symbol) return;
|
|
const tradingViewSymbol = getTradingViewSymbol(symbol, market);
|
|
|
|
const script = document.createElement('script');
|
|
script.src = 'https://s3.tradingview.com/external-embedding/embed-widget-advanced-chart.js';
|
|
script.async = true;
|
|
script.innerHTML = JSON.stringify({
|
|
autosize: true,
|
|
symbol: tradingViewSymbol,
|
|
interval: 'D',
|
|
timezone: 'Asia/Shanghai',
|
|
theme: 'light',
|
|
style: '1',
|
|
locale: 'zh_CN',
|
|
toolbar_bg: '#f1f3f6',
|
|
enable_publishing: false,
|
|
hide_top_toolbar: false,
|
|
hide_legend: false,
|
|
save_image: false,
|
|
container_id: `tradingview_${symbol}`,
|
|
studies: [],
|
|
show_popup_button: false,
|
|
no_referrer_id: true,
|
|
referrer_id: 'fundamental-analysis',
|
|
// 强制启用对数坐标
|
|
logarithmic: true,
|
|
disabled_features: [
|
|
'use_localstorage_for_settings',
|
|
'volume_force_overlay',
|
|
'create_volume_indicator_by_default'
|
|
],
|
|
enabled_features: [
|
|
'side_toolbar_in_fullscreen_mode',
|
|
'header_in_fullscreen_mode'
|
|
],
|
|
overrides: {
|
|
'paneProperties.background': '#ffffff',
|
|
'paneProperties.vertGridProperties.color': '#e1e3e6',
|
|
'paneProperties.horzGridProperties.color': '#e1e3e6',
|
|
'symbolWatermarkProperties.transparency': 90,
|
|
'scalesProperties.textColor': '#333333',
|
|
// 对数坐标设置
|
|
'scalesProperties.logarithmic': true,
|
|
'rightPriceScale.mode': 1,
|
|
'leftPriceScale.mode': 1,
|
|
'paneProperties.priceScaleProperties.log': true,
|
|
'paneProperties.priceScaleProperties.mode': 1
|
|
},
|
|
// 强制启用对数坐标
|
|
studies_overrides: {
|
|
'volume.volume.color.0': '#00bcd4',
|
|
'volume.volume.color.1': '#ff9800',
|
|
'volume.volume.transparency': 70
|
|
}
|
|
});
|
|
|
|
const container = containerRef.current;
|
|
if (container) {
|
|
// 避免重复挂载与 Next 热更新多次执行导致的报错
|
|
container.innerHTML = '';
|
|
// 延迟到下一帧,确保容器已插入并可获取 iframe.contentWindow
|
|
requestAnimationFrame(() => {
|
|
try {
|
|
if (container.isConnected) {
|
|
container.appendChild(script);
|
|
}
|
|
} catch (e) {
|
|
// 忽略偶发性 contentWindow 不可用的报错
|
|
console.warn('TradingView widget mount error:', e);
|
|
}
|
|
});
|
|
}
|
|
|
|
return () => {
|
|
const c = containerRef.current;
|
|
if (c) {
|
|
try {
|
|
c.innerHTML = '';
|
|
} catch {}
|
|
}
|
|
};
|
|
}, [symbol, market]);
|
|
|
|
return (
|
|
<div className="w-full">
|
|
<div
|
|
ref={containerRef}
|
|
id={`tradingview_${symbol}`}
|
|
style={{ height: `${height}px`, width }}
|
|
className="border rounded-lg overflow-hidden"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|