Update some files.
This commit is contained in:
parent
b885393f3a
commit
783260b334
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||
settings: { react: { version: '18.2' } },
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
20
LICENSE
20
LICENSE
|
@ -1,9 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 skong
|
||||
Copyright (c) 2023 Fromsko
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
66
README.md
66
README.md
|
@ -1,3 +1,65 @@
|
|||
# good-frontend
|
||||
# **课表查询**
|
||||
|
||||
好看的前端模板-自研( chakra-ui + react + emotion )
|
||||
<div align="center">
|
||||
|
||||
## <img src="./src/assets/logo.png" height="80" style="border-radius: 50%;"/>
|
||||
|
||||
📅 **吉首大学课表查询**
|
||||
|
||||
🛠️ 简化查询流程
|
||||
|
||||
---
|
||||
|
||||
</div>
|
||||
|
||||
## 📑 功能特点
|
||||
|
||||
- 自动展示本周课表信息
|
||||
- 可以直接下载
|
||||
- 响应式布局
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
1. 填写后端地址 `./src/components/CnameData.jsx`
|
||||
|
||||
```jsx
|
||||
export const ApiURL = 'http://1.117.154.114:20000'
|
||||
```
|
||||
|
||||
2. 📦 安装并启动应用
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm i
|
||||
npm run dev
|
||||
```
|
||||
|
||||
3. 展示
|
||||
|
||||
- `Loading`
|
||||
|
||||
<div align="center">
|
||||
<img src="./src/assets/loading.png" height=""/>
|
||||
</div>
|
||||
|
||||
* `Running`
|
||||
|
||||
<div align="center">
|
||||
<img src="./src/assets/app-running.png" height=""/>
|
||||
</div>
|
||||
|
||||
## 🔗 访问
|
||||
|
||||
- **请求地址:** `http://host:prot`
|
||||
|
||||
## 🙏 鸣谢
|
||||
|
||||
感谢以下开源项目,它们为本项目的开发提供了重要支持:
|
||||
|
||||
- [React](https://github.com/facebook/react): 一个 `js` 框架
|
||||
- [chakra-ui](https://github.com/chakra-ui/chakra-uichakra-ui): 🌐 用于构建用户界面
|
||||
- [Vite](https://github.com/vitejs/vite): 🚀 下一代前端工具,它的速度很快!
|
||||
|
||||
## ©️ 许可
|
||||
|
||||
本项目基于 MIT 许可证,请查阅 LICENSE 文件以获取更多信息。
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/src/assets/logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>课表查询</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "small-web",
|
||||
"private": true,
|
||||
"version": "5.2.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/icons": "^2.1.1",
|
||||
"@chakra-ui/react": "^2.8.1",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@vue/runtime-core": "^3.3.4",
|
||||
"axios": "^1.5.1",
|
||||
"framer-motion": "^10.16.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.47.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.15",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@vitejs/plugin-react": "^4.0.3",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.3",
|
||||
"vite": "^4.4.11"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
import {
|
||||
Box,
|
||||
ChakraProvider,
|
||||
Divider,
|
||||
Flex,
|
||||
SimpleGrid
|
||||
} from '@chakra-ui/react'
|
||||
import React, { useState } from 'react'
|
||||
import HeartLoading from './components/HeartLoading'
|
||||
import { Navigation } from './components/Navigation'
|
||||
import SendChoice from './components/SendChoice'
|
||||
import TypingText from './components/TypingText'
|
||||
|
||||
function App () {
|
||||
const [typingDone, setTypingDone] = useState(false)
|
||||
|
||||
const Load = () => {
|
||||
setTypingDone(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<ChakraProvider>
|
||||
<Flex direction="column" align="center" justify="center" h="100vh">
|
||||
{/* Loading 动画和打字效果 */}
|
||||
<Flex direction="column" align="center">
|
||||
{!typingDone && (
|
||||
<Flex align="center" justify="center" h="20vh">
|
||||
<HeartLoading />
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{!typingDone && (
|
||||
<Flex align="center">
|
||||
<TypingText
|
||||
text="追风赶月莫停留,平芜尽处是春山。"
|
||||
speed={100}
|
||||
onFinish={Load}
|
||||
timeOut={1500}
|
||||
isclear={true}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{typingDone && (
|
||||
<Box
|
||||
bg="rgba(255, 255, 255, 0.1)"
|
||||
borderRadius="8px"
|
||||
p="4"
|
||||
w={['100%', '80%', '60%']}
|
||||
mx="auto" // 水平居中
|
||||
boxShadow="0px 2px 4px rgba(0, 0, 0, 0.1)" // 减小阴影
|
||||
>
|
||||
<SimpleGrid columns={1} spacing={4} alignItems="center">
|
||||
{/* 状态栏组件 */}
|
||||
<Box bg="rgba(255, 255, 255, 0.2)" p="2" borderRadius="8px">
|
||||
<Navigation />
|
||||
</Box>
|
||||
{/* 选择和发送组件 */}
|
||||
<SendChoice />
|
||||
|
||||
{/* 分割线 */}
|
||||
<Divider />
|
||||
|
||||
{/* 备案信息 */}
|
||||
{/* <Text align="center">xx备xxx号</Text> */}
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
</ChakraProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
Binary file not shown.
After Width: | Height: | Size: 214 KiB |
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,112 @@
|
|||
import { Box, Table, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react'
|
||||
import axios from 'axios'
|
||||
import TypingText from './TypingText'
|
||||
|
||||
export const ApiURL = "http://localhost:2000"
|
||||
|
||||
// 获取当前星期
|
||||
function getWeekly () {
|
||||
const now = new Date()
|
||||
const weekday = now.getDay()
|
||||
const weekdayMap = {
|
||||
1: "星期一",
|
||||
2: "星期二",
|
||||
3: "星期三",
|
||||
4: "星期四",
|
||||
5: "星期五",
|
||||
6: "星期六",
|
||||
0: "星期日",
|
||||
}
|
||||
|
||||
return weekdayMap[weekday]
|
||||
}
|
||||
|
||||
// 获取当前第几周
|
||||
export function GetWeek (startMon) {
|
||||
const now = new Date()
|
||||
const startOfYear = new Date(now.getFullYear(), 0, 1)
|
||||
const days = Math.floor((now - startOfYear) / (24 * 60 * 60 * 1000))
|
||||
const weeks = Math.ceil((days + startOfYear.getDay() + 1) / 7)
|
||||
|
||||
return (weeks - startMon).toString()
|
||||
}
|
||||
|
||||
|
||||
// 获取课表数据
|
||||
export async function GetCnameData () {
|
||||
const week = GetWeek(36)
|
||||
const weekly = getWeekly()
|
||||
let result = ""
|
||||
let fetchUrl = `${ApiURL}/api/v1/get_cname_data?week=${week}`
|
||||
|
||||
try {
|
||||
const response = await axios.get(fetchUrl)
|
||||
const data = response
|
||||
|
||||
console.log(data)
|
||||
// 判断是否请求成功
|
||||
if (data.status === 200) {
|
||||
// 遍历本周数据
|
||||
for (const [key, value] of Object.entries(data.data.课程信息.课程数据[weekly])) {
|
||||
if (value !== "没课哟") {
|
||||
result += `${key} ${value.课程名 || ""} ${value.老师.split('(')[0] || ""} ${value.教室 || ""}\n`
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error)
|
||||
return result
|
||||
}
|
||||
console.error("课表数据获取失败!")
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
export const CnameTable = ({ data }) => {
|
||||
if (data === "") {
|
||||
return (<Box>
|
||||
<TypingText
|
||||
text="一天都没有课呢,好好休息休息吧!"
|
||||
speed={100}
|
||||
onFinish={() => { }}
|
||||
timeOut={1000}
|
||||
/>
|
||||
</Box>)
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Table variant="simple">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th textAlign="center">上课节次</Th>
|
||||
<Th textAlign="center">课程名</Th>
|
||||
<Th textAlign="center">老师</Th>
|
||||
<Th textAlign="center">班级</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody fontSize={"3xs"} whiteSpace="nowrap">
|
||||
{data.map((item, index) => (
|
||||
<Tr key={index}>
|
||||
<Td>{item.time}</Td>
|
||||
<Td>{item.course}</Td>
|
||||
<Td>{item.teacher}</Td>
|
||||
<Td>{item.class}</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export const ParseCnameData = (data) => {
|
||||
if (data === "") {
|
||||
return data
|
||||
}
|
||||
const lines = data.split('\n').filter(Boolean)
|
||||
return lines.map((line) => {
|
||||
const [time, course, teacher, classInfo] = line.split(' ')
|
||||
return { time, course, teacher, class: classInfo }
|
||||
})
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import React from 'react'
|
||||
import { Box } from '@chakra-ui/react'
|
||||
|
||||
|
||||
const HeartLoading = () => {
|
||||
const heartStyle = {
|
||||
position: 'relative',
|
||||
width: '100px',
|
||||
height: '90px',
|
||||
left: '10px',
|
||||
top: '10px',
|
||||
animation: 'heart infinite 2s linear',
|
||||
}
|
||||
|
||||
const heartBeforeAfterStyle = {
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '30px',
|
||||
width: '30px',
|
||||
height: '50px',
|
||||
content: '""',
|
||||
transform: 'rotate(-45deg)',
|
||||
transformOrigin: '0 100%',
|
||||
borderRadius: '30px 30px 0 0',
|
||||
background: 'pink',
|
||||
}
|
||||
|
||||
return (
|
||||
<Box style={heartStyle} className="loading">
|
||||
<Box style={heartBeforeAfterStyle}></Box>
|
||||
<Box style={{ ...heartBeforeAfterStyle, left: '0', transform: 'rotate(45deg)', transformOrigin: '100% 100%' }}></Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default HeartLoading
|
|
@ -0,0 +1,54 @@
|
|||
import React, { useState } from 'react'
|
||||
import { Flex, Text, Image, Box } from '@chakra-ui/react'
|
||||
|
||||
export const Navigation = () => {
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
|
||||
const logoStyle = {
|
||||
height: '5em', // 调整 Logo 大小
|
||||
willChange: 'filter',
|
||||
transition: 'filter 300ms',
|
||||
borderRadius: '60%',
|
||||
filter: isHovered ? 'drop-shadow(0 0 2em #61dafbaa)' : 'none',
|
||||
}
|
||||
|
||||
const containerStyle = {
|
||||
backgroundColor: isHovered ? '#f0f0f0' : 'transparent', // 调整背景颜色
|
||||
margin: 'auto', // 居中
|
||||
borderRadius: '20px',
|
||||
width: '100%', // 调整容器宽度
|
||||
transition: 'background-color 300ms', // 添加过渡效果
|
||||
textAlign: 'center', // 文字居中
|
||||
}
|
||||
|
||||
const textStyle = {
|
||||
color: isHovered ? '#333' : '#000', // 调整文字颜色
|
||||
transition: 'color 300ms', // 添加过渡效果
|
||||
}
|
||||
|
||||
return (
|
||||
<Box style={containerStyle} borderRadius="50px" overflow="hidden">
|
||||
<Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
p="3"
|
||||
height="60px" // 调整高度
|
||||
boxShadow={isHovered ? 'lg' : 'none'}
|
||||
transition="box-shadow 300ms"
|
||||
>
|
||||
<Flex
|
||||
align="center"
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<a href="https://github.com/Fromsko/JishouSchedule" target="_blank">
|
||||
<Image src="/src/assets/logo.png" style={logoStyle} ml="3" />
|
||||
</a>
|
||||
<Text fontSize="xl" noOfLines={1} lineHeight="2" verticalAlign="middle" ml="4" style={textStyle}>
|
||||
吉首大学课表查询
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// PeriodSelector.js
|
||||
import { Menu, MenuButton, MenuList, MenuItem, Button } from '@chakra-ui/react'
|
||||
import { ChevronDownIcon } from '@chakra-ui/icons'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
export const PeriodSelector = ({ onSelect, selectedPeriod }) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
const handleSelect = (period) => {
|
||||
onSelect(period)
|
||||
setIsOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Menu isOpen={isOpen} onClose={() => setIsOpen(false)}>
|
||||
<MenuButton as={Button} rightIcon={<ChevronDownIcon />} onClick={() => setIsOpen(!isOpen)}>
|
||||
{selectedPeriod ? `第${selectedPeriod}周` : '选择周期'}
|
||||
</MenuButton>
|
||||
<MenuList>
|
||||
{[...Array(20)].map((_, index) => (
|
||||
<MenuItem key={index + 1} onClick={() => handleSelect(index + 1)}>
|
||||
第{index + 1}周
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
import axios from 'axios'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Flex, Button, Card, Image, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalCloseButton, useDisclosure } from '@chakra-ui/react'
|
||||
|
||||
import { PeriodSelector } from './PeriodSelector'
|
||||
import { GetCnameData, GetWeek, ApiURL, CnameTable, ParseCnameData } from './CnameData'
|
||||
import TypingText from './TypingText'
|
||||
|
||||
|
||||
// 触发请求
|
||||
const handleSend = async (WeeklyChoice, ImgStatus) => {
|
||||
try {
|
||||
const response = await axios.get(`${ApiURL}/api/v1/get_cname_table?week=${WeeklyChoice}`, {
|
||||
responseType: 'arraybuffer',
|
||||
})
|
||||
|
||||
const blob = new Blob([response.data], { type: 'image/png' })
|
||||
ImgStatus(URL.createObjectURL(blob))
|
||||
} catch (error) {
|
||||
console.error('Error fetching schedule image:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const SendChoice = () => {
|
||||
const week = GetWeek(36)
|
||||
const [Selected, setSelected] = useState(null) // 周期选择
|
||||
const [ImgStatus, setImgStatus] = useState(null) // 图片存储
|
||||
const [cnameData, setCnameData] = useState(null) // 数据存储
|
||||
const [loading, setLoading] = useState(true) // 添加 loading 状态
|
||||
const { isOpen, onOpen, onClose } = useDisclosure() // 用于控制模态框的显示和隐藏
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await GetCnameData()
|
||||
setCnameData(data)
|
||||
} finally {
|
||||
setLoading(false) // 设置 loading 为 false,表示数据加载完成
|
||||
}
|
||||
}
|
||||
|
||||
fetchData()
|
||||
setSelected(week)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// 根据选择改变
|
||||
if (Selected !== null) {
|
||||
handleSend(Selected, setImgStatus)
|
||||
}
|
||||
}, [Selected])
|
||||
|
||||
const handleStore = () => {
|
||||
// 这里你可以提供一个下载链接,让用户点击下载
|
||||
const link = document.createElement('a')
|
||||
link.href = ImgStatus
|
||||
link.download = `第${Selected}周课表.png`
|
||||
link.click()
|
||||
onClose() // 存储后关闭模态框
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex direction="column" align="center">
|
||||
{/* Period Selector */}
|
||||
<PeriodSelector onSelect={setSelected} selectedPeriod={Selected} />
|
||||
|
||||
{ImgStatus && (
|
||||
<Card maxW={['100%', '500px']} mt="3" onClick={onOpen} cursor="pointer">
|
||||
<Image src={ImgStatus} alt="课表" borderRadius="lg" />
|
||||
</Card>
|
||||
)}
|
||||
|
||||
|
||||
<Flex direction="column" align="start" mt="5">
|
||||
{loading ? (
|
||||
<TypingText
|
||||
text="Loading"
|
||||
speed={100}
|
||||
onFinish={() => { }}
|
||||
timeOut={500}
|
||||
/>
|
||||
) : (
|
||||
cnameData !== null && (
|
||||
<CnameTable data={ParseCnameData(cnameData)} />
|
||||
)
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{/* Modal */}
|
||||
< Modal isOpen={isOpen} onClose={onClose} >
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>放大预览</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Image src={ImgStatus} alt="课表" borderRadius="lg" width="100%" height="auto" />
|
||||
</ModalBody>
|
||||
<Button colorScheme="blue" mt="4" onClick={handleStore}>
|
||||
保存
|
||||
</Button>
|
||||
</ModalContent>
|
||||
</Modal >
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export default SendChoice
|
|
@ -0,0 +1,51 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { Text } from '@chakra-ui/react'
|
||||
|
||||
const TypingText = ({ text, speed, onFinish, timeOut, isclear }) => {
|
||||
const [displayText, setDisplayText] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
let index = 0
|
||||
const intervalId = setInterval(() => {
|
||||
setDisplayText((prev) => {
|
||||
if (text[index] === undefined) {
|
||||
return text
|
||||
}
|
||||
return prev + text[index]
|
||||
})
|
||||
index++
|
||||
if (index === text.length) {
|
||||
clearInterval(intervalId)
|
||||
// 打字完成后触发 onFinish 回调
|
||||
setTimeout(() => {
|
||||
if (isclear) {
|
||||
setDisplayText('')
|
||||
}
|
||||
onFinish()
|
||||
}, timeOut) // 等待 timeOut 毫秒后清空 displayText
|
||||
}
|
||||
}, speed)
|
||||
|
||||
// 清理定时器
|
||||
return () => clearInterval(intervalId)
|
||||
}, [text, speed, onFinish, timeOut])
|
||||
|
||||
return (
|
||||
<Text
|
||||
sx={{
|
||||
fontFamily: 'monospace', // 使用等宽字体以保证效果
|
||||
background: 'linear-gradient(to right, rgba(238, 174, 202, 1), rgba(148, 187, 233, 1), rgba(179, 229, 252, 1))',
|
||||
backgroundSize: '200% 100%',
|
||||
animation: `rainbow ${text.length * speed}ms linear infinite`,
|
||||
whiteSpace: 'pre',
|
||||
borderRadius: '4px', // 可以添加圆角效果
|
||||
padding: '8px', // 可以调整内边距
|
||||
fontWeight: 'bold', // 可以加粗字体
|
||||
}}
|
||||
>
|
||||
{displayText}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
export default TypingText
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
host: '0.0.0.0',//ip地址
|
||||
port: 80, // 设置服务启动端口号
|
||||
open: false, // 设置服务启动时是否自动打开浏览器
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue