Merge branch 'feature_bugfix/251113_ui' into feature_bugfix/251113_bugfix

* feature_bugfix/251113_ui:
  update ui
  update ui
  update ui
  update ui
  update ui
This commit is contained in:
zdl
2025-11-14 16:16:10 +08:00
12 changed files with 25226 additions and 49 deletions

Binary file not shown.

Binary file not shown.

127
app.py
View File

@@ -68,6 +68,17 @@ def load_trading_days():
print(f"加载交易日数据失败: {e}")
def row_to_dict(row):
"""
将 SQLAlchemy Row 对象转换为字典
兼容 SQLAlchemy 1.4+ 版本
"""
if row is None:
return None
# 使用 _mapping 属性来访问列数据
return dict(row._mapping)
def get_trading_day_near_date(target_date):
"""
获取距离目标日期最近的交易日
@@ -5642,7 +5653,8 @@ def get_stock_basic_info(stock_code):
# 转换为字典
basic_info = {}
for key, value in zip(result.keys(), result):
result_dict = row_to_dict(result)
for key, value in result_dict.items():
if isinstance(value, datetime):
basic_info[key] = value.strftime('%Y-%m-%d')
elif isinstance(value, Decimal):
@@ -5685,7 +5697,7 @@ def get_stock_announcements(stock_code):
announcements = []
for row in result:
announcement = {}
for key, value in zip(row.keys(), row):
for key, value in row_to_dict(row).items():
if value is None:
announcement[key] = None
elif isinstance(value, datetime):
@@ -5734,7 +5746,7 @@ def get_stock_disclosure_schedule(stock_code):
schedules = []
for row in result:
schedule = {}
for key, value in zip(row.keys(), row):
for key, value in row_to_dict(row).items():
if value is None:
schedule[key] = None
elif isinstance(value, datetime):
@@ -5815,7 +5827,7 @@ def get_stock_actual_control(stock_code):
control_info = []
for row in result:
control_record = {}
for key, value in zip(row.keys(), row):
for key, value in row_to_dict(row).items():
if value is None:
control_record[key] = None
elif isinstance(value, datetime):
@@ -5864,7 +5876,7 @@ def get_stock_concentration(stock_code):
concentration_info = []
for row in result:
concentration_record = {}
for key, value in zip(row.keys(), row):
for key, value in row_to_dict(row).items():
if value is None:
concentration_record[key] = None
elif isinstance(value, datetime):
@@ -5933,7 +5945,7 @@ def get_stock_management(stock_code):
management_info = []
for row in result:
management_record = {}
for key, value in zip(row.keys(), row):
for key, value in row_to_dict(row).items():
if value is None:
management_record[key] = None
elif isinstance(value, datetime):
@@ -5992,7 +6004,7 @@ def get_stock_top_circulation_shareholders(stock_code):
shareholders_info = []
for row in result:
shareholder_record = {}
for key, value in zip(row.keys(), row):
for key, value in row_to_dict(row).items():
if value is None:
shareholder_record[key] = None
elif isinstance(value, datetime):
@@ -6051,7 +6063,7 @@ def get_stock_top_shareholders(stock_code):
shareholders_info = []
for row in result:
shareholder_record = {}
for key, value in zip(row.keys(), row):
for key, value in row_to_dict(row).items():
if value is None:
shareholder_record[key] = None
elif isinstance(value, datetime):
@@ -6102,7 +6114,7 @@ def get_stock_branches(stock_code):
branches_info = []
for row in result:
branch_record = {}
for key, value in zip(row.keys(), row):
for key, value in row_to_dict(row).items():
if value is None:
branch_record[key] = None
elif isinstance(value, datetime):
@@ -6169,7 +6181,7 @@ def get_stock_patents(stock_code):
patents_info = []
for row in result:
patent_record = {}
for key, value in zip(row.keys(), row):
for key, value in row_to_dict(row).items():
if value is None:
patent_record[key] = None
elif isinstance(value, datetime):
@@ -8644,7 +8656,8 @@ def get_stock_info(seccode):
ORDER BY a.ENDDATE DESC LIMIT 1
""")
result = engine.execute(query, seccode=seccode).fetchone()
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode}).fetchone()
if not result:
return jsonify({
@@ -8667,7 +8680,8 @@ def get_stock_info(seccode):
ORDER BY F001D DESC LIMIT 1
""")
forecast_result = engine.execute(forecast_query, seccode=seccode).fetchone()
with engine.connect() as conn:
forecast_result = conn.execute(forecast_query, {'seccode': seccode}).fetchone()
data = {
'stock_code': result.SECCODE,
@@ -8828,7 +8842,8 @@ def get_balance_sheet(seccode):
ORDER BY ENDDATE DESC LIMIT :limit
""")
result = engine.execute(query, seccode=seccode, limit=limit)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'limit': limit})
data = []
for row in result:
@@ -9018,7 +9033,8 @@ def get_income_statement(seccode):
ORDER BY ENDDATE DESC LIMIT :limit
""")
result = engine.execute(query, seccode=seccode, limit=limit)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'limit': limit})
data = []
for row in result:
@@ -9227,7 +9243,8 @@ def get_cashflow(seccode):
ORDER BY ENDDATE DESC LIMIT :limit
""")
result = engine.execute(query, seccode=seccode, limit=limit)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'limit': limit})
data = []
for row in result:
@@ -9462,7 +9479,8 @@ def get_financial_metrics(seccode):
ORDER BY ENDDATE DESC LIMIT :limit
""")
result = engine.execute(query, seccode=seccode, limit=limit)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'limit': limit})
data = []
for row in result:
@@ -9602,7 +9620,8 @@ def get_main_business(seccode):
ORDER BY ENDDATE DESC LIMIT :limit
""")
periods = engine.execute(period_query, seccode=seccode, limit=limit).fetchall()
with engine.connect() as conn:
periods = conn.execute(period_query, {'seccode': seccode, 'limit': limit}).fetchall()
# 产品分类数据
product_data = []
@@ -9620,7 +9639,8 @@ def get_main_business(seccode):
ORDER BY F005N DESC
""")
result = engine.execute(query, seccode=seccode, enddate=period[0])
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'enddate': period[0]})
# Convert result to list to allow multiple iterations
rows = list(result)
@@ -9669,7 +9689,8 @@ def get_main_business(seccode):
ORDER BY F007N DESC
""")
result = engine.execute(query, seccode=seccode, enddate=period[0])
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'enddate': period[0]})
# Convert result to list to allow multiple iterations
rows = list(result)
@@ -9730,7 +9751,8 @@ def get_forecast(seccode):
ORDER BY F001D DESC, UPDATE_DATE DESC LIMIT 10
""")
forecast_result = engine.execute(forecast_query, seccode=seccode)
with engine.connect() as conn:
forecast_result = conn.execute(forecast_query, {'seccode': seccode})
forecast_data = []
for row in forecast_result:
@@ -9771,7 +9793,8 @@ def get_forecast(seccode):
ORDER BY F001D DESC LIMIT 8
""")
pretime_result = engine.execute(pretime_query, seccode=seccode)
with engine.connect() as conn:
pretime_result = conn.execute(pretime_query, {'seccode': seccode})
pretime_data = []
for row in pretime_result:
@@ -9870,7 +9893,8 @@ def get_industry_rank(seccode):
""")
# 获取多个报告期的数据
result = engine.execute(query, seccode=seccode, limit_total=limit * 4)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'limit_total': limit * 4})
# 按报告期和行业级别组织数据
data_by_period = {}
@@ -9990,7 +10014,8 @@ def get_period_comparison(seccode):
ORDER BY fi.ENDDATE DESC LIMIT :periods
""")
result = engine.execute(query, seccode=seccode, periods=periods)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'periods': periods})
data = []
for row in result:
@@ -10114,7 +10139,8 @@ def get_trade_data(seccode):
LIMIT :days
""")
result = engine.execute(query, seccode=seccode, end_date=end_date, days=days)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'end_date': end_date, 'days': days})
data = []
for row in result:
@@ -10190,7 +10216,8 @@ def get_funding_data(seccode):
ORDER BY TRADEDATE DESC LIMIT :days
""")
result = engine.execute(query, seccode=seccode, days=days)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'days': days})
data = []
for row in result:
@@ -10248,7 +10275,8 @@ def get_bigdeal_data(seccode):
ORDER BY TRADEDATE DESC, F007N LIMIT :days
""")
result = engine.execute(query, seccode=seccode, days=days)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode, 'days': days})
data = []
for row in result:
@@ -10322,7 +10350,8 @@ def get_unusual_data(seccode):
ORDER BY TRADEDATE DESC, F004N LIMIT 100
""")
result = engine.execute(query, seccode=seccode)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode})
data = []
for row in result:
@@ -10400,7 +10429,8 @@ def get_pledge_data(seccode):
ORDER BY ENDDATE DESC LIMIT 12
""")
result = engine.execute(query, seccode=seccode)
with engine.connect() as conn:
result = conn.execute(query, {'seccode': seccode})
data = []
for row in result:
@@ -10457,9 +10487,12 @@ def get_market_summary(seccode):
ORDER BY ENDDATE DESC LIMIT 1
""")
trade_result = engine.execute(trade_query, seccode=seccode).fetchone()
funding_result = engine.execute(funding_query, seccode=seccode).fetchone()
pledge_result = engine.execute(pledge_query, seccode=seccode).fetchone()
with engine.connect() as conn:
trade_result = conn.execute(trade_query, {'seccode': seccode}).fetchone()
with engine.connect() as conn:
funding_result = conn.execute(funding_query, {'seccode': seccode}).fetchone()
with engine.connect() as conn:
pledge_result = conn.execute(pledge_query, {'seccode': seccode}).fetchone()
summary = {
'stock_code': seccode,
@@ -10954,7 +10987,8 @@ def get_rise_analysis(seccode):
ORDER BY trade_date DESC LIMIT 100
""")
result = engine.execute(query, **params).fetchall()
with engine.connect() as conn:
result = conn.execute(query, params).fetchall()
# 格式化数据
rise_analysis_data = []
@@ -11016,7 +11050,8 @@ def get_comprehensive_analysis(company_code):
WHERE company_code = :company_code
""")
qualitative_result = engine.execute(qualitative_query, company_code=company_code).fetchone()
with engine.connect() as conn:
qualitative_result = conn.execute(qualitative_query, {'company_code': company_code}).fetchone()
# 获取业务板块分析
segments_query = text("""
@@ -11033,7 +11068,8 @@ def get_comprehensive_analysis(company_code):
ORDER BY created_at DESC
""")
segments_result = engine.execute(segments_query, company_code=company_code).fetchall()
with engine.connect() as conn:
segments_result = conn.execute(segments_query, {'company_code': company_code}).fetchall()
# 获取竞争地位数据 - 最新一期
competitive_query = text("""
@@ -11058,7 +11094,8 @@ def get_comprehensive_analysis(company_code):
ORDER BY report_period DESC LIMIT 1
""")
competitive_result = engine.execute(competitive_query, company_code=company_code).fetchone()
with engine.connect() as conn:
competitive_result = conn.execute(competitive_query, {'company_code': company_code}).fetchone()
# 获取业务结构数据 - 最新一期
business_structure_query = text("""
@@ -11085,7 +11122,8 @@ def get_comprehensive_analysis(company_code):
ORDER BY revenue_ratio DESC
""")
business_structure_result = engine.execute(business_structure_query, company_code=company_code).fetchall()
with engine.connect() as conn:
business_structure_result = conn.execute(business_structure_query, {'company_code': company_code}).fetchall()
# 构建返回数据
response_data = {
@@ -11222,7 +11260,8 @@ def get_value_chain_analysis(company_code):
ORDER BY node_level ASC, importance_score DESC
""")
nodes_result = engine.execute(nodes_query, company_code=company_code).fetchall()
with engine.connect() as conn:
nodes_result = conn.execute(nodes_query, {'company_code': company_code}).fetchall()
# 获取产业链流向数据
flows_query = text("""
@@ -11242,7 +11281,8 @@ def get_value_chain_analysis(company_code):
ORDER BY flow_ratio DESC
""")
flows_result = engine.execute(flows_query, company_code=company_code).fetchall()
with engine.connect() as conn:
flows_result = conn.execute(flows_query, {'company_code': company_code}).fetchall()
# 构建节点数据结构
nodes_by_level = {}
@@ -11352,7 +11392,8 @@ def get_key_factors_timeline(company_code):
ORDER BY display_order ASC, created_at ASC
""")
categories_result = engine.execute(categories_query, company_code=company_code).fetchall()
with engine.connect() as conn:
categories_result = conn.execute(categories_query, {'company_code': company_code}).fetchall()
# 获取关键因素详情
factors_query = text("""
@@ -11417,7 +11458,8 @@ def get_key_factors_timeline(company_code):
ORDER BY kf.report_period DESC, kf.impact_weight DESC, kf.updated_at DESC
""")
factors_result = engine.execute(factors_query, **params).fetchall()
with engine.connect() as conn:
factors_result = conn.execute(factors_query, params).fetchall()
# 获取发展时间线事件
timeline_query = text("""
@@ -11436,9 +11478,8 @@ def get_key_factors_timeline(company_code):
ORDER BY event_date DESC LIMIT :limit
""")
timeline_result = engine.execute(timeline_query,
company_code=company_code,
limit=event_limit).fetchall()
with engine.connect() as conn:
timeline_result = conn.execute(timeline_query, {'company_code': company_code, 'limit': event_limit}).fetchall()
# 构建关键因素数据结构
key_factors_data = {}

