| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- import React from 'react';
- import { Button, Space } from 'antd';
- import { SendOutlined, CloseOutlined } from '@ant-design/icons';
- import type { ClientState } from '@/types';
- import { px2vw } from '@/utils/viewport';
- import { getOssUrl } from '@/utils/oss';
- import { wrapTrackedClick } from '@/utils/tracking';
- const progressRing = getOssUrl('progress-ring.svg');
- const micIcon = getOssUrl('mic-icon.svg');
- interface RecordButtonProps {
- state: ClientState;
- onStartRecord: () => void;
- onSend: () => void;
- onCancel: () => void;
- /** 埋点用页面 ID,默认 chatbot;教程页传 tutorial_1 ~ tutorial_10 */
- pageId?: string;
- }
- /**
- * 录音控制按钮组件 - 根据Figma设计
- * 麦克风按钮:96x96,带114x114渐变进度环
- */
- export const RecordButton: React.FC<RecordButtonProps> = ({
- state,
- onStartRecord,
- onSend,
- onCancel,
- pageId = 'chatbot',
- }) => {
- // 空闲或错误状态:仅显示麦克风按钮(隐藏 Tap to chat 提示)
- if (state === 'idle' || state === 'error') {
- return (
- <div className="flex flex-col items-center justify-center relative">
- {/* 麦克风按钮 - 响应式尺寸 */}
- <div className="relative flex items-center justify-center">
- {/* 进度环 (114x114) - 响应式 */}
- <div className="absolute flex items-center justify-center"
- style={{
- width: px2vw(114),
- height: px2vw(114),
- maxWidth: '32vw',
- maxHeight: '32vw',
- }}>
- <img
- src={progressRing}
- alt="Progress Ring"
- className="w-full h-full object-contain"
- />
- </div>
- {/* 麦克风按钮背景 (96x96) - 响应式 */}
- <button
- onClick={wrapTrackedClick(pageId, 'mic', onStartRecord)}
- className="relative rounded-full flex items-center justify-center cursor-pointer transition-all duration-300 hover:scale-105 active:scale-95 focus:outline-none touch-manipulation"
- style={{
- width: px2vw(96),
- height: px2vw(96),
- minWidth: px2vw(44),
- minHeight: px2vw(44),
- maxWidth: '28vw',
- maxHeight: '28vw',
- background: 'rgba(255, 255, 255, 0.1)',
- backdropFilter: 'blur(15px)',
- WebkitBackdropFilter: 'blur(15px)',
- border: '1px solid transparent',
- backgroundImage: `conic-gradient(from 102deg at 53% 39%, rgba(64, 64, 64, 0.5) 3%, rgba(64, 64, 64, 0.35) 9%, rgba(255, 255, 255, 0.5) 17%, rgba(255, 255, 255, 0.5) 30%, rgba(64, 64, 64, 0.35) 52%, rgba(249, 249, 249, 1) 58%, rgba(255, 255, 255, 0.5) 80%, rgba(249, 249, 249, 0.5) 91%)`,
- boxShadow: '-1px 3px 6px 0px rgba(0, 0, 0, 0.08), -3px 11px 11px 0px rgba(0, 0, 0, 0.07), -7px 24px 15px 0px rgba(0, 0, 0, 0.04), -13px 43px 18px 0px rgba(0, 0, 0, 0.01), -20px 68px 20px 0px rgba(0, 0, 0, 0)',
- }}
- >
- {/* 麦克风图标 - 响应式尺寸 */}
- <div className="flex items-center justify-center"
- style={{
- width: px2vw(32),
- height: px2vw(32),
- maxWidth: '10vw',
- maxHeight: '10vw',
- }}>
- <img
- src={micIcon}
- alt="Microphone"
- className="w-full h-full object-contain"
- />
- </div>
- </button>
- </div>
- </div>
- );
- }
- // 录音状态:显示取消/发送按钮
- if (state === 'recording') {
- return (
- <div className="flex flex-col items-center justify-center">
- {/* 录音状态提示 - 响应式尺寸 */}
- <div className="flex items-center justify-center"
- style={{
- marginBottom: px2vw(20),
- gap: px2vw(8),
- }}>
- <span className="inline-block rounded-full bg-red-500 animate-pulse"
- style={{
- width: px2vw(8),
- height: px2vw(8),
- }} />
- <span className="text-white"
- style={{
- fontSize: px2vw(12),
- }}>
- 录音中...
- </span>
- </div>
- {/* 按钮组 - 响应式尺寸 */}
- <Space size="large" className="flex items-center justify-center flex-wrap">
- <Button
- size="large"
- icon={<CloseOutlined />}
- onClick={wrapTrackedClick(pageId, 'stop', onCancel)}
- className="bg-white/20 border-white/30 text-white hover:bg-white/30 touch-manipulation"
- style={{
- width: px2vw(90),
- height: px2vw(48),
- minWidth: px2vw(44),
- minHeight: px2vw(44),
- fontSize: px2vw(12),
- }}
- >
- 取消
- </Button>
- <Button
- type="primary"
- size="large"
- icon={<SendOutlined />}
- onClick={wrapTrackedClick(pageId, 'send', onSend)}
- className="touch-manipulation"
- style={{
- width: px2vw(90),
- height: px2vw(48),
- minWidth: px2vw(44),
- minHeight: px2vw(44),
- fontSize: px2vw(12),
- }}
- >
- 发送
- </Button>
- </Space>
- </div>
- );
- }
- // 发送状态:显示加载中
- if (state === 'sending') {
- return (
- <div className="flex flex-col items-center justify-center">
- <Button
- type="primary"
- loading
- size="large"
- className="touch-manipulation"
- style={{
- width: px2vw(120),
- height: px2vw(50),
- minWidth: '44px',
- minHeight: '44px',
- fontSize: px2vw(14),
- }}
- >
- 处理中...
- </Button>
- </div>
- );
- }
- // 播放状态:显示禁用按钮
- if (state === 'playing') {
- return (
- <div className="flex flex-col items-center justify-center">
- <Button
- type="primary"
- size="large"
- disabled
- className="touch-manipulation"
- style={{
- width: px2vw(120),
- height: px2vw(50),
- minWidth: '44px',
- minHeight: '44px',
- fontSize: px2vw(14),
- }}
- >
- 播放中...
- </Button>
- </div>
- );
- }
- return null;
- };
|