增加需求模糊提醒、圖表顯示

This commit is contained in:
2025-07-21 23:46:27 +08:00
parent 7ec4a39a2f
commit 916e97b35e
3 changed files with 177 additions and 43 deletions

View File

@@ -11,7 +11,9 @@ import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
import { Checkbox } from "@/components/ui/checkbox"
import { Sparkles, ArrowLeft, Send, BarChart3, Eye, EyeOff, Shield, Info, Mail, ImageIcon } from "lucide-react"
import { Alert, AlertDescription } from "@/components/ui/alert"
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"
import { Sparkles, ArrowLeft, Send, BarChart3, Eye, EyeOff, Shield, Info, Mail, ImageIcon, Lightbulb } from "lucide-react"
import { useToast } from "@/hooks/use-toast"
import { soundManager } from "@/lib/sound-effects"
import HeaderMusicControl from "@/components/header-music-control"
@@ -20,6 +22,7 @@ import ContentModerationFeedback from "@/components/content-moderation-feedback"
import ImageUpload from "@/components/image-upload"
import type { ImageFile } from "@/lib/image-utils"
import { WishService } from "@/lib/supabase-service"
import { categorizeWish, type Wish } from "@/lib/categorization"
export default function SubmitPage() {
const [formData, setFormData] = useState({
@@ -36,6 +39,9 @@ export default function SubmitPage() {
const router = useRouter()
const [moderationResult, setModerationResult] = useState<ModerationResult | null>(null)
const [showModerationFeedback, setShowModerationFeedback] = useState(false)
const [currentCategory, setCurrentCategory] = useState<string | null>(null)
const [showCategoryHint, setShowCategoryHint] = useState(false)
const [showConfirmDialog, setShowConfirmDialog] = useState(false)
// 初始化音效系統
useEffect(() => {
@@ -50,24 +56,43 @@ export default function SubmitPage() {
initSound()
}, [])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
// 實時檢測分類並顯示提示
useEffect(() => {
// 只有當用戶輸入了一定內容才進行分類檢測
const hasMinimumContent =
formData.title.trim().length > 3 ||
formData.currentPain.trim().length > 10
// 先進行內容審核
const moderation = moderateWishForm(formData)
setModerationResult(moderation)
if (!moderation.isAppropriate) {
setShowModerationFeedback(true)
await soundManager.play("click") // 播放提示音效
toast({
title: "內容需要修改",
description: "請根據建議修改內容後再次提交",
variant: "destructive",
})
return
if (hasMinimumContent) {
// 創建模擬的 Wish 物件進行分類
const mockWish: Wish = {
id: 0,
title: formData.title,
currentPain: formData.currentPain,
expectedSolution: formData.expectedSolution,
expectedEffect: formData.expectedEffect,
createdAt: new Date().toISOString()
}
const category = categorizeWish(mockWish)
setCurrentCategory(category.name)
// 如果分類為"其他問題"且內容不夠詳細,顯示提示(只對公開分享)
const isOtherCategory = category.name === "其他問題"
const contentLength =
formData.title.trim().length +
formData.currentPain.trim().length +
formData.expectedSolution.trim().length
setShowCategoryHint(isOtherCategory && contentLength < 50 && formData.isPublic)
} else {
setCurrentCategory(null)
setShowCategoryHint(false)
}
}, [formData.title, formData.currentPain, formData.expectedSolution, formData.expectedEffect, formData.isPublic])
// 实际的提交逻辑
const performSubmit = async () => {
setIsSubmitting(true)
setShowModerationFeedback(false)
@@ -95,22 +120,8 @@ export default function SubmitPage() {
? "正在為你準備專業的回饋,其他人也能看到你的分享..."
: "正在為你準備專業的回饋,你的分享將保持私密...",
})
} catch (error) {
console.error("提交困擾失敗:", error)
// 播放錯誤音效
await soundManager.play("click")
toast({
title: "提交失敗",
description: "請稍後再試或檢查網路連接",
variant: "destructive",
})
setIsSubmitting(false)
return
}
// 重置表单
setFormData({
title: "",
currentPain: "",
@@ -127,6 +138,56 @@ export default function SubmitPage() {
setTimeout(() => {
router.push("/thank-you")
}, 1000)
} catch (error) {
console.error("提交困擾失敗:", error)
// 播放錯誤音效
await soundManager.play("click")
toast({
title: "提交失敗",
description: "請稍後再試或檢查網路連接",
variant: "destructive",
})
setIsSubmitting(false)
}
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
// 先進行內容審核
const moderation = moderateWishForm(formData)
setModerationResult(moderation)
if (!moderation.isAppropriate) {
setShowModerationFeedback(true)
await soundManager.play("click") // 播放提示音效
toast({
title: "內容需要修改",
description: "請根據建議修改內容後再次提交",
variant: "destructive",
})
return
}
// 检查是否为"其他问题"分类且内容较少,需要确认
if (currentCategory === "其他問題" && formData.isPublic) {
const contentLength =
formData.title.trim().length +
formData.currentPain.trim().length +
formData.expectedSolution.trim().length
if (contentLength < 50) {
setShowConfirmDialog(true)
return
}
}
// 直接提交
await performSubmit()
}
const handleChange = (field: string, value: string | boolean) => {
@@ -463,6 +524,26 @@ export default function SubmitPage() {
/>
)}
{/* 分類提示 - 當被歸類為其他問題時的友好提示 */}
{showCategoryHint && currentCategory === "其他問題" && (
<Alert className="bg-yellow-900/50 border-yellow-700/50 text-yellow-200 animate-in slide-in-from-top-2 duration-300">
<Lightbulb className="w-4 h-4" />
<AlertDescription className="flex items-start gap-2">
<span className="flex-1">
<br />
<br /> 使
<br />
<br />
<br />
<small className="text-yellow-300 opacity-90 mt-2 block">
</small>
</span>
</AlertDescription>
</Alert>
)}
{/* 隱私設定區塊 */}
<div className="space-y-4 p-4 md:p-5 bg-gradient-to-r from-slate-700/30 to-slate-800/30 rounded-lg border border-slate-600/50">
<div className="flex items-center gap-3">
@@ -565,6 +646,45 @@ export default function SubmitPage() {
</form>
</CardContent>
</Card>
{/* 确认对话框 */}
<AlertDialog open={showConfirmDialog} onOpenChange={setShowConfirmDialog}>
<AlertDialogContent className="bg-slate-800 border-slate-600 text-white max-w-md">
<AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2 text-yellow-400">
<Lightbulb className="w-5 h-5" />
</AlertDialogTitle>
<AlertDialogDescription className="text-slate-300 leading-relaxed">
<br /><br />
<br />
使<br />
<br />
<br /><br />
<span className="text-yellow-300"></span>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter className="gap-2">
<AlertDialogCancel
className="bg-slate-700 border-slate-600 text-slate-300 hover:bg-slate-600"
onClick={() => setShowConfirmDialog(false)}
>
</AlertDialogCancel>
<AlertDialogAction
className="bg-gradient-to-r from-cyan-500 to-blue-600 hover:from-cyan-600 hover:to-blue-700"
onClick={async () => {
setShowConfirmDialog(false)
await performSubmit()
}}
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</main>

View File

@@ -41,7 +41,8 @@ export default function RadarChart({ data }: RadarChartProps) {
const centerX = size / 2
const centerY = size / 2
const radius = Math.min(centerX, centerY) - 60
// 调整边距到合理范围,既保证文字显示又不让图表太小
const radius = Math.min(centerX, centerY) - 80
// 清除畫布
ctx.clearRect(0, 0, size, size)
@@ -124,31 +125,44 @@ export default function RadarChart({ data }: RadarChartProps) {
ctx.stroke()
})
// 繪製標籤 - 響應式字體大小
// 繪製標籤 - 调整为合适的文字大小
ctx.fillStyle = "#E2E8F0"
const fontSize = Math.max(10, Math.min(14, size / 30)) // 響應式字體大小
const fontSize = Math.max(10, Math.min(14, size / 32)) // 适当增大字体提高可读性
ctx.font = `${fontSize}px sans-serif`
ctx.textAlign = "center"
activeData.forEach((item, index) => {
const angle = index * angleStep - Math.PI / 2
const labelRadius = radius + 30
// 增加标签距离确保文字不被遮住
const labelRadius = radius + 40
const x = centerX + Math.cos(angle) * labelRadius
const y = centerY + Math.sin(angle) * labelRadius
// 調整文字對齊
if (Math.cos(angle) < -0.1) {
// 更精確的文字對齊
const cosAngle = Math.cos(angle)
const sinAngle = Math.sin(angle)
if (cosAngle < -0.3) {
ctx.textAlign = "right"
} else if (Math.cos(angle) > 0.1) {
} else if (cosAngle > 0.3) {
ctx.textAlign = "left"
} else {
ctx.textAlign = "center"
}
// 垂直對齊調整
let textY = y
if (sinAngle < -0.3) {
// 上方文字,向下偏移一點
textY = y + fontSize / 3
} else if (sinAngle > 0.3) {
// 下方文字,向上偏移一點
textY = y - fontSize / 3
}
// 繪製分類名稱
ctx.fillText(item.name, x, y)
// 繪製數量
ctx.fillText(`${item.count}`, x, y + fontSize + 2)
ctx.fillText(item.name, x, textY)
// 繪製數量,減少行間距
ctx.fillText(`${item.count}`, x, textY + fontSize + 3)
})
}, [data])
@@ -168,7 +182,7 @@ export default function RadarChart({ data }: RadarChartProps) {
}, [])
return (
<div className="w-full h-full flex items-center justify-center">
<div className="w-full h-full flex items-center justify-center p-4">
<canvas
ref={canvasRef}
className="max-w-full max-h-full"

View File

@@ -56,7 +56,7 @@ export const categories = [
icon: "📊",
},
{
name: "找不到資料",
name: "查找困難",
description: "文件、SOP、規則等資訊難查找",
keywords: [
"找不到",