修改concept
This commit is contained in:
@@ -2,6 +2,195 @@
|
||||
* 文本处理工具函数
|
||||
*/
|
||||
|
||||
/**
|
||||
* 简单的 MD5 实现(与 Python hashlib.md5 兼容)
|
||||
* 用于生成概念链接的哈希值
|
||||
*/
|
||||
const md5 = (string) => {
|
||||
function rotateLeft(lValue, iShiftBits) {
|
||||
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
|
||||
}
|
||||
|
||||
function addUnsigned(lX, lY) {
|
||||
const lX8 = lX & 0x80000000;
|
||||
const lY8 = lY & 0x80000000;
|
||||
const lX4 = lX & 0x40000000;
|
||||
const lY4 = lY & 0x40000000;
|
||||
const lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
|
||||
if (lX4 & lY4) return lResult ^ 0x80000000 ^ lX8 ^ lY8;
|
||||
if (lX4 | lY4) {
|
||||
if (lResult & 0x40000000) return lResult ^ 0xC0000000 ^ lX8 ^ lY8;
|
||||
return lResult ^ 0x40000000 ^ lX8 ^ lY8;
|
||||
}
|
||||
return lResult ^ lX8 ^ lY8;
|
||||
}
|
||||
|
||||
function f(x, y, z) { return (x & y) | (~x & z); }
|
||||
function g(x, y, z) { return (x & z) | (y & ~z); }
|
||||
function h(x, y, z) { return x ^ y ^ z; }
|
||||
function i(x, y, z) { return y ^ (x | ~z); }
|
||||
|
||||
function ff(a, b, c, d, x, s, ac) {
|
||||
a = addUnsigned(a, addUnsigned(addUnsigned(f(b, c, d), x), ac));
|
||||
return addUnsigned(rotateLeft(a, s), b);
|
||||
}
|
||||
function gg(a, b, c, d, x, s, ac) {
|
||||
a = addUnsigned(a, addUnsigned(addUnsigned(g(b, c, d), x), ac));
|
||||
return addUnsigned(rotateLeft(a, s), b);
|
||||
}
|
||||
function hh(a, b, c, d, x, s, ac) {
|
||||
a = addUnsigned(a, addUnsigned(addUnsigned(h(b, c, d), x), ac));
|
||||
return addUnsigned(rotateLeft(a, s), b);
|
||||
}
|
||||
function ii(a, b, c, d, x, s, ac) {
|
||||
a = addUnsigned(a, addUnsigned(addUnsigned(i(b, c, d), x), ac));
|
||||
return addUnsigned(rotateLeft(a, s), b);
|
||||
}
|
||||
|
||||
function convertToWordArray(str) {
|
||||
let lWordCount;
|
||||
const lMessageLength = str.length;
|
||||
const lNumberOfWordsTemp1 = lMessageLength + 8;
|
||||
const lNumberOfWordsTemp2 = (lNumberOfWordsTemp1 - (lNumberOfWordsTemp1 % 64)) / 64;
|
||||
const lNumberOfWords = (lNumberOfWordsTemp2 + 1) * 16;
|
||||
const lWordArray = Array(lNumberOfWords - 1);
|
||||
let lBytePosition = 0;
|
||||
let lByteCount = 0;
|
||||
while (lByteCount < lMessageLength) {
|
||||
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
|
||||
lBytePosition = (lByteCount % 4) * 8;
|
||||
lWordArray[lWordCount] = lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition);
|
||||
lByteCount++;
|
||||
}
|
||||
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
|
||||
lBytePosition = (lByteCount % 4) * 8;
|
||||
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
|
||||
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
|
||||
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
|
||||
return lWordArray;
|
||||
}
|
||||
|
||||
function wordToHex(lValue) {
|
||||
let wordToHexValue = '', wordToHexValueTemp = '', lByte, lCount;
|
||||
for (lCount = 0; lCount <= 3; lCount++) {
|
||||
lByte = (lValue >>> (lCount * 8)) & 255;
|
||||
wordToHexValueTemp = '0' + lByte.toString(16);
|
||||
wordToHexValue = wordToHexValue + wordToHexValueTemp.substr(wordToHexValueTemp.length - 2, 2);
|
||||
}
|
||||
return wordToHexValue;
|
||||
}
|
||||
|
||||
function utf8Encode(str) {
|
||||
str = str.replace(/\r\n/g, '\n');
|
||||
let utftext = '';
|
||||
for (let n = 0; n < str.length; n++) {
|
||||
const c = str.charCodeAt(n);
|
||||
if (c < 128) {
|
||||
utftext += String.fromCharCode(c);
|
||||
} else if (c > 127 && c < 2048) {
|
||||
utftext += String.fromCharCode((c >> 6) | 192);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
} else {
|
||||
utftext += String.fromCharCode((c >> 12) | 224);
|
||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
}
|
||||
return utftext;
|
||||
}
|
||||
|
||||
const x = convertToWordArray(utf8Encode(string));
|
||||
let a = 0x67452301, b = 0xEFCDAB89, c = 0x98BADCFE, d = 0x10325476;
|
||||
const S11 = 7, S12 = 12, S13 = 17, S14 = 22;
|
||||
const S21 = 5, S22 = 9, S23 = 14, S24 = 20;
|
||||
const S31 = 4, S32 = 11, S33 = 16, S34 = 23;
|
||||
const S41 = 6, S42 = 10, S43 = 15, S44 = 21;
|
||||
|
||||
for (let k = 0; k < x.length; k += 16) {
|
||||
const AA = a, BB = b, CC = c, DD = d;
|
||||
a = ff(a, b, c, d, x[k], S11, 0xD76AA478);
|
||||
d = ff(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
|
||||
c = ff(c, d, a, b, x[k + 2], S13, 0x242070DB);
|
||||
b = ff(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
|
||||
a = ff(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
|
||||
d = ff(d, a, b, c, x[k + 5], S12, 0x4787C62A);
|
||||
c = ff(c, d, a, b, x[k + 6], S13, 0xA8304613);
|
||||
b = ff(b, c, d, a, x[k + 7], S14, 0xFD469501);
|
||||
a = ff(a, b, c, d, x[k + 8], S11, 0x698098D8);
|
||||
d = ff(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
|
||||
c = ff(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
|
||||
b = ff(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
|
||||
a = ff(a, b, c, d, x[k + 12], S11, 0x6B901122);
|
||||
d = ff(d, a, b, c, x[k + 13], S12, 0xFD987193);
|
||||
c = ff(c, d, a, b, x[k + 14], S13, 0xA679438E);
|
||||
b = ff(b, c, d, a, x[k + 15], S14, 0x49B40821);
|
||||
a = gg(a, b, c, d, x[k + 1], S21, 0xF61E2562);
|
||||
d = gg(d, a, b, c, x[k + 6], S22, 0xC040B340);
|
||||
c = gg(c, d, a, b, x[k + 11], S23, 0x265E5A51);
|
||||
b = gg(b, c, d, a, x[k], S24, 0xE9B6C7AA);
|
||||
a = gg(a, b, c, d, x[k + 5], S21, 0xD62F105D);
|
||||
d = gg(d, a, b, c, x[k + 10], S22, 0x2441453);
|
||||
c = gg(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
|
||||
b = gg(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
|
||||
a = gg(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
|
||||
d = gg(d, a, b, c, x[k + 14], S22, 0xC33707D6);
|
||||
c = gg(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
|
||||
b = gg(b, c, d, a, x[k + 8], S24, 0x455A14ED);
|
||||
a = gg(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
|
||||
d = gg(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
|
||||
c = gg(c, d, a, b, x[k + 7], S23, 0x676F02D9);
|
||||
b = gg(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
|
||||
a = hh(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
|
||||
d = hh(d, a, b, c, x[k + 8], S32, 0x8771F681);
|
||||
c = hh(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
|
||||
b = hh(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
|
||||
a = hh(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
|
||||
d = hh(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
|
||||
c = hh(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
|
||||
b = hh(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
|
||||
a = hh(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
|
||||
d = hh(d, a, b, c, x[k], S32, 0xEAA127FA);
|
||||
c = hh(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
|
||||
b = hh(b, c, d, a, x[k + 6], S34, 0x4881D05);
|
||||
a = hh(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
|
||||
d = hh(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
|
||||
c = hh(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
|
||||
b = hh(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
|
||||
a = ii(a, b, c, d, x[k], S41, 0xF4292244);
|
||||
d = ii(d, a, b, c, x[k + 7], S42, 0x432AFF97);
|
||||
c = ii(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
|
||||
b = ii(b, c, d, a, x[k + 5], S44, 0xFC93A039);
|
||||
a = ii(a, b, c, d, x[k + 12], S41, 0x655B59C3);
|
||||
d = ii(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
|
||||
c = ii(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
|
||||
b = ii(b, c, d, a, x[k + 1], S44, 0x85845DD1);
|
||||
a = ii(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
|
||||
d = ii(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
|
||||
c = ii(c, d, a, b, x[k + 6], S43, 0xA3014314);
|
||||
b = ii(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
|
||||
a = ii(a, b, c, d, x[k + 4], S41, 0xF7537E82);
|
||||
d = ii(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
|
||||
c = ii(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
|
||||
b = ii(b, c, d, a, x[k + 9], S44, 0xEB86D391);
|
||||
a = addUnsigned(a, AA);
|
||||
b = addUnsigned(b, BB);
|
||||
c = addUnsigned(c, CC);
|
||||
d = addUnsigned(d, DD);
|
||||
}
|
||||
return (wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d)).toLowerCase();
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据概念名称生成概念详情页面的 URL
|
||||
* @param {string} conceptName - 概念名称
|
||||
* @returns {string} 概念详情页面的完整 URL
|
||||
*/
|
||||
export const getConceptHtmlUrl = (conceptName) => {
|
||||
if (!conceptName) return '';
|
||||
const conceptHash = md5(conceptName);
|
||||
return `https://valuefrontier.cn/htmls/concept/${conceptHash}/`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理包含<br>标签的文本,转换为适合在React中显示的格式
|
||||
* @param {string} text - 包含<br>标签的文本
|
||||
|
||||
@@ -29,6 +29,7 @@ import { AlertCircle, Clock, TrendingUp, Info, RefreshCw } from 'lucide-react';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
import { logger } from '@utils/logger';
|
||||
import { getApiBase } from '@utils/apiConfig';
|
||||
import { getConceptHtmlUrl } from '@utils/textUtils';
|
||||
import { useIndexQuote } from '@hooks/useIndexQuote';
|
||||
import conceptStaticService from '@services/conceptStaticService';
|
||||
import { GLASS_BLUR } from '@/constants/glassConfig';
|
||||
@@ -450,7 +451,7 @@ const FlowingConcepts = () => {
|
||||
};
|
||||
|
||||
const handleClick = (name) => {
|
||||
window.open(`https://valuefrontier.cn/htmls/${name}.html`, '_blank');
|
||||
window.open(getConceptHtmlUrl(name), '_blank');
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
ArrowLeft,
|
||||
} from 'lucide-react';
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { getConceptHtmlUrl } from '../../../utils/textUtils';
|
||||
import { GLASS_BLUR } from '@/constants/glassConfig';
|
||||
|
||||
// 极光动画 - 黑金色主题
|
||||
@@ -942,7 +943,7 @@ const ForceGraphView = ({
|
||||
|
||||
if (data.level === 'concept') {
|
||||
// 跳转到概念详情页
|
||||
const htmlPath = `https://valuefrontier.cn/htmls/${encodeURIComponent(params.name)}.html`;
|
||||
const htmlPath = getConceptHtmlUrl(params.name);
|
||||
window.open(htmlPath, '_blank');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ import {
|
||||
ExternalLink,
|
||||
} from 'lucide-react';
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { getConceptHtmlUrl } from '../../../utils/textUtils';
|
||||
import { GLASS_BLUR } from '@/constants/glassConfig';
|
||||
|
||||
// 一级分类图标映射
|
||||
@@ -685,7 +686,7 @@ const HierarchyView = ({
|
||||
]);
|
||||
} else if (item.level === 'concept') {
|
||||
// 跳转到概念详情页
|
||||
const htmlPath = `https://valuefrontier.cn/htmls/${encodeURIComponent(item.name)}.html`;
|
||||
const htmlPath = getConceptHtmlUrl(item.name);
|
||||
window.open(htmlPath, '_blank');
|
||||
}
|
||||
}, [currentLv1, currentLv2]);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { useSearchParams, useNavigate } from 'react-router-dom';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { getConceptHtmlUrl } from '../../utils/textUtils';
|
||||
import defaultEventImage from '../../assets/img/default-event.jpg';
|
||||
import {
|
||||
Box,
|
||||
@@ -674,7 +675,7 @@ const ConceptCenter = () => {
|
||||
trackConceptClicked(concept, position);
|
||||
}
|
||||
|
||||
const htmlPath = `https://valuefrontier.cn/htmls/${encodeURIComponent(conceptName)}.html`;
|
||||
const htmlPath = getConceptHtmlUrl(conceptName);
|
||||
window.open(htmlPath, '_blank');
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import dayjs from 'dayjs';
|
||||
import tradingDayUtils from '../../../utils/tradingDayUtils'; // 引入交易日工具
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { getApiBase } from '../../../utils/apiConfig';
|
||||
import { getConceptHtmlUrl } from '../../../utils/textUtils';
|
||||
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
|
||||
|
||||
// 增强版 ConceptCard 组件 - 展示更多数据细节
|
||||
@@ -61,7 +62,7 @@ const ConceptCard = ({ concept, tradingDate, onViewDetails }) => {
|
||||
|
||||
// 处理概念点击
|
||||
const handleConceptClick = () => {
|
||||
window.open(`https://valuefrontier.cn/htmls/${encodeURIComponent(concept.concept)}.html`, '_blank');
|
||||
window.open(getConceptHtmlUrl(concept.concept), '_blank');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -758,7 +759,7 @@ const RelatedConcepts = ({ eventTitle, eventTime, eventId, loading: externalLoad
|
||||
size="lg"
|
||||
flex={1}
|
||||
onClick={() => {
|
||||
window.open(`https://valuefrontier.cn/htmls/${encodeURIComponent(selectedConcept.concept)}.html`, '_blank');
|
||||
window.open(getConceptHtmlUrl(selectedConcept.concept), '_blank');
|
||||
}}
|
||||
leftIcon={<Icon as={ExternalLink} boxSize={4} />}
|
||||
>
|
||||
|
||||
@@ -55,6 +55,7 @@ import HotspotOverview from './components/HotspotOverview';
|
||||
import FlexScreen from './components/FlexScreen';
|
||||
import { echarts } from '@lib/echarts';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { getConceptHtmlUrl } from '../../utils/textUtils';
|
||||
import tradingDays from '../../data/tradingDays.json';
|
||||
import { useStockOverviewEvents } from './hooks/useStockOverviewEvents';
|
||||
import { GLASS_BLUR } from '@/constants/glassConfig';
|
||||
@@ -534,7 +535,7 @@ const StockOverview = () => {
|
||||
// 🎯 追踪概念点击
|
||||
trackConceptClicked(concept, rank);
|
||||
|
||||
const htmlPath = `/htmls/${concept.concept_name}.html`;
|
||||
const htmlPath = getConceptHtmlUrl(concept.concept_name);
|
||||
window.open(htmlPath, '_blank');
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user