|
@@ -263,6 +263,11 @@ export const ChatBotPage = () => {
|
|
|
const [displayProgress, setDisplayProgress] = useState(0);
|
|
const [displayProgress, setDisplayProgress] = useState(0);
|
|
|
const animationFrameRef = useRef<number>();
|
|
const animationFrameRef = useRef<number>();
|
|
|
const totalProgressRef = useRef(0);
|
|
const totalProgressRef = useRef(0);
|
|
|
|
|
+ // 环形进度条参数(匹配 114×114 设计稿)
|
|
|
|
|
+ const PROGRESS_RING_CENTER = 57; // 114 / 2
|
|
|
|
|
+ const PROGRESS_RING_RADIUS = 52; // 估算半径
|
|
|
|
|
+ const PROGRESS_RING_START_ANGLE = 215; // 起始角度(度)
|
|
|
|
|
+ const PROGRESS_RING_MAX_ANGLE = 288; // 最大角度(100%时,2.88 * 100)
|
|
|
// 默认置为 true,避免组件初次挂载或路由返回时尚未完成数据同步就触发历史进度动画。
|
|
// 默认置为 true,避免组件初次挂载或路由返回时尚未完成数据同步就触发历史进度动画。
|
|
|
const shouldSkipProgressAnimationRef = useRef(true);
|
|
const shouldSkipProgressAnimationRef = useRef(true);
|
|
|
// 保存上一次的进度状态,用于计算动画起始值
|
|
// 保存上一次的进度状态,用于计算动画起始值
|
|
@@ -2764,6 +2769,78 @@ export const ChatBotPage = () => {
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ {/* 末端2px圆弧段:与当前进度条颜色一致,圆头 */}
|
|
|
|
|
+ {displayProgress > 0 && (() => {
|
|
|
|
|
+ // 半径稍微大一点,让它稍微超出主进度条,完全覆盖末端
|
|
|
|
|
+ const endArcRadius = PROGRESS_RING_RADIUS + 1.5;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算当前进度对应的角度(与主进度条一致)
|
|
|
|
|
+ const currentAngle = displayProgress * 2.88; // 0-288deg
|
|
|
|
|
+
|
|
|
|
|
+ // 计算当前进度条末端的角度(CSS坐标系:0°在上方)
|
|
|
|
|
+ const endAngle = PROGRESS_RING_START_ANGLE + currentAngle;
|
|
|
|
|
+
|
|
|
|
|
+ // 2px长度对应的角度(弧度转角度)
|
|
|
|
|
+ const twoPxAngleDeg = (2 / endArcRadius) * (180 / Math.PI);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算末端2px圆弧段的起始角度和结束角度
|
|
|
|
|
+ const endSegmentStartAngle = endAngle - twoPxAngleDeg;
|
|
|
|
|
+ const endSegmentEndAngle = endAngle;
|
|
|
|
|
+
|
|
|
|
|
+ // SVG 坐标系转换:SVG角度 = CSS角度 - 90°
|
|
|
|
|
+ const svgStartAngle = endSegmentStartAngle - 90;
|
|
|
|
|
+ const svgEndAngle = endSegmentEndAngle - 90;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算起始点和结束点坐标
|
|
|
|
|
+ const startAngleRad = (svgStartAngle * Math.PI) / 180;
|
|
|
|
|
+ const endAngleRad = (svgEndAngle * Math.PI) / 180;
|
|
|
|
|
+
|
|
|
|
|
+ const startX = PROGRESS_RING_CENTER + endArcRadius * Math.cos(startAngleRad);
|
|
|
|
|
+ const startY = PROGRESS_RING_CENTER + endArcRadius * Math.sin(startAngleRad);
|
|
|
|
|
+ const endX = PROGRESS_RING_CENTER + endArcRadius * Math.cos(endAngleRad);
|
|
|
|
|
+ const endY = PROGRESS_RING_CENTER + endArcRadius * Math.sin(endAngleRad);
|
|
|
|
|
+
|
|
|
|
|
+ // SVG path:绘制2px长度的圆弧段
|
|
|
|
|
+ const segmentAngle = twoPxAngleDeg;
|
|
|
|
|
+ const largeArcFlag = segmentAngle > 180 ? 1 : 0;
|
|
|
|
|
+ const pathData = `M ${startX} ${startY} A ${endArcRadius} ${endArcRadius} 0 ${largeArcFlag} 1 ${endX} ${endY}`;
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <svg
|
|
|
|
|
+ className="pointer-events-none"
|
|
|
|
|
+ viewBox="0 0 114 114"
|
|
|
|
|
+ style={{
|
|
|
|
|
+ position: 'absolute',
|
|
|
|
|
+ top: px2vmin(0),
|
|
|
|
|
+ left: '-0.5PX',
|
|
|
|
|
+ width: '100%',
|
|
|
|
|
+ height: '100%',
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <defs>
|
|
|
|
|
+ {/* 使用progressFull图片作为颜色源 */}
|
|
|
|
|
+ <pattern
|
|
|
|
|
+ id="chatbot-end-segment-pattern"
|
|
|
|
|
+ x="0"
|
|
|
|
|
+ y="0"
|
|
|
|
|
+ width="114"
|
|
|
|
|
+ height="114"
|
|
|
|
|
+ patternUnits="userSpaceOnUse"
|
|
|
|
|
+ >
|
|
|
|
|
+ <image href={progressFull} x="0" y="0" width="114" height="114" />
|
|
|
|
|
+ </pattern>
|
|
|
|
|
+ </defs>
|
|
|
|
|
+ <path
|
|
|
|
|
+ d={pathData}
|
|
|
|
|
+ stroke="url(#chatbot-end-segment-pattern)"
|
|
|
|
|
+ strokeWidth="7"
|
|
|
|
|
+ strokeLinecap="round"
|
|
|
|
|
+ fill="none"
|
|
|
|
|
+ />
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ );
|
|
|
|
|
+ })()}
|
|
|
|
|
+
|
|
|
{/* 进度条提示 - 相对于进度环定位 - 首次有进度时显示 */}
|
|
{/* 进度条提示 - 相对于进度环定位 - 首次有进度时显示 */}
|
|
|
{showProgressBarTooltip && isConnected && (
|
|
{showProgressBarTooltip && isConnected && (
|
|
|
<img
|
|
<img
|