12556
app.py.backup Normal file

File diff suppressed because it is too large Load Diff

12556
app.py.backup_20251114_145340 Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -21,6 +21,8 @@ const { Text } = Typography;
* @param {Object} props.prefixStyle - 前缀标签的自定义样式(可选)
* @param {boolean} props.showAIBadge - 是否显示右上角 AI 标识,默认 true可选
* @param {Object} props.containerStyle - 容器额外样式(可选)
* @param {string} props.textColor - 文本颜色,默认自动判断背景色(可选)
* @param {string} props.titleColor - 标题颜色,默认继承 textColor可选
*
* @example
* <CitedContent
@@ -30,6 +32,7 @@ const { Text } = Typography;
* prefixStyle={{ color: '#666' }}
* showAIBadge={true}
* containerStyle={{ marginTop: 16 }}
* textColor="#E2E8F0"
* />
*/
const CitedContent = ({
@@ -38,7 +41,9 @@ const CitedContent = ({
prefix = '',
prefixStyle = {},
showAIBadge = true,
containerStyle = {}
containerStyle = {},
textColor,
titleColor
}) => {
// 处理数据
const processed = processCitationData(data);
@@ -52,6 +57,19 @@ const CitedContent = ({
return null;
}
// 自动判断文本颜色:如果容器背景是深色,使用浅色文本
const bgColor = containerStyle.backgroundColor;
const isDarkBg = bgColor && (
bgColor.includes('rgba(0,0,0') ||
bgColor.includes('rgba(0, 0, 0') ||
bgColor === 'transparent' ||
bgColor.includes('#1A202C') ||
bgColor.includes('#171923')
);
const finalTextColor = textColor || (isDarkBg ? '#E2E8F0' : '#262626');
const finalTitleColor = titleColor || finalTextColor;
return (
<div
style={{
@@ -87,7 +105,7 @@ const CitedContent = ({
{/* 标题栏 */}
{title && (
<div style={{ marginBottom: 12, paddingRight: 80 }}>
<Text strong style={{ fontSize: 14 }}>
<Text strong style={{ fontSize: 14, color: finalTitleColor }}>
{title}
</Text>
</div>
@@ -105,6 +123,7 @@ const CitedContent = ({
fontWeight: 'bold',
display: 'inline',
marginRight: 4,
color: finalTextColor,
...prefixStyle
}}>
{prefix}
@@ -114,7 +133,7 @@ const CitedContent = ({
{processed.segments.map((segment, index) => (
<React.Fragment key={`segment-${segment.citationId}`}>
{/* 文本片段 */}
<Text style={{ fontSize: 14, display: 'inline' }}>
<Text style={{ fontSize: 14, display: 'inline', color: finalTextColor }}>
{segment.text}
</Text>
@@ -126,7 +145,7 @@ const CitedContent = ({
{/* 在片段之间添加逗号分隔符(最后一个不加) */}
{index < processed.segments.length - 1 && (
<Text style={{ fontSize: 14, display: 'inline' }}></Text>
<Text style={{ fontSize: 14, display: 'inline', color: finalTextColor }}></Text>
)}
</React.Fragment>
))}

View File

@@ -311,6 +311,7 @@ const StockListItem = ({
data={stock.relation_desc}
title=""
showAIBadge={true}
textColor={PROFESSIONAL_COLORS.text.primary}
containerStyle={{
backgroundColor: 'transparent',
borderRadius: '0',

View File

@@ -344,6 +344,7 @@ const HistoricalEvents = ({
data={content}
title=""
showAIBadge={true}
textColor={PROFESSIONAL_COLORS.text.primary}
containerStyle={{
backgroundColor: useColorModeValue('#f7fafc', 'rgba(45, 55, 72, 0.6)'),
borderRadius: '8px',

View File

@@ -972,6 +972,7 @@ const TransmissionChainAnalysis = ({ eventId }) => {
<CitedContent
data={selectedNode.extra.description}
title=""
textColor={PROFESSIONAL_COLORS.text.primary}
/>
) : (
`${selectedNode.extra.description}AI合成`
@@ -1081,7 +1082,8 @@ const TransmissionChainAnalysis = ({ eventId }) => {
data={parent.transmission_mechanism}
title=""
prefix="机制:"
prefixStyle={{ fontSize: 12, color: '#666', fontWeight: 'bold' }}
prefixStyle={{ fontSize: 12, color: PROFESSIONAL_COLORS.text.secondary, fontWeight: 'bold' }}
textColor={PROFESSIONAL_COLORS.text.primary}
containerStyle={{ marginTop: 8 }}
showAIBadge={false}
/>
@@ -1136,7 +1138,8 @@ const TransmissionChainAnalysis = ({ eventId }) => {
data={child.transmission_mechanism}
title=""
prefix="机制:"
prefixStyle={{ fontSize: 12, color: '#666', fontWeight: 'bold' }}
prefixStyle={{ fontSize: 12, color: PROFESSIONAL_COLORS.text.secondary, fontWeight: 'bold' }}
textColor={PROFESSIONAL_COLORS.text.primary}
containerStyle={{ marginTop: 8 }}
showAIBadge={false}
/>