feat: 更新 ESLint 和 Prettier 配置,增强代码质量管理
- 在 .gitignore 中添加 public/audio/* 以忽略音频文件。 - 更新 bun.lock,添加 eslint-config-prettier 和 eslint-plugin-prettier 依赖,确保 ESLint 与 Prettier 的兼容性。 - 修改 eslint.config.ts,整合 Prettier 规则,优化代码风格一致性。 - 在 package.json 中添加 lint:fix 和 format 脚本,简化代码格式化和修复流程。 - 更新 tailwind.config.ts 和其他组件文件,统一代码风格,提升可读性。
This commit is contained in:
1 parent
5d00adc1a5
commit
76c67b492e
216 files changed
+12454
-13976
No files matched your search
@@ -1,17 +1,17 @@
|
||||
import { useState, useEffect, useMemo, useCallback, useRef } from "react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { cn, getStaticImage } from "@/lib/utils";
|
||||
import { Image, DragFill } from "@/components";
|
||||
import type { DragFillResult } from "@/components";
|
||||
import { ErrorAlert, SuccessAlert } from "@/components/layout";
|
||||
import { useState, useEffect, useMemo, useCallback, useRef } from 'react'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { cn, getStaticImage } from '@/lib/utils'
|
||||
import { Image, DragFill } from '@/components'
|
||||
import type { DragFillResult } from '@/components'
|
||||
import { ErrorAlert, SuccessAlert } from '@/components/layout'
|
||||
|
||||
export interface TowelSalesDialogProps {
|
||||
/** 是否可见 */
|
||||
visible?: boolean;
|
||||
visible?: boolean
|
||||
/** 关闭回调 */
|
||||
onClose?: () => void;
|
||||
onClose?: () => void
|
||||
/** 完成回调 */
|
||||
onComplete?: () => void;
|
||||
onComplete?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,151 +21,143 @@ export interface TowelSalesDialogProps {
|
||||
* - 拖拽话术到毛巾上,点亮对应的毛巾
|
||||
* - 3个都点亮后完成
|
||||
*/
|
||||
const TowelSalesDialog = ({
|
||||
visible = true,
|
||||
onClose,
|
||||
onComplete,
|
||||
}: TowelSalesDialogProps) => {
|
||||
const maskSrc = getStaticImage("components/蒙版遮罩");
|
||||
const backSrc = getStaticImage("components/关闭");
|
||||
const TowelSalesDialog = ({ visible = true, onClose, onComplete }: TowelSalesDialogProps) => {
|
||||
const maskSrc = getStaticImage('components/蒙版遮罩')
|
||||
const backSrc = getStaticImage('components/关闭')
|
||||
|
||||
// 毛巾图片资源(未点亮)
|
||||
const towel1 = getStaticImage("huangyanpei/reflection/scene4/12_002");
|
||||
const towel2 = getStaticImage("huangyanpei/reflection/scene4/12_003");
|
||||
const towel3 = getStaticImage("huangyanpei/reflection/scene4/12_004");
|
||||
const towel1 = getStaticImage('huangyanpei/reflection/scene4/12_002')
|
||||
const towel2 = getStaticImage('huangyanpei/reflection/scene4/12_003')
|
||||
const towel3 = getStaticImage('huangyanpei/reflection/scene4/12_004')
|
||||
|
||||
// 毛巾图片资源(点亮)
|
||||
const towel1Lit = getStaticImage("huangyanpei/reflection/scene4/12_002_02");
|
||||
const towel2Lit = getStaticImage("huangyanpei/reflection/scene4/12_003_02");
|
||||
const towel3Lit = getStaticImage("huangyanpei/reflection/scene4/12_004_03");
|
||||
const towel1Lit = getStaticImage('huangyanpei/reflection/scene4/12_002_02')
|
||||
const towel2Lit = getStaticImage('huangyanpei/reflection/scene4/12_003_02')
|
||||
const towel3Lit = getStaticImage('huangyanpei/reflection/scene4/12_004_03')
|
||||
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [value, setValue] = useState<DragFillResult[]>([]);
|
||||
const [errorVisible, setErrorVisible] = useState(false);
|
||||
const [successVisible, setSuccessVisible] = useState(false);
|
||||
const completedRef = useRef(false);
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const [value, setValue] = useState<DragFillResult[]>([])
|
||||
const [errorVisible, setErrorVisible] = useState(false)
|
||||
const [successVisible, setSuccessVisible] = useState(false)
|
||||
const completedRef = useRef(false)
|
||||
|
||||
// SSR 兼容
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
// 重置游戏状态
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setValue([]);
|
||||
completedRef.current = false;
|
||||
setErrorVisible(false);
|
||||
setSuccessVisible(false);
|
||||
setValue([])
|
||||
completedRef.current = false
|
||||
setErrorVisible(false)
|
||||
setSuccessVisible(false)
|
||||
}
|
||||
}, [visible]);
|
||||
}, [visible])
|
||||
|
||||
// 检查是否完成(3个毛巾都点亮)
|
||||
useEffect(() => {
|
||||
if (value.length === 3 && !completedRef.current) {
|
||||
completedRef.current = true;
|
||||
completedRef.current = true
|
||||
// 显示成功提示
|
||||
setSuccessVisible(true);
|
||||
setSuccessVisible(true)
|
||||
}
|
||||
}, [value]);
|
||||
}, [value])
|
||||
|
||||
const handleClose = useCallback(
|
||||
(e: React.MouseEvent<HTMLImageElement>) => {
|
||||
e.stopPropagation();
|
||||
onClose?.();
|
||||
e.stopPropagation()
|
||||
onClose?.()
|
||||
},
|
||||
[onClose]
|
||||
);
|
||||
)
|
||||
|
||||
// 关闭错误提示
|
||||
const handleErrorClose = useCallback(() => {
|
||||
setErrorVisible(false);
|
||||
}, []);
|
||||
setErrorVisible(false)
|
||||
}, [])
|
||||
|
||||
// 关闭成功提示
|
||||
const handleSuccessContinue = useCallback(() => {
|
||||
setSuccessVisible(false);
|
||||
setSuccessVisible(false)
|
||||
// 关闭提示后,完成并关闭弹窗
|
||||
onComplete?.();
|
||||
onClose?.();
|
||||
}, [onComplete, onClose]);
|
||||
onComplete?.()
|
||||
onClose?.()
|
||||
}, [onComplete, onClose])
|
||||
|
||||
// 可选话术选项
|
||||
const options = useMemo(
|
||||
() => [
|
||||
{
|
||||
value: "option1",
|
||||
label: "这是中华职校生产的国货,质量比洋货还好!",
|
||||
value: 'option1',
|
||||
label: '这是中华职校生产的国货,质量比洋货还好!',
|
||||
},
|
||||
{
|
||||
value: "option2",
|
||||
label: "价格便宜,量大从优,诚信经营绝不掺假",
|
||||
value: 'option2',
|
||||
label: '价格便宜,量大从优,诚信经营绝不掺假',
|
||||
},
|
||||
{
|
||||
value: "option3",
|
||||
label: "买国货就是支持实业救国,为国家出一份力!",
|
||||
value: 'option3',
|
||||
label: '买国货就是支持实业救国,为国家出一份力!',
|
||||
},
|
||||
{
|
||||
value: "option4",
|
||||
label: "我只管赚钱,东西好不好不管哦",
|
||||
value: 'option4',
|
||||
label: '我只管赚钱,东西好不好不管哦',
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
)
|
||||
|
||||
// 转换为 DragFill 的 blanks
|
||||
const blanks = useMemo(
|
||||
() => [
|
||||
{ id: "towel1", label: "毛巾1" },
|
||||
{ id: "towel2", label: "毛巾2" },
|
||||
{ id: "towel3", label: "毛巾3" },
|
||||
{ id: 'towel1', label: '毛巾1' },
|
||||
{ id: 'towel2', label: '毛巾2' },
|
||||
{ id: 'towel3', label: '毛巾3' },
|
||||
],
|
||||
[]
|
||||
);
|
||||
)
|
||||
|
||||
// 处理拖拽结果变化
|
||||
const handleValueChange = useCallback((newValue: DragFillResult[]) => {
|
||||
// 检查是否选择了错误的选项(option4)
|
||||
const hasOption4 = newValue.some(
|
||||
(result) => result.optionValue === "option4"
|
||||
);
|
||||
const hasOption4 = newValue.some((result) => result.optionValue === 'option4')
|
||||
|
||||
if (hasOption4) {
|
||||
// 显示错误提示
|
||||
setErrorVisible(true);
|
||||
setErrorVisible(true)
|
||||
// 移除 option4,保留其他选项
|
||||
const filteredValue = newValue.filter(
|
||||
(result) => result.optionValue !== "option4"
|
||||
);
|
||||
setValue(filteredValue);
|
||||
const filteredValue = newValue.filter((result) => result.optionValue !== 'option4')
|
||||
setValue(filteredValue)
|
||||
} else {
|
||||
// 限制最多3个
|
||||
if (newValue.length <= 3) {
|
||||
setValue(newValue);
|
||||
setValue(newValue)
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
}, [])
|
||||
|
||||
// 检查毛巾是否被点亮
|
||||
const isTowelLit = useCallback(
|
||||
(towelId: string) => {
|
||||
return value.some((result) => result.blankId === towelId);
|
||||
return value.some((result) => result.blankId === towelId)
|
||||
},
|
||||
[value]
|
||||
);
|
||||
)
|
||||
|
||||
// 样式
|
||||
const overlayStyle = useMemo<React.CSSProperties>(
|
||||
() => ({
|
||||
position: "fixed",
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
zIndex: 9999,
|
||||
pointerEvents: "none",
|
||||
pointerEvents: 'none',
|
||||
}),
|
||||
[]
|
||||
);
|
||||
)
|
||||
|
||||
if (!mounted) return null;
|
||||
if (!mounted) return null
|
||||
|
||||
const content = (
|
||||
<AnimatePresence>
|
||||
@@ -176,11 +168,11 @@ const TowelSalesDialog = ({
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.3, ease: "easeInOut" }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
>
|
||||
<div
|
||||
className={cn("absolute inset-0 flex flex-col", "bg-black/70")}
|
||||
style={{ pointerEvents: "auto" }}
|
||||
className={cn('absolute inset-0 flex flex-col', 'bg-black/70')}
|
||||
style={{ pointerEvents: 'auto' }}
|
||||
>
|
||||
{/* 背景蒙版 */}
|
||||
{maskSrc && (
|
||||
@@ -224,7 +216,7 @@ const TowelSalesDialog = ({
|
||||
className="relative w-full flex items-center justify-center transition-all duration-300 rounded-2xl"
|
||||
>
|
||||
<img
|
||||
src={isTowelLit("towel1") ? towel1Lit : towel1}
|
||||
src={isTowelLit('towel1') ? towel1Lit : towel1}
|
||||
draggable={false}
|
||||
alt="毛巾1"
|
||||
className="w-full h-full object-contain"
|
||||
@@ -237,7 +229,7 @@ const TowelSalesDialog = ({
|
||||
className="relative w-full flex items-center justify-center transition-all duration-300 rounded-2xl"
|
||||
>
|
||||
<img
|
||||
src={isTowelLit("towel2") ? towel2Lit : towel2}
|
||||
src={isTowelLit('towel2') ? towel2Lit : towel2}
|
||||
draggable={false}
|
||||
alt="毛巾2"
|
||||
className="w-full h-full object-contain"
|
||||
@@ -250,7 +242,7 @@ const TowelSalesDialog = ({
|
||||
className="relative w-full flex items-center justify-center transition-all duration-300 rounded-2xl"
|
||||
>
|
||||
<img
|
||||
src={isTowelLit("towel3") ? towel3Lit : towel3}
|
||||
src={isTowelLit('towel3') ? towel3Lit : towel3}
|
||||
draggable={false}
|
||||
alt="毛巾3"
|
||||
className="w-full h-full object-contain"
|
||||
@@ -275,32 +267,30 @@ const TowelSalesDialog = ({
|
||||
showSubmitButton={false}
|
||||
className="w-full"
|
||||
renderOption={(option, isUsed) => {
|
||||
const index = options.findIndex(
|
||||
(opt) => opt.value === option.value
|
||||
);
|
||||
const index = options.findIndex((opt) => opt.value === option.value)
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"px-10 py-10 rounded-2xl border-3 text-3xl font-medium transition-all select-none",
|
||||
'px-10 py-10 rounded-2xl border-3 text-3xl font-medium transition-all select-none',
|
||||
isUsed
|
||||
? "border-[#8b7355] bg-[#3d3329] text-[#a89378] cursor-not-allowed opacity-50"
|
||||
: "border-[#79471f] bg-black/20 text-white cursor-move hover:border-[#f0be75] hover:shadow-[0_12px_40px_rgba(240,190,117,0.7)]"
|
||||
? 'border-[#8b7355] bg-[#3d3329] text-[#a89378] cursor-not-allowed opacity-50'
|
||||
: 'border-[#79471f] bg-black/20 text-white cursor-move hover:border-[#f0be75] hover:shadow-[0_12px_40px_rgba(240,190,117,0.7)]'
|
||||
)}
|
||||
>
|
||||
{index + 1}. {option.label}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}}
|
||||
renderDragging={(option) => {
|
||||
return (
|
||||
<div className="w-[638px] h-[90px] flex items-center justify-center rounded-2xl border-3 text-2xl font-medium border-[#f0be75] bg-black/40 text-white shadow-[0_12px_40px_rgba(240,190,117,0.7)]">
|
||||
{option.label}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}}
|
||||
renderBlank={() => {
|
||||
// 返回隐藏元素,实际的空白区域在页面中通过 data-blank-id 定位
|
||||
return <div className="hidden" />;
|
||||
return <div className="hidden" />
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -327,9 +317,9 @@ const TowelSalesDialog = ({
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
)
|
||||
|
||||
return content;
|
||||
};
|
||||
return content
|
||||
}
|
||||
|
||||
export default TowelSalesDialog;
|
||||
export default TowelSalesDialog
|
||||
Reference in new issue
Block a user