158 lines
4.9 KiB
TypeScript
158 lines
4.9 KiB
TypeScript
import React from 'react'
|
||
import { motion } from 'framer-motion'
|
||
import { Delete, RotateCcw, Check } from 'lucide-react'
|
||
|
||
interface NumberKeyboardProps {
|
||
onNumberClick: (number: string) => void
|
||
onDeleteClick: () => void
|
||
onClearClick: () => void
|
||
onConfirmClick: () => void
|
||
disabled?: boolean
|
||
canConfirm?: boolean
|
||
studentIdLength?: number
|
||
}
|
||
|
||
const NumberKeyboard: React.FC<NumberKeyboardProps> = ({
|
||
onNumberClick,
|
||
onDeleteClick,
|
||
onClearClick,
|
||
onConfirmClick,
|
||
disabled = false,
|
||
canConfirm = false,
|
||
studentIdLength = 0
|
||
}) => {
|
||
// 数字键盘布局
|
||
const keyboardLayout = [
|
||
['1', '2', '3'],
|
||
['4', '5', '6'],
|
||
['7', '8', '9'],
|
||
['clear', '0', 'delete']
|
||
]
|
||
|
||
// 按钮点击动画变体
|
||
const buttonVariants = {
|
||
initial: { scale: 1 },
|
||
tap: { scale: 0.95 },
|
||
hover: { scale: 1.05 }
|
||
}
|
||
|
||
// 渲染按钮内容
|
||
const renderButtonContent = (key: string) => {
|
||
switch (key) {
|
||
case 'delete':
|
||
return <Delete className="w-8 h-8" />
|
||
case 'clear':
|
||
return <RotateCcw className="w-8 h-8" />
|
||
default:
|
||
return <span className="text-3xl font-bold">{key}</span>
|
||
}
|
||
}
|
||
|
||
// 检查数字键是否应该被禁用
|
||
const isNumberKeyDisabled = (key: string) => {
|
||
// 如果是数字键且学号已达到12位,则禁用
|
||
return /^[0-9]$/.test(key) && studentIdLength >= 12
|
||
}
|
||
|
||
// 获取按钮样式
|
||
const getButtonStyle = (key: string) => {
|
||
const baseStyle = "w-[140px] h-[140px] rounded-2xl font-bold transition-all duration-200 flex items-center justify-center shadow-lg"
|
||
|
||
if (disabled) {
|
||
return `${baseStyle} bg-gray-500/20 text-gray-400 cursor-not-allowed`
|
||
}
|
||
|
||
// 检查数字键是否被禁用
|
||
if (isNumberKeyDisabled(key)) {
|
||
return `${baseStyle} bg-gray-500/20 text-gray-400 cursor-not-allowed`
|
||
}
|
||
|
||
switch (key) {
|
||
case 'delete':
|
||
return `${baseStyle} bg-red-500/20 hover:bg-red-500/30 text-red-400 hover:text-red-300 border border-red-500/30`
|
||
case 'clear':
|
||
return `${baseStyle} bg-yellow-500/20 hover:bg-yellow-500/30 text-yellow-400 hover:text-yellow-300 border border-yellow-500/30`
|
||
default:
|
||
return `${baseStyle} bg-blue-500/20 hover:bg-blue-500/30 text-white border border-blue-500/30 hover:border-blue-400`
|
||
}
|
||
}
|
||
|
||
// 处理按钮点击
|
||
const handleButtonClick = (key: string) => {
|
||
if (disabled) return
|
||
|
||
// 如果是数字键且被禁用,则不处理点击
|
||
if (isNumberKeyDisabled(key)) return
|
||
|
||
switch (key) {
|
||
case 'delete':
|
||
onDeleteClick()
|
||
break
|
||
case 'clear':
|
||
onClearClick()
|
||
break
|
||
default:
|
||
onNumberClick(key)
|
||
break
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="flex flex-col items-center space-y-4">
|
||
{/* 数字键盘网格 */}
|
||
<div className="grid grid-cols-3 gap-[10px]">
|
||
{keyboardLayout.map((row, rowIndex) =>
|
||
row.map((key, keyIndex) => (
|
||
<motion.button
|
||
key={`${rowIndex}-${keyIndex}`}
|
||
variants={buttonVariants}
|
||
initial="initial"
|
||
whileHover={disabled || isNumberKeyDisabled(key) ? "initial" : "hover"}
|
||
whileTap={disabled || isNumberKeyDisabled(key) ? "initial" : "tap"}
|
||
onClick={() => handleButtonClick(key)}
|
||
className={getButtonStyle(key)}
|
||
disabled={disabled || isNumberKeyDisabled(key)}
|
||
>
|
||
{renderButtonContent(key)}
|
||
</motion.button>
|
||
))
|
||
)}
|
||
</div>
|
||
|
||
{/* 确认按钮 */}
|
||
<motion.button
|
||
variants={buttonVariants}
|
||
initial="initial"
|
||
whileHover={(!disabled && canConfirm) ? "hover" : "initial"}
|
||
whileTap={(!disabled && canConfirm) ? "tap" : "initial"}
|
||
onClick={onConfirmClick}
|
||
disabled={disabled || !canConfirm}
|
||
className={`w-full max-w-[450px] h-[80px] rounded-2xl font-bold text-xl transition-all duration-200 flex items-center justify-center space-x-3 shadow-lg ${
|
||
disabled || !canConfirm
|
||
? 'bg-gray-500/20 text-gray-400 cursor-not-allowed border border-gray-500/30'
|
||
: 'bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600 hover:to-emerald-600 text-white shadow-green-500/25 hover:shadow-green-500/40'
|
||
}`}
|
||
>
|
||
<Check className="w-6 h-6" />
|
||
<span>开始抽奖</span>
|
||
</motion.button>
|
||
|
||
{/* 键盘说明 */}
|
||
<div className="text-center text-white/50 text-sm space-y-1">
|
||
<p>点击数字输入学号</p>
|
||
<div className="flex items-center justify-center space-x-4">
|
||
<div className="flex items-center space-x-1">
|
||
<RotateCcw className="w-4 h-4" />
|
||
<span>清空</span>
|
||
</div>
|
||
<div className="flex items-center space-x-1">
|
||
<Delete className="w-4 h-4" />
|
||
<span>删除</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default React.memo(NumberKeyboard) |