重构决策检查模块:提取冷静期检查和理性检查清单到独立模块,优化显示逻辑和代码可维护性

This commit is contained in:
zihanjian
2025-06-08 11:36:47 +08:00
parent 0962de5b5d
commit 54006465f0
3 changed files with 268 additions and 34 deletions

View 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;

View File

@@ -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
View 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 "可以立即决策";
}