refactor: clean up dependencies and enhance component functionality
- Removed unused dependencies related to @dnd-kit from pnpm-lock.yaml. - Updated Vite configuration to set the base path. - Added input styles to the main CSS file. - Enhanced BackgroundImage component to include an overflowHidden prop for better layout control. - Deleted the DraggableItem component as it was no longer needed. - Updated routing to include new Publicity and Content pages, and modified navigation in Menu and NotFound components.
No files matched your search
@@ -8,15 +8,6 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@dnd-kit/core':
|
|
||||||
specifier: ^6.3.1
|
|
||||||
version: 6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
|
||||||
'@dnd-kit/sortable':
|
|
||||||
specifier: ^10.0.0
|
|
||||||
version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)
|
|
||||||
'@dnd-kit/utilities':
|
|
||||||
specifier: ^3.2.2
|
|
||||||
version: 3.2.2(react@19.2.0)
|
|
||||||
axios:
|
axios:
|
||||||
specifier: ^1.12.2
|
specifier: ^1.12.2
|
||||||
version: 1.12.2
|
version: 1.12.2
|
||||||
@@ -180,28 +171,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
|
resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
'@dnd-kit/accessibility@3.1.1':
|
|
||||||
resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.8.0'
|
|
||||||
|
|
||||||
'@dnd-kit/core@6.3.1':
|
|
||||||
resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.8.0'
|
|
||||||
react-dom: '>=16.8.0'
|
|
||||||
|
|
||||||
'@dnd-kit/sortable@10.0.0':
|
|
||||||
resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==}
|
|
||||||
peerDependencies:
|
|
||||||
'@dnd-kit/core': ^6.3.0
|
|
||||||
react: '>=16.8.0'
|
|
||||||
|
|
||||||
'@dnd-kit/utilities@3.2.2':
|
|
||||||
resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.8.0'
|
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.25.11':
|
'@esbuild/aix-ppc64@0.25.11':
|
||||||
resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
|
resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -1464,9 +1433,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=4.8.4'
|
typescript: '>=4.8.4'
|
||||||
|
|
||||||
tslib@2.8.1:
|
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
|
||||||
|
|
||||||
type-check@0.4.0:
|
type-check@0.4.0:
|
||||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -1671,31 +1637,6 @@ snapshots:
|
|||||||
'@babel/helper-string-parser': 7.27.1
|
'@babel/helper-string-parser': 7.27.1
|
||||||
'@babel/helper-validator-identifier': 7.27.1
|
'@babel/helper-validator-identifier': 7.27.1
|
||||||
|
|
||||||
'@dnd-kit/accessibility@3.1.1(react@19.2.0)':
|
|
||||||
dependencies:
|
|
||||||
react: 19.2.0
|
|
||||||
tslib: 2.8.1
|
|
||||||
|
|
||||||
'@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
|
||||||
dependencies:
|
|
||||||
'@dnd-kit/accessibility': 3.1.1(react@19.2.0)
|
|
||||||
'@dnd-kit/utilities': 3.2.2(react@19.2.0)
|
|
||||||
react: 19.2.0
|
|
||||||
react-dom: 19.2.0(react@19.2.0)
|
|
||||||
tslib: 2.8.1
|
|
||||||
|
|
||||||
'@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)':
|
|
||||||
dependencies:
|
|
||||||
'@dnd-kit/core': 6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
|
||||||
'@dnd-kit/utilities': 3.2.2(react@19.2.0)
|
|
||||||
react: 19.2.0
|
|
||||||
tslib: 2.8.1
|
|
||||||
|
|
||||||
'@dnd-kit/utilities@3.2.2(react@19.2.0)':
|
|
||||||
dependencies:
|
|
||||||
react: 19.2.0
|
|
||||||
tslib: 2.8.1
|
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.25.11':
|
'@esbuild/aix-ppc64@0.25.11':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -2814,8 +2755,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
||||||
tslib@2.8.1: {}
|
|
||||||
|
|
||||||
type-check@0.4.0:
|
type-check@0.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
|
|||||||
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 538 KiB |
|
After Width: | Height: | Size: 231 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 641 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
@@ -19,6 +19,7 @@ interface BackgroundImageProps {
|
|||||||
overlay?: boolean;
|
overlay?: boolean;
|
||||||
overlayColor?: string;
|
overlayColor?: string;
|
||||||
overlayOpacity?: number;
|
overlayOpacity?: number;
|
||||||
|
overflowHidden?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BackgroundImage({
|
export default function BackgroundImage({
|
||||||
@@ -39,6 +40,7 @@ export default function BackgroundImage({
|
|||||||
overlay = false,
|
overlay = false,
|
||||||
overlayColor = "rgba(0, 0, 0, 0.5)",
|
overlayColor = "rgba(0, 0, 0, 0.5)",
|
||||||
overlayOpacity = 0.5,
|
overlayOpacity = 0.5,
|
||||||
|
overflowHidden = true,
|
||||||
}: BackgroundImageProps) {
|
}: BackgroundImageProps) {
|
||||||
const [imageSrc, setImageSrc] = useState(src);
|
const [imageSrc, setImageSrc] = useState(src);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
@@ -133,7 +135,11 @@ export default function BackgroundImage({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className={clsx("relative overflow-hidden", className)}
|
className={clsx(
|
||||||
|
"relative",
|
||||||
|
overflowHidden && "overflow-hidden",
|
||||||
|
className
|
||||||
|
)}
|
||||||
style={backgroundStyle}
|
style={backgroundStyle}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
import { ReactNode } from "react";
|
|
||||||
import { useSortable } from "@dnd-kit/sortable";
|
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
|
||||||
import { GripVertical } from "lucide-react";
|
|
||||||
|
|
||||||
interface DraggableItemProps {
|
|
||||||
id: string;
|
|
||||||
children: ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DraggableItem({ id, children }: DraggableItemProps) {
|
|
||||||
const {
|
|
||||||
attributes,
|
|
||||||
listeners,
|
|
||||||
setNodeRef,
|
|
||||||
transform,
|
|
||||||
transition,
|
|
||||||
isDragging,
|
|
||||||
} = useSortable({ id });
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
transform: CSS.Transform.toString(transform),
|
|
||||||
transition,
|
|
||||||
opacity: isDragging ? 0.5 : 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={setNodeRef}
|
|
||||||
style={style}
|
|
||||||
className={`bg-white border border-gray-200 rounded-lg p-4 mb-3 flex items-center gap-3 shadow-sm hover:shadow-md transition-shadow ${
|
|
||||||
isDragging ? "cursor-grabbing" : "cursor-grab"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
{...attributes}
|
|
||||||
{...listeners}
|
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<GripVertical size={20} />
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">{children}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
export { default as Image } from "./Image";
|
export { default as Image } from "./Image";
|
||||||
export { default as BackgroundImage } from "./BackgroundImage";
|
export { default as BackgroundImage } from "./BackgroundImage";
|
||||||
export { default as DraggableItem } from "./DraggableItem";
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
@import "./styles/input.css";
|
||||||
|
|
||||||
/* *{ */
|
/* *{ */
|
||||||
/* margin: 0;
|
/* margin: 0;
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
import { BackgroundImage, Image } from "@/components";
|
||||||
|
import { getStaticImage } from "@/utils";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tab列表
|
||||||
|
* 1. 01工作室介绍
|
||||||
|
* 2. 02带教活动
|
||||||
|
* 3. 03带教心得
|
||||||
|
* 4. 04成果展示
|
||||||
|
*/
|
||||||
|
const tabList = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "01工作室介绍",
|
||||||
|
image: getStaticImage("content/工作室介绍未选中.png"),
|
||||||
|
selectedImage: getStaticImage("content/工作室介绍选中.png"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "02带教活动",
|
||||||
|
image: getStaticImage("content/带教活动未选中.png"),
|
||||||
|
selectedImage: getStaticImage("content/带教活动选中.png"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "03带教心得",
|
||||||
|
image: getStaticImage("content/带教心得未选中.png"),
|
||||||
|
selectedImage: getStaticImage("content/带教心得选中.png"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "04成果展示",
|
||||||
|
image: getStaticImage("content/成果展示未选中.png"),
|
||||||
|
selectedImage: getStaticImage("content/成果展示选中.png"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 侧边栏
|
||||||
|
* 论与技能转向培训
|
||||||
|
* 教学实践与反思
|
||||||
|
* 项目与课题研究
|
||||||
|
* 经验传承与资源共享
|
||||||
|
* 交流与展示活动
|
||||||
|
*/
|
||||||
|
const sideBarList = [
|
||||||
|
{
|
||||||
|
name: "理论与技能转向培训",
|
||||||
|
image: getStaticImage("content/理论与技能专项培训-未选中.png"),
|
||||||
|
selectedImage: getStaticImage("content/理论与技能专项培训-选中.png"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "教学实践与反思",
|
||||||
|
image: getStaticImage("content/教学实践与反思-未选中.png"),
|
||||||
|
selectedImage: getStaticImage("content/教学实践与反思-选中.png"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "项目与课题研究",
|
||||||
|
image: getStaticImage("content/项目与课题研究-未选中.png"),
|
||||||
|
selectedImage: getStaticImage("content/项目与课题研究-选中.png"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "经验传承与资源共享",
|
||||||
|
image: getStaticImage("content/经验传承与资源共享-未选中.png"),
|
||||||
|
selectedImage: getStaticImage("content/经验传承与资源共享-选中.png"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "交流与展示活动",
|
||||||
|
image: getStaticImage("content/交流与展示活动-未选中.png"),
|
||||||
|
selectedImage: getStaticImage("content/交流与展示活动-选中.png"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Tab = ({
|
||||||
|
changeTab,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
changeTab?: (id: number) => void;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
const [selectedTabId, setSelectedTabId] = useState(1);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* tab */}
|
||||||
|
<div className={clsx("w-full flex justify-end", className)}>
|
||||||
|
{tabList.map((item) => (
|
||||||
|
<Image
|
||||||
|
key={item.id}
|
||||||
|
src={selectedTabId === item.id ? item.selectedImage : item.image}
|
||||||
|
alt={item.name}
|
||||||
|
button
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedTabId(item.id);
|
||||||
|
changeTab?.(item.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TabContent
|
||||||
|
className="h-[calc(100%-200px)]"
|
||||||
|
selectedTabId={selectedTabId}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TabContent = ({
|
||||||
|
className,
|
||||||
|
selectedTabId,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
selectedTabId: number;
|
||||||
|
}) => {
|
||||||
|
const [selectedSideBarId, setSelectedSideBarId] = useState(0);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={clsx("overflow-y-auto custom-scrollbar", className)}>
|
||||||
|
{/* 通过tab切换内容 */}
|
||||||
|
{selectedTabId === 1 && (
|
||||||
|
<Image
|
||||||
|
height={942}
|
||||||
|
src={getStaticImage("content/图文.png")}
|
||||||
|
alt="工作室介绍"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{selectedTabId === 2 && (
|
||||||
|
<div className="flex gap-10 pt-8">
|
||||||
|
{/* 侧边栏 */}
|
||||||
|
<div className="flex flex-col gap-10">
|
||||||
|
{sideBarList.map((item, index) => (
|
||||||
|
<Image
|
||||||
|
key={item.name}
|
||||||
|
src={
|
||||||
|
selectedSideBarId === index
|
||||||
|
? item.selectedImage
|
||||||
|
: item.image
|
||||||
|
}
|
||||||
|
alt={item.name}
|
||||||
|
button
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedSideBarId(index);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<BackgroundImage
|
||||||
|
src={getStaticImage("content/视频区域框.png")}
|
||||||
|
width={1354}
|
||||||
|
height={766}
|
||||||
|
>
|
||||||
|
<div className="h-full flex justify-center items-center">
|
||||||
|
<span className="text-[#82B2D6] text-4xl font-bold">
|
||||||
|
图文、视频展示理论与技能专项培训内容
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</BackgroundImage>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selectedTabId === 3 && (
|
||||||
|
<div className="text-center text-[#82B2D6] text-4xl font-bold">
|
||||||
|
暂无内容
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selectedTabId === 4 && (
|
||||||
|
<div className="text-center text-[#82B2D6] text-4xl font-bold">
|
||||||
|
暂无内容
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PublicityPage() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const changeTab = (id: number) => {
|
||||||
|
console.log(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BackgroundImage
|
||||||
|
src={getStaticImage("content/内页底图02.png")}
|
||||||
|
className="h-full w-full relative"
|
||||||
|
backgroundSize="cover"
|
||||||
|
backgroundPosition="center"
|
||||||
|
>
|
||||||
|
<div className=" w-full h-full pt-25">
|
||||||
|
<Image
|
||||||
|
src={getStaticImage("components/按钮-返回.png")}
|
||||||
|
alt="返回"
|
||||||
|
button
|
||||||
|
className="absolute top-0 right-0"
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/publicity");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Tab changeTab={changeTab} />
|
||||||
|
</div>
|
||||||
|
</BackgroundImage>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@ export default function Home() {
|
|||||||
|
|
||||||
const handleSelectTeacher = (teacherId: number) => {
|
const handleSelectTeacher = (teacherId: number) => {
|
||||||
setSelectedTeacherId(teacherId);
|
setSelectedTeacherId(teacherId);
|
||||||
|
navigate("/Publicity");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStartExperiment = () => {
|
const handleStartExperiment = () => {
|
||||||
@@ -66,7 +67,7 @@ export default function Home() {
|
|||||||
alt="返回"
|
alt="返回"
|
||||||
button
|
button
|
||||||
className="absolute top-0 right-0"
|
className="absolute top-0 right-0"
|
||||||
onClick={handleStartExperiment}
|
onClick={() => navigate("/")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from "react-router-dom";
|
||||||
import { Home, ArrowLeft } from 'lucide-react';
|
import { Home, ArrowLeft } from "lucide-react";
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
|
<div className="h-full w-full relative bg-white">
|
||||||
<div className="max-w-2xl mx-auto text-center">
|
<div className="max-w-2xl mx-auto text-center">
|
||||||
<div className="bg-white rounded-xl shadow-lg p-12">
|
<div className="bg-white rounded-xl shadow-lg p-12">
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
@@ -33,5 +34,6 @@ export default function NotFound() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
import { BackgroundImage, Image } from "@/components";
|
||||||
|
import { getStaticImage } from "@/utils";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
// 解答弹窗组件
|
||||||
|
interface DialogBoxProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 功能列表
|
||||||
|
* 1. 01工作室介绍
|
||||||
|
* 2. 02带教活动
|
||||||
|
* 3. 03带教心得
|
||||||
|
* 4. 04成果展示
|
||||||
|
*/
|
||||||
|
const functionList = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "01工作室介绍",
|
||||||
|
image: getStaticImage("publicity/01未选中.png"),
|
||||||
|
selectedImage: getStaticImage("publicity/01工作室介绍选中.png"),
|
||||||
|
position: "absolute bottom-25 left-[35%]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "02带教活动",
|
||||||
|
image: getStaticImage("publicity/02未选中.png"),
|
||||||
|
selectedImage: getStaticImage("publicity/02带教活动选中.png"),
|
||||||
|
position: "absolute bottom-65 left-[50%]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "03带教心得",
|
||||||
|
image: getStaticImage("publicity/03未选中.png"),
|
||||||
|
selectedImage: getStaticImage("publicity/03带教心得选中.png"),
|
||||||
|
position: "absolute bottom-100 left-[65%]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "04成果展示",
|
||||||
|
image: getStaticImage("publicity/04未选中.png"),
|
||||||
|
selectedImage: getStaticImage("publicity/04成果展示选中.png"),
|
||||||
|
position: "absolute bottom-130 left-[80%]",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function DialogBox({ isOpen, onClose }: DialogBoxProps) {
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-start justify-center pt-10 pointer-events-none">
|
||||||
|
{/* 遮罩层 */}
|
||||||
|
{/* <div className="absolute inset-0 bg-black bg-opacity-50" /> */}
|
||||||
|
|
||||||
|
{/* 解答框 */}
|
||||||
|
<BackgroundImage
|
||||||
|
src={getStaticImage("publicity/功能-输入.png")}
|
||||||
|
alt="解答框"
|
||||||
|
width={877}
|
||||||
|
height={218}
|
||||||
|
overflowHidden={false}
|
||||||
|
className="relative z-10 pointer-events-auto"
|
||||||
|
>
|
||||||
|
<div className="relative w-full h-full pt-20 pl-8">
|
||||||
|
{/* 数字人头像 */}
|
||||||
|
<Image
|
||||||
|
src={getStaticImage("publicity/数字人-头像框.png")}
|
||||||
|
alt="数字人头像"
|
||||||
|
className="absolute -left-10 -top-10 z-10"
|
||||||
|
/>
|
||||||
|
{/* 关闭按钮 */}
|
||||||
|
<Image
|
||||||
|
src={getStaticImage("publicity/按钮-关闭.png")}
|
||||||
|
alt="关闭按钮"
|
||||||
|
className="absolute -right-5 -top-5"
|
||||||
|
button
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
{/* 输入框 */}
|
||||||
|
<BackgroundImage
|
||||||
|
src={getStaticImage("publicity/输入框.png")}
|
||||||
|
alt="输入框"
|
||||||
|
width={811}
|
||||||
|
height={100}
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
placeholder="有什么我能帮你的吗直接输入即可.……"
|
||||||
|
className="w-full h-full pl-15 pr-35 pt-4"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
|
{/* icon语音输入和发送按钮 */}
|
||||||
|
<Image
|
||||||
|
src={getStaticImage("publicity/功能-语音输入.png")}
|
||||||
|
className="absolute right-20 bottom-6"
|
||||||
|
button
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src={getStaticImage("publicity/功能-发送.png")}
|
||||||
|
className="absolute right-4 bottom-6"
|
||||||
|
button
|
||||||
|
/>
|
||||||
|
</BackgroundImage>
|
||||||
|
</div>
|
||||||
|
</BackgroundImage>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PublicityPage() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
|
const [selectedFunctionId, setSelectedFunctionId] = useState(0);
|
||||||
|
|
||||||
|
const handleOpenDialog = () => {
|
||||||
|
setIsDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseDialog = () => {
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BackgroundImage
|
||||||
|
src={getStaticImage("publicity/内页底图01.png")}
|
||||||
|
className="h-full w-full relative"
|
||||||
|
backgroundSize="cover"
|
||||||
|
backgroundPosition="center"
|
||||||
|
>
|
||||||
|
{/* 功能列表 */}
|
||||||
|
{functionList.map((item) => (
|
||||||
|
<Image
|
||||||
|
key={item.id}
|
||||||
|
src={selectedFunctionId === item.id ? item.selectedImage : item.image}
|
||||||
|
alt={item.name}
|
||||||
|
className={`${item.position} transition-all duration-300`}
|
||||||
|
button
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedFunctionId(item.id);
|
||||||
|
navigate(`/content/${item.id}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Image
|
||||||
|
src={getStaticImage("components/按钮-返回.png")}
|
||||||
|
alt="返回"
|
||||||
|
button
|
||||||
|
className="absolute top-0 right-0"
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/menu");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 标题 */}
|
||||||
|
<Image
|
||||||
|
src={getStaticImage("publicity/名师工作室-标题.png")}
|
||||||
|
alt="标题"
|
||||||
|
className="absolute left-0 top-30"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 数字人 */}
|
||||||
|
<Image
|
||||||
|
src={getStaticImage("publicity/曹君名师数字人分身.png")}
|
||||||
|
alt="数字人"
|
||||||
|
className="absolute left-0 -bottom-20"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 提示 */}
|
||||||
|
<Image
|
||||||
|
src={getStaticImage("publicity/点击提示.png")}
|
||||||
|
alt="提示"
|
||||||
|
className="absolute left-0 -bottom-10"
|
||||||
|
button
|
||||||
|
onClick={handleOpenDialog}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 解答弹窗 */}
|
||||||
|
<DialogBox isOpen={isDialogOpen} onClose={handleCloseDialog} />
|
||||||
|
</BackgroundImage>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import { createBrowserRouter, Navigate } from "react-router-dom";
|
import { createHashRouter, Navigate } from "react-router-dom";
|
||||||
import Home from "@/pages/Home";
|
import Home from "@/pages/Home";
|
||||||
import Menu from "@/pages/Menu";
|
import Menu from "@/pages/Menu";
|
||||||
import NotFound from "@/pages/NotFound";
|
import NotFound from "@/pages/NotFound";
|
||||||
|
import Publicity from "@/pages/Publicity";
|
||||||
|
import Content from "@/pages/Content";
|
||||||
/**
|
/**
|
||||||
* 应用路由配置
|
* 应用路由配置
|
||||||
* 使用 React Router v7 的 createBrowserRouter
|
* 使用 React Router v7 的 createHashRouter
|
||||||
*/
|
*/
|
||||||
const router = createBrowserRouter([
|
const router = createHashRouter([
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
element: <Navigate to="/home" replace />,
|
element: <Navigate to="/home" replace />,
|
||||||
@@ -21,6 +22,14 @@ const router = createBrowserRouter([
|
|||||||
path: "/menu",
|
path: "/menu",
|
||||||
element: <Menu />,
|
element: <Menu />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/publicity",
|
||||||
|
element: <Publicity />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/content/:id",
|
||||||
|
element: <Content />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "*",
|
path: "*",
|
||||||
element: <NotFound />,
|
element: <NotFound />,
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
input, textarea {
|
||||||
|
font-size: 20px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输入框placeholder颜色 */
|
||||||
|
input::placeholder, textarea::placeholder{
|
||||||
|
font-size: 20px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义滚动容器 - 隐藏滚动条 */
|
||||||
|
.custom-scrollbar {
|
||||||
|
scrollbar-width: none; /* Firefox隐藏滚动条 */
|
||||||
|
-ms-overflow-style: none; /* IE和Edge隐藏滚动条 */
|
||||||
|
perspective: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Webkit浏览器(Chrome, Safari等)隐藏滚动条 */
|
||||||
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import path from "path";
|
|||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: "./",
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||