style: show voice error message
This commit is contained in:
@@ -7,10 +7,12 @@ interface AlertInfoProps {
|
||||
message: string;
|
||||
rows?: number;
|
||||
icon?: React.ReactNode;
|
||||
ellipsis?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const AlertInfo: React.FC<AlertInfoProps> = (props) => {
|
||||
const { message, type, rows = 1 } = props;
|
||||
const { message, type, rows = 1, ellipsis, style } = props;
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
@@ -18,16 +20,21 @@ const AlertInfo: React.FC<AlertInfoProps> = (props) => {
|
||||
return (
|
||||
<Typography.Paragraph
|
||||
type={type}
|
||||
ellipsis={{
|
||||
rows: rows,
|
||||
tooltip: message
|
||||
}}
|
||||
ellipsis={
|
||||
ellipsis !== undefined
|
||||
? ellipsis
|
||||
: {
|
||||
rows: rows,
|
||||
tooltip: message
|
||||
}
|
||||
}
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
padding: '2px 5px',
|
||||
borderRadius: 'var(--border-radius-base)',
|
||||
margin: 0,
|
||||
backgroundColor: 'var(--ant-color-error-bg)'
|
||||
backgroundColor: 'var(--ant-color-error-bg)',
|
||||
...style
|
||||
}}
|
||||
>
|
||||
<WarningOutlined className="m-r-8" />
|
||||
|
||||
@@ -2,19 +2,54 @@
|
||||
width: 100%;
|
||||
display: flex;
|
||||
background-color: var(--ant-color-fill-quaternary);
|
||||
border-radius: 36px;
|
||||
border-radius: 6px;
|
||||
|
||||
.player-ui {
|
||||
padding: 5px 10px;
|
||||
padding: 8px 16px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-inline: 10px;
|
||||
}
|
||||
|
||||
.play-btn {
|
||||
margin-inline: 30px;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
overflow: hidden;
|
||||
|
||||
.ant-btn {
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.backward,
|
||||
.forward {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.slider {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.slider-inner {
|
||||
flex: 1;
|
||||
margin-inline: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.play-content {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
@@ -43,7 +78,7 @@
|
||||
}
|
||||
|
||||
.ant-slider-horizontal {
|
||||
margin-block: 2px;
|
||||
margin-block: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { formatTime } from '@/utils/index';
|
||||
import { PauseCircleFilled, PlayCircleFilled } from '@ant-design/icons';
|
||||
import {
|
||||
FastBackwardOutlined,
|
||||
FastForwardOutlined,
|
||||
PauseCircleFilled,
|
||||
PlayCircleFilled
|
||||
} from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Slider, Tooltip } from 'antd';
|
||||
import { round } from 'lodash';
|
||||
import React, {
|
||||
@@ -8,8 +14,6 @@ import React, {
|
||||
useEffect,
|
||||
useImperativeHandle
|
||||
} from 'react';
|
||||
import CheckButtons from '../check-buttons';
|
||||
import IconFont from '../icon-font';
|
||||
import './index.less';
|
||||
|
||||
interface AudioPlayerProps {
|
||||
@@ -30,7 +34,14 @@ const speedOptions = [
|
||||
{ label: '4x', value: 4 }
|
||||
];
|
||||
|
||||
const speedConfig = {
|
||||
min: 0.5,
|
||||
max: 2,
|
||||
step: 0.25
|
||||
};
|
||||
|
||||
const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
const intl = useIntl();
|
||||
const { autoplay = false, speed: defaultSpeed = 1 } = props;
|
||||
const audioRef = React.useRef<HTMLAudioElement>(null);
|
||||
const [audioState, setAudioState] = React.useState<{
|
||||
@@ -95,7 +106,10 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
setPlayOn(!playOn);
|
||||
}, [playOn]);
|
||||
|
||||
const handleFormatVolume = (val: number) => {
|
||||
const handleFormatVolume = (val?: number) => {
|
||||
if (val === undefined) {
|
||||
return `${round(volume * 100)}%`;
|
||||
}
|
||||
return `${round(val * 100)}%`;
|
||||
};
|
||||
|
||||
@@ -132,6 +146,28 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleReduceSpeed = () => {
|
||||
setSpeed((pre) => {
|
||||
if (pre - speedConfig.step < speedConfig.min) {
|
||||
return speedConfig.min;
|
||||
}
|
||||
const next = pre - speedConfig.step;
|
||||
audioRef.current!.playbackRate = next;
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddSpeed = () => {
|
||||
setSpeed((pre) => {
|
||||
if (pre + speedConfig.step > speedConfig.max) {
|
||||
return speedConfig.max;
|
||||
}
|
||||
const next = pre + speedConfig.step;
|
||||
audioRef.current!.playbackRate = next;
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (audioRef.current) {
|
||||
initPlayerConfig();
|
||||
@@ -147,69 +183,115 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
return (
|
||||
<div className="player-wrap" style={{ width: props.width || '100%' }}>
|
||||
<div className="player-ui">
|
||||
<span className="play-btn">
|
||||
<Button
|
||||
size="middle"
|
||||
type="text"
|
||||
onClick={handlePlay}
|
||||
icon={
|
||||
!playOn ? (
|
||||
<PlayCircleFilled
|
||||
style={{ fontSize: '22px' }}
|
||||
></PlayCircleFilled>
|
||||
) : (
|
||||
<PauseCircleFilled
|
||||
style={{ fontSize: '22px' }}
|
||||
></PauseCircleFilled>
|
||||
)
|
||||
}
|
||||
></Button>
|
||||
</span>
|
||||
<div className="play-content">
|
||||
<span className="time current">
|
||||
{' '}
|
||||
{formatTime(audioState.currentTime)}
|
||||
</span>
|
||||
<div className="progress-bar">
|
||||
<span className="file-name">{props.name}</span>
|
||||
<div className="slider">
|
||||
<Slider
|
||||
tooltip={{ open: false }}
|
||||
min={0}
|
||||
max={audioState.duration}
|
||||
value={audioState.currentTime}
|
||||
onChange={handleCurrentChange}
|
||||
/>
|
||||
</div>
|
||||
<Tooltip
|
||||
overlayInnerStyle={{
|
||||
backgroundColor: 'var(--color-white-1)'
|
||||
}}
|
||||
arrow={false}
|
||||
title={
|
||||
<CheckButtons
|
||||
options={speedOptions}
|
||||
onChange={handleSeepdChange}
|
||||
size="small"
|
||||
></CheckButtons>
|
||||
}
|
||||
>
|
||||
<span style={{ cursor: 'pointer' }}>
|
||||
{speed ? `${speed}x` : '1x'}
|
||||
<span className="time current">
|
||||
{' '}
|
||||
{formatTime(audioState.currentTime)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
<div className="slider-inner">
|
||||
<Slider
|
||||
tooltip={{ open: false }}
|
||||
min={0}
|
||||
max={audioState.duration}
|
||||
value={audioState.currentTime}
|
||||
onChange={handleCurrentChange}
|
||||
/>
|
||||
</div>
|
||||
<span className="time">{formatTime(audioState.duration)}</span>
|
||||
</div>
|
||||
<div className="controls">
|
||||
{/* <Tooltip
|
||||
overlayInnerStyle={{
|
||||
backgroundColor: 'var(--color-white-1)'
|
||||
}}
|
||||
arrow={false}
|
||||
title={
|
||||
<CheckButtons
|
||||
options={speedOptions}
|
||||
onChange={handleSeepdChange}
|
||||
size="small"
|
||||
></CheckButtons>
|
||||
}
|
||||
>
|
||||
<span style={{ cursor: 'pointer' }}>
|
||||
{speed ? `${speed}x` : '1x'}
|
||||
</span>
|
||||
</Tooltip> */}
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'playground.audio.button.slow'
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="backward"
|
||||
disabled={
|
||||
speed === speedConfig.min || speed < speedConfig.min
|
||||
}
|
||||
onClick={handleReduceSpeed}
|
||||
>
|
||||
<FastBackwardOutlined className="font-size-20" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<span className="play-btn">
|
||||
<Button
|
||||
size="middle"
|
||||
type="text"
|
||||
onClick={handlePlay}
|
||||
disabled={!audioState?.duration}
|
||||
icon={
|
||||
!playOn ? (
|
||||
<PlayCircleFilled
|
||||
style={{ fontSize: '22px' }}
|
||||
></PlayCircleFilled>
|
||||
) : (
|
||||
<PauseCircleFilled
|
||||
style={{ fontSize: '22px' }}
|
||||
></PauseCircleFilled>
|
||||
)
|
||||
}
|
||||
></Button>
|
||||
</span>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'playground.audio.button.fast'
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="forward"
|
||||
disabled={
|
||||
speed === speedConfig.max || speed > speedConfig.max
|
||||
}
|
||||
onClick={handleAddSpeed}
|
||||
>
|
||||
<FastForwardOutlined className="font-size-20" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<span className="time">{formatTime(audioState.duration)}</span>
|
||||
</div>
|
||||
<span className="speaker">
|
||||
{/* <span className="speaker">
|
||||
<Button
|
||||
size="middle"
|
||||
type="text"
|
||||
icon={
|
||||
<IconFont
|
||||
type="icon-SpeakerHigh"
|
||||
style={{ fontSize: '22px' }}
|
||||
></IconFont>
|
||||
volume > 0 ? (
|
||||
<IconFont
|
||||
type="icon-SpeakerHigh"
|
||||
style={{ fontSize: '22px' }}
|
||||
></IconFont>
|
||||
) : (
|
||||
<IconFont
|
||||
type="icon-speaker-slash"
|
||||
style={{ fontSize: '22px' }}
|
||||
></IconFont>
|
||||
)
|
||||
}
|
||||
></Button>
|
||||
{speakerOn && (
|
||||
@@ -225,7 +307,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
onChange={handleVolumeChange}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</span> */}
|
||||
</div>
|
||||
<audio
|
||||
controls
|
||||
|
||||
@@ -37,7 +37,7 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
|
||||
autoBgColor
|
||||
} = props;
|
||||
|
||||
const [color, setColor] = React.useState<string>('');
|
||||
const [color, setColor] = React.useState({});
|
||||
const imgWrapper = React.useRef<HTMLSpanElement>(null);
|
||||
|
||||
const thumImgWrapStyle = React.useMemo(() => {
|
||||
@@ -59,10 +59,17 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
|
||||
return;
|
||||
}
|
||||
const color = palette?.Vibrant?.rgb;
|
||||
const rgba = color
|
||||
const mutedColor = palette?.Muted?.rgb;
|
||||
|
||||
const startColor = color
|
||||
? `rgba(${color[0]}, ${color[1]}, ${color[2]},0.7)`
|
||||
: '';
|
||||
setColor(rgba);
|
||||
const stopColor = mutedColor
|
||||
? `rgba(${mutedColor[0]}, ${mutedColor[1]}, ${mutedColor[2]},0.5)`
|
||||
: '';
|
||||
setColor({
|
||||
backgroundImage: `linear-gradient(135deg, ${startColor}, ${stopColor})`
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createFromIconfontCN } from '@ant-design/icons';
|
||||
import './iconfont/iconfont.js';
|
||||
// import './iconfont/iconfont.js';
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: ''
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_oyisgn7lqa.js'
|
||||
});
|
||||
|
||||
export default IconFont;
|
||||
|
||||
@@ -18,13 +18,14 @@ interface AudioPlayerProps {
|
||||
const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
const { autoplay, audioUrl, speed = 1, ...rest } = props;
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
const { createWavesurfer, play, pause, destroyWavesurfer } = useWavesurfer({
|
||||
container,
|
||||
autoplay: autoplay,
|
||||
url: audioUrl,
|
||||
audioRate: speed,
|
||||
...rest
|
||||
});
|
||||
const { createWavesurfer, play, pause, destroyWavesurfer, wavesurfer } =
|
||||
useWavesurfer({
|
||||
container,
|
||||
autoplay: autoplay,
|
||||
url: audioUrl,
|
||||
audioRate: speed,
|
||||
...rest
|
||||
});
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
|
||||
@@ -61,6 +61,7 @@ const useWavesurfer = (options: Options) => {
|
||||
createWavesurfer,
|
||||
play,
|
||||
pause,
|
||||
wavesurfer,
|
||||
destroyWavesurfer
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user