mirror of
https://github.com/Zippland/worth-buying.git
synced 2026-01-19 01:21:11 +08:00
重构决策检查模块:提取冷静期检查和理性检查清单到独立模块,优化显示逻辑和代码可维护性
This commit is contained in:
133
components/DecisionChecks.tsx
Normal file
133
components/DecisionChecks.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import React from 'react';
|
||||
import { AlertTriangle, Clock, Brain } from 'lucide-react';
|
||||
import {
|
||||
getCooldownChecks,
|
||||
getRationalChecklist,
|
||||
getScoreBasedReminders,
|
||||
getRecommendedWaitTime,
|
||||
CooldownCheckItem,
|
||||
RationalCheckItem
|
||||
} from '../utils/decisionChecks';
|
||||
|
||||
interface DecisionChecksProps {
|
||||
productPrice: string;
|
||||
adjustments: { discount: boolean };
|
||||
score: number;
|
||||
}
|
||||
|
||||
const DecisionChecks: React.FC<DecisionChecksProps> = ({
|
||||
productPrice,
|
||||
adjustments,
|
||||
score
|
||||
}) => {
|
||||
const cooldownChecks = getCooldownChecks(productPrice, adjustments, score);
|
||||
const rationalChecklist = getRationalChecklist();
|
||||
const scoreReminders = getScoreBasedReminders(score);
|
||||
const waitTime = getRecommendedWaitTime(parseInt(productPrice) || 0, score);
|
||||
|
||||
const getCheckIcon = (type: CooldownCheckItem['type']) => {
|
||||
switch (type) {
|
||||
case 'error': return '🔴';
|
||||
case 'warning': return '⚠️';
|
||||
case 'info': return 'ℹ️';
|
||||
default: return '•';
|
||||
}
|
||||
};
|
||||
|
||||
const getCheckBgColor = (type: CooldownCheckItem['type']) => {
|
||||
switch (type) {
|
||||
case 'error': return 'bg-red-50 border-red-200';
|
||||
case 'warning': return 'bg-yellow-50 border-yellow-200';
|
||||
case 'info': return 'bg-blue-50 border-blue-200';
|
||||
default: return 'bg-gray-50 border-gray-200';
|
||||
}
|
||||
};
|
||||
|
||||
const getCheckTextColor = (type: CooldownCheckItem['type']) => {
|
||||
switch (type) {
|
||||
case 'error': return 'text-red-800';
|
||||
case 'warning': return 'text-yellow-800';
|
||||
case 'info': return 'text-blue-800';
|
||||
default: return 'text-gray-800';
|
||||
}
|
||||
};
|
||||
|
||||
const getCategoryIcon = (category: RationalCheckItem['category']) => {
|
||||
switch (category) {
|
||||
case 'financial': return '💰';
|
||||
case 'necessity': return '🎯';
|
||||
case 'social': return '👥';
|
||||
case 'risk': return '⚡';
|
||||
default: return '❓';
|
||||
}
|
||||
};
|
||||
|
||||
const shouldShowCooldownChecks = productPrice && (cooldownChecks.length > 0 || scoreReminders.length > 0);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* 冷静期检查 */}
|
||||
{shouldShowCooldownChecks && (
|
||||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
|
||||
<h4 className="font-semibold text-yellow-800 mb-3 flex items-center gap-2">
|
||||
<AlertTriangle size={18} />
|
||||
冷静期检查
|
||||
</h4>
|
||||
|
||||
{(cooldownChecks.length > 0 || parseInt(productPrice) > 0) && (
|
||||
<div className="mb-3 p-2 bg-yellow-100 rounded text-sm text-yellow-700">
|
||||
<Clock size={16} className="inline mr-1" />
|
||||
<strong>{waitTime}</strong>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{cooldownChecks.length > 0 && (
|
||||
<div className="space-y-2 mb-3">
|
||||
{cooldownChecks.map((check, index) => (
|
||||
<div key={index} className={`p-2 border rounded text-sm ${getCheckBgColor(check.type)}`}>
|
||||
<div className={`flex items-center gap-2 ${getCheckTextColor(check.type)}`}>
|
||||
<span>{getCheckIcon(check.type)}</span>
|
||||
<span>{check.message}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{scoreReminders.length > 0 && (
|
||||
<div className="pt-2 border-t border-yellow-300">
|
||||
{scoreReminders.map((reminder, index) => (
|
||||
<div key={index} className="text-sm text-yellow-700 flex items-center gap-2">
|
||||
<Brain size={14} />
|
||||
{reminder}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 理性检查清单 */}
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
||||
<h4 className="font-semibold text-blue-800 mb-3 flex items-center gap-2">
|
||||
<Brain size={18} />
|
||||
理性检查清单
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
{rationalChecklist.map((item, index) => (
|
||||
<div key={index} className="flex items-start gap-2 text-sm text-blue-700">
|
||||
<span className="mt-0.5">{getCategoryIcon(item.category)}</span>
|
||||
<span>□ {item.question}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-3 pt-2 border-t border-blue-300 text-xs text-blue-600">
|
||||
💡 建议逐一思考上述问题,每个问题花费1-2分钟
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DecisionChecks;
|
||||
44
page.tsx
44
page.tsx
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { Calculator, ShoppingCart, TrendingUp, Shield, Clock, Heart, AlertTriangle, Settings } from 'lucide-react';
|
||||
import DecisionChecks from './components/DecisionChecks';
|
||||
|
||||
const PurchaseDecisionCalculator = () => {
|
||||
const [productName, setProductName] = useState('');
|
||||
@@ -224,9 +225,9 @@ const PurchaseDecisionCalculator = () => {
|
||||
],
|
||||
D2: [
|
||||
{ value: 0, label: "无收益" },
|
||||
{ value: 1, label: "小幅改善" },
|
||||
{ value: 2, label: "改善健康" },
|
||||
{ value: 3, label: "提升技能" },
|
||||
{ value: 1, label: "稍有益处" },
|
||||
{ value: 2, label: "提升技能" },
|
||||
{ value: 3, label: "改善健康" },
|
||||
{ value: 4, label: "节省成本" },
|
||||
{ value: 5, label: "带来收入" }
|
||||
],
|
||||
@@ -640,37 +641,12 @@ const PurchaseDecisionCalculator = () => {
|
||||
{/* 结果面板 */}
|
||||
<div className="space-y-6">
|
||||
|
||||
{/* 冷静期检查 */}
|
||||
{productPrice && (
|
||||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
|
||||
<h4 className="font-semibold text-yellow-800 mb-2 flex items-center gap-2">
|
||||
<AlertTriangle size={18} />
|
||||
冷静期检查
|
||||
</h4>
|
||||
<div className="text-sm text-yellow-700 space-y-1">
|
||||
{parseInt(productPrice) > 5000 && (
|
||||
<div>• 大额支出 → 建议等待24小时</div>
|
||||
)}
|
||||
{adjustments.discount && (
|
||||
<div>• 限时促销 → 验证真实性和退换政策</div>
|
||||
)}
|
||||
{calculateScore >= 75 && calculateScore < 85 && (
|
||||
<div>• 高分但未到闭眼入手 → 找朋友独立评分</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 理性检查清单 */}
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
||||
<h4 className="font-semibold text-blue-800 mb-2">理性检查清单</h4>
|
||||
<div className="text-sm text-blue-700 space-y-2">
|
||||
<div>□ 不买的话现有的能坚持多久?</div>
|
||||
<div>□ 这笔钱理财一年后能有多少?</div>
|
||||
<div>□ 家人知道价格会怎么说?</div>
|
||||
<div>□ 买了还有钱应对意外吗?</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 决策检查组件 */}
|
||||
<DecisionChecks
|
||||
productPrice={productPrice}
|
||||
adjustments={adjustments}
|
||||
score={calculateScore}
|
||||
/>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className="space-y-3">
|
||||
|
||||
125
utils/decisionChecks.ts
Normal file
125
utils/decisionChecks.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
// 冷静期检查项目类型
|
||||
export interface CooldownCheckItem {
|
||||
condition: boolean;
|
||||
message: string;
|
||||
type: 'warning' | 'info' | 'error';
|
||||
}
|
||||
|
||||
// 理性检查清单项目类型
|
||||
export interface RationalCheckItem {
|
||||
question: string;
|
||||
category: 'financial' | 'necessity' | 'social' | 'risk';
|
||||
}
|
||||
|
||||
// 冷静期检查逻辑
|
||||
export function getCooldownChecks(
|
||||
productPrice: string,
|
||||
adjustments: { discount: boolean },
|
||||
score: number
|
||||
): CooldownCheckItem[] {
|
||||
const checks: CooldownCheckItem[] = [];
|
||||
|
||||
const price = parseInt(productPrice) || 0;
|
||||
|
||||
// 如果没有输入价格,直接返回空数组
|
||||
if (!productPrice || price <= 0) {
|
||||
return checks;
|
||||
}
|
||||
|
||||
// 大额支出检查
|
||||
if (price > 5000) {
|
||||
checks.push({
|
||||
condition: true,
|
||||
message: "大额支出 → 建议等待24小时",
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
|
||||
// 限时促销检查
|
||||
if (adjustments.discount) {
|
||||
checks.push({
|
||||
condition: true,
|
||||
message: "限时促销 → 验证真实性和退换政策",
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
|
||||
// 高分但未达到闭眼入手检查
|
||||
if (score >= 75 && score < 85) {
|
||||
checks.push({
|
||||
condition: true,
|
||||
message: "高分但未到闭眼入手 → 找朋友独立评分",
|
||||
type: 'info'
|
||||
});
|
||||
}
|
||||
|
||||
// 超高价格警告
|
||||
if (price > 20000) {
|
||||
checks.push({
|
||||
condition: true,
|
||||
message: "超高价格 → 建议等待一周并咨询专业人士",
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
return checks;
|
||||
}
|
||||
|
||||
// 理性检查清单
|
||||
export function getRationalChecklist(): RationalCheckItem[] {
|
||||
return [
|
||||
{
|
||||
question: "不买的话现有的能坚持多久?",
|
||||
category: 'necessity'
|
||||
},
|
||||
{
|
||||
question: "这笔钱理财一年后能有多少?",
|
||||
category: 'financial'
|
||||
},
|
||||
{
|
||||
question: "家人知道价格会怎么说?",
|
||||
category: 'social'
|
||||
},
|
||||
{
|
||||
question: "买了还有钱应对意外吗?",
|
||||
category: 'risk'
|
||||
},
|
||||
{
|
||||
question: "一个月后还会想要这个产品吗?",
|
||||
category: 'necessity'
|
||||
},
|
||||
{
|
||||
question: "有没有更便宜的替代方案?",
|
||||
category: 'financial'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// 根据分数获取额外的理性提醒
|
||||
export function getScoreBasedReminders(score: number): string[] {
|
||||
const reminders: string[] = [];
|
||||
|
||||
if (score < 50) {
|
||||
reminders.push("分数较低,建议重新考虑是否真的需要");
|
||||
}
|
||||
|
||||
if (score >= 50 && score < 65) {
|
||||
reminders.push("分数中等,建议等待更好的时机或价格");
|
||||
}
|
||||
|
||||
if (score >= 85) {
|
||||
reminders.push("分数很高,但仍建议确认预算充足");
|
||||
}
|
||||
|
||||
return reminders;
|
||||
}
|
||||
|
||||
// 获取冷静期建议的等待时间
|
||||
export function getRecommendedWaitTime(price: number, score: number): string {
|
||||
if (price > 50000) return "建议等待2周";
|
||||
if (price > 20000) return "建议等待1周";
|
||||
if (price > 10000) return "建议等待3天";
|
||||
if (price > 5000) return "建议等待24小时";
|
||||
if (score < 65) return "建议等待1天冷静思考";
|
||||
return "可以立即决策";
|
||||
}
|
||||
Reference in New Issue
Block a user