Skip to content

Commit c2642ef

Browse files
committed
添加向量化报告功能,更新 ScanControls 组件以支持打开和关闭向量化模态窗,新增相应的状态管理和按钮交互。更新国际化文本以支持向量化报告的相关内容,增强用户体验。
1 parent 9da9142 commit c2642ef

5 files changed

Lines changed: 1024 additions & 87 deletions

File tree

src/components/ScanControls.tsx

Lines changed: 155 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { isFileSystemObserverSupported } from "../lib/fileObserver";
2727
import { useTranslations } from "./LocaleProvider";
2828
import { motion, AnimatePresence } from "framer-motion";
2929
import { detectDockerfile } from "../lib/dockerService";
30+
import VectorizeModal from "./VectorizeModal";
3031

3132
export default function ScanControls() {
3233
const { t } = useTranslations();
@@ -49,6 +50,7 @@ export default function ScanControls() {
4950
const [scanProgress, setScanProgress] = useState(0);
5051
const [scanCompleted, setScanCompleted] = useState(false);
5152
const [showPulse, setShowPulse] = useState(false);
53+
const [showVectorizeModal, setShowVectorizeModal] = useState(false);
5254

5355
// 监控定时器引用
5456
const monitorTimerRef = useRef<NodeJS.Timeout | null>(null);
@@ -359,6 +361,16 @@ export default function ScanControls() {
359361
}
360362
};
361363

364+
// 打开向量化报告模态窗
365+
const handleOpenVectorizeModal = () => {
366+
setShowVectorizeModal(true);
367+
};
368+
369+
// 关闭向量化报告模态窗
370+
const handleCloseVectorizeModal = () => {
371+
setShowVectorizeModal(false);
372+
};
373+
362374
// 如果没有目录句柄,不显示控制器
363375
if (!directoryHandle) return null;
364376

@@ -647,98 +659,134 @@ export default function ScanControls() {
647659
</span>
648660
</motion.button>
649661

662+
<motion.button
663+
onClick={handleDownloadReport}
664+
disabled={isDownloading || !currentScan}
665+
className={`px-4 py-2 rounded-md text-white transition-colors ${
666+
isDownloading || !currentScan
667+
? "bg-gray-400 cursor-not-allowed"
668+
: "bg-indigo-600 hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
669+
}`}
670+
whileHover={{ scale: isDownloading || !currentScan ? 1 : 1.05 }}
671+
whileTap={{ scale: isDownloading || !currentScan ? 1 : 0.95 }}
672+
transition={{ type: "spring", stiffness: 400, damping: 10 }}
673+
>
674+
<span className="flex items-center">
675+
{isDownloading || scanStatus === "preparing" ? (
676+
<>
677+
<motion.svg
678+
className="-ml-1 mr-2 h-5 w-5 text-white"
679+
xmlns="http://www.w3.org/2000/svg"
680+
fill="none"
681+
viewBox="0 0 24 24"
682+
animate={{
683+
rotate: 360,
684+
scale: [1, 1.1, 1],
685+
}}
686+
transition={{
687+
rotate: {
688+
duration: 1.5,
689+
repeat: Infinity,
690+
ease: "linear",
691+
},
692+
scale: {
693+
duration: 1,
694+
repeat: Infinity,
695+
ease: "easeInOut",
696+
},
697+
}}
698+
>
699+
<circle
700+
className="opacity-25"
701+
cx="12"
702+
cy="12"
703+
r="10"
704+
stroke="currentColor"
705+
strokeWidth="4"
706+
></circle>
707+
<path
708+
className="opacity-75"
709+
fill="currentColor"
710+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
711+
></path>
712+
</motion.svg>
713+
<motion.span
714+
animate={{ opacity: [0.6, 1, 0.6] }}
715+
transition={{
716+
duration: 1.5,
717+
repeat: Infinity,
718+
ease: "easeInOut",
719+
}}
720+
>
721+
{scanStatus === "preparing"
722+
? t("scanControls.preparingReport")
723+
: t("scanControls.downloading")}
724+
</motion.span>
725+
</>
726+
) : (
727+
<>
728+
<motion.svg
729+
xmlns="http://www.w3.org/2000/svg"
730+
className="h-5 w-5 mr-2"
731+
fill="none"
732+
viewBox="0 0 24 24"
733+
stroke="currentColor"
734+
animate={{
735+
y: [0, -2, 0, 2, 0],
736+
}}
737+
transition={{
738+
duration: 2,
739+
repeat: Infinity,
740+
ease: "easeInOut",
741+
}}
742+
>
743+
<path
744+
strokeLinecap="round"
745+
strokeLinejoin="round"
746+
strokeWidth={2}
747+
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
748+
/>
749+
</motion.svg>
750+
{t("scanControls.download")}
751+
</>
752+
)}
753+
</span>
754+
</motion.button>
755+
756+
{/* 向量化报告按钮 */}
650757
{currentScan && (
651758
<motion.button
652-
onClick={handleDownloadReport}
653-
disabled={isDownloading || !changeReport}
654-
className={`px-4 py-2 rounded-md text-white transition-colors ${
655-
isDownloading || !changeReport
656-
? "bg-gray-400 cursor-not-allowed"
657-
: "bg-indigo-600 hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
658-
}`}
659-
whileHover={{ scale: isDownloading || !changeReport ? 1 : 1.05 }}
660-
whileTap={{ scale: isDownloading || !changeReport ? 1 : 0.95 }}
759+
onClick={handleOpenVectorizeModal}
760+
className="px-4 py-2 rounded-md text-white transition-colors bg-purple-600 hover:bg-purple-700 focus:ring-2 focus:ring-purple-500 focus:ring-offset-2"
761+
whileHover={{ scale: 1.05 }}
762+
whileTap={{ scale: 0.95 }}
661763
transition={{ type: "spring", stiffness: 400, damping: 10 }}
662764
>
663765
<span className="flex items-center">
664-
{isDownloading || scanStatus === "preparing" ? (
665-
<>
666-
<motion.svg
667-
className="-ml-1 mr-2 h-5 w-5 text-white"
668-
xmlns="http://www.w3.org/2000/svg"
669-
fill="none"
670-
viewBox="0 0 24 24"
671-
animate={{
672-
rotate: 360,
673-
scale: [1, 1.1, 1],
674-
}}
675-
transition={{
676-
rotate: {
677-
duration: 1.5,
678-
repeat: Infinity,
679-
ease: "linear",
680-
},
681-
scale: {
682-
duration: 1,
683-
repeat: Infinity,
684-
ease: "easeInOut",
685-
},
686-
}}
687-
>
688-
<circle
689-
className="opacity-25"
690-
cx="12"
691-
cy="12"
692-
r="10"
693-
stroke="currentColor"
694-
strokeWidth="4"
695-
></circle>
696-
<path
697-
className="opacity-75"
698-
fill="currentColor"
699-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
700-
></path>
701-
</motion.svg>
702-
<motion.span
703-
animate={{ opacity: [0.6, 1, 0.6] }}
704-
transition={{
705-
duration: 1.5,
706-
repeat: Infinity,
707-
ease: "easeInOut",
708-
}}
709-
>
710-
{scanStatus === "preparing"
711-
? t("scanControls.preparingReport")
712-
: t("scanControls.downloading")}
713-
</motion.span>
714-
</>
715-
) : (
716-
<>
717-
<motion.svg
718-
xmlns="http://www.w3.org/2000/svg"
719-
className="h-5 w-5 mr-2"
720-
fill="none"
721-
viewBox="0 0 24 24"
722-
stroke="currentColor"
723-
animate={{
724-
y: [0, -2, 0, 2, 0],
725-
}}
726-
transition={{
727-
duration: 2,
728-
repeat: Infinity,
729-
ease: "easeInOut",
730-
}}
731-
>
732-
<path
733-
strokeLinecap="round"
734-
strokeLinejoin="round"
735-
strokeWidth={2}
736-
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
737-
/>
738-
</motion.svg>
739-
{t("scanControls.download")}
740-
</>
741-
)}
766+
<motion.svg
767+
xmlns="http://www.w3.org/2000/svg"
768+
className="h-5 w-5 mr-2"
769+
fill="none"
770+
viewBox="0 0 24 24"
771+
stroke="currentColor"
772+
animate={{
773+
rotate: [0, 0, 10, -10, 0],
774+
scale: [1, 1.1, 1.1, 1.1, 1],
775+
}}
776+
transition={{
777+
duration: 3,
778+
repeat: Infinity,
779+
ease: "easeInOut",
780+
}}
781+
>
782+
<path
783+
strokeLinecap="round"
784+
strokeLinejoin="round"
785+
strokeWidth={2}
786+
d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"
787+
/>
788+
</motion.svg>
789+
{t("scanControls.vectorize")}
742790
</span>
743791
</motion.button>
744792
)}
@@ -829,6 +877,26 @@ export default function ScanControls() {
829877
</motion.div>
830878
)}
831879
</AnimatePresence>
880+
881+
{/* 向量化报告模态窗 */}
882+
{showVectorizeModal && (
883+
<div
884+
className="fixed inset-0 z-[9999]"
885+
style={{
886+
position: "fixed",
887+
top: 0,
888+
left: 0,
889+
right: 0,
890+
bottom: 0,
891+
display: "flex",
892+
alignItems: "center",
893+
justifyContent: "center",
894+
backgroundColor: "rgba(0, 0, 0, 0.5)",
895+
}}
896+
>
897+
<VectorizeModal onClose={handleCloseVectorizeModal} />
898+
</div>
899+
)}
832900
</div>
833901
);
834902
}

0 commit comments

Comments
 (0)