Files
kefu_cloud/web/src/widgets/visitor/RatingOverlay.tsx
T
yml2213 6304f97bc3 refactor(web): 拆分 Dashboard/Settings/VisitorChat 超大组件为子模块
- Dashboard 2591→1944 行,抽出 dashboard/ 纯函数、客户资料字段与 5 个状态自持弹窗
- Settings 1602→478 行,抽出 settings/ 16 个分区模块(设置卡片、弹窗与工具函数)
- VisitorChat 1343→1045 行,抽出 visitor/ 头部、消息区、输入区与评价浮层
- 均为纯代码搬移:行为、路由入口与导出不变;tsc -b 与 vite build 通过
2026-09-05 14:40:14 +08:00

113 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { StarFilled } from '@ant-design/icons'
/** 评价浮层:星级 + 快捷评语 + 提交/跳过 */
export function RatingOverlay({
score,
hoverStar,
text,
submitting,
sending,
presets,
onScore,
onHover,
onText,
onSubmit,
onSkip,
onSkipAndRestart,
onTogglePreset,
}: {
score: number
hoverStar: number
text: string
submitting: boolean
sending: boolean
presets: string[]
onScore: (star: number) => void
onHover: (star: number) => void
onText: (next: string) => void
onSubmit: () => void
onSkip: () => void
onSkipAndRestart: () => void
/** 快捷评语:重复点击同一标签取消选中 */
onTogglePreset: (preset: string) => void
}) {
return (
<div className="absolute inset-0 bg-black/40 flex items-center justify-center z-10 p-4">
<div className="bg-white rounded-xl p-5 w-full max-w-xs text-center shadow-lg">
<div className="text-lg font-semibold text-neutral-800 mb-1">本次服务如何?</div>
<div className="text-sm text-neutral-400 mb-3">先点亮星级,可补充感受后再提交</div>
<div className="flex justify-center gap-1.5 mb-1">
{[1, 2, 3, 4, 5].map(star => {
const active = star <= (hoverStar || score)
return (
<StarFilled
key={star}
className="text-2xl cursor-pointer transition-colors"
style={{ color: active ? '#facc15' : '#e2e8f0' }}
onMouseEnter={() => onHover(star)}
onMouseLeave={() => onHover(0)}
onClick={() => onScore(star)}
/>
)
})}
</div>
<div className="text-xs text-neutral-400 mb-3 h-4">
{score > 0 ? `${score} 星` : '请选择 1~5 星'}
</div>
<div className="flex flex-wrap gap-1.5 justify-center mb-3">
{presets.map(preset => {
const selected = text === preset
return (
<button
key={preset}
type="button"
onClick={() => onTogglePreset(preset)}
className={`px-2 py-1 rounded-full text-[11px] border cursor-pointer transition-colors ${
selected
? 'bg-[#2563eb] border-[#2563eb] text-white'
: 'bg-neutral-50 border-neutral-200 text-neutral-600 hover:border-blue-300 hover:text-blue-600'
}`}
>
{preset}
</button>
)
})}
</div>
<textarea
className="w-full rounded-lg border border-neutral-200 p-2 text-sm outline-none focus:border-blue-400 mb-3 resize-none text-left"
rows={3}
maxLength={500}
value={text}
onChange={e => onText(e.target.value)}
placeholder="可选:点击上方标签快速填入,或自行输入"
/>
<button
type="button"
disabled={submitting || score < 1}
onClick={onSubmit}
className="w-full h-10 rounded-xl border-0 bg-[#2563eb] hover:bg-[#1d4ed8] disabled:opacity-40 cursor-pointer text-white text-sm font-medium mb-2"
>
{submitting ? '提交中...' : '提交评价'}
</button>
<div className="flex flex-col gap-1.5">
<button
type="button"
onClick={onSkip}
className="text-sm text-neutral-400 hover:text-neutral-600 border-0 bg-transparent cursor-pointer"
>
跳过
</button>
<button
type="button"
disabled={sending || submitting}
onClick={onSkipAndRestart}
className="text-sm text-[#2563eb] hover:text-[#1d4ed8] border-0 bg-transparent cursor-pointer disabled:opacity-50"
>
跳过并重新咨询
</button>
</div>
</div>
</div>
)
}