Commit dc87e256 authored by yangyutan's avatar yangyutan

feat:create react proj lab-tapvideo-web

parents
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/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
# 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?
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
This diff is collapsed.
{
"name": "lab-tapvideo-web",
"private": true,
"version": "0.0.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": {
"antd": "^5.15.3",
"mitt": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3"
},
"devDependencies": {
"@types/react": "^18.2.64",
"@types/react-dom": "^18.2.21",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"vite": "^5.1.6"
}
}
File added
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
/* #root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
} */
/* @keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
} */
/* @media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
} */
import './App.css'
import routes from './routes/index.jsx'
import { useRoutes } from 'react-router-dom'
function App() {
const pages = useRoutes(routes)
return <>{pages}</>
}
export default App
import React from 'react';
import { Button } from 'antd';
const AntTest = () => (
<div className="App">
<Button type="primary">Button</Button>
</div>
);
export default AntTest;
body {
background-color: rgb(48, 66, 148);
}
#box {
width:300px;
height:300px;
border:1px solid yellow;
}
#boll {
width:50px;
height:50px;
background-color:chartreuse;
animation-name:move,chColor;
animation-duration:2s,2s;
animation-direction:alternate-reverse;
animation-iteration-count:4,1; /*infinite*/
}
@keyframes toGroat {
from {
width:50px;
height: 50px;
}
to {
width:200px;
height:200px;
}
}
@keyframes chColor {
from,to {
background-color:green;
}
25% {
background-color:yellow;
}
50% {
background-color: red;
}
75% {
background-color:blueviolet;
}
}
@keyframes move {
25% {
transform: translateX(250px);
}
50% {
transform: translate(250px,250px);
}
75% {
transform: translateY(250px);
}
}
\ No newline at end of file
import './index.css'
import { useEffect } from 'react'
function cssAnimation() {
useEffect(() => {
console.log('all')
})
return (
<>
<div className="container">
<p>css animation</p>
<div id="box">
<div id="boll"></div>
</div>
</div>
</>
)
}
export default cssAnimation
#canvas-container {
width: 360px;
height: 640px;
background-color: #E4E4E4;
}
\ No newline at end of file
import './index.css'
import { useEffect } from 'react'
// import { fabric } from 'fabric'
function Fabric() {
// useEffect(() => {
// var canvas =new fabric.Canvas('canvas-container');
// var rect = new fabric.Rect({
// left:100,//距离画布左侧的距离,单位是像素
// top:100,//距离画布上边的距离
// fill:'red',//填充的颜色
// width:30,//方形的宽度
// height:30//方形的高度
// })
// canvas.add(rect)
// })
return (
<>
<div className="container">
<p>画布</p>
<canvas id="canvas-container">3</canvas>
</div>
</>
)
}
export default Fabric
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import { BrowserRouter } from 'react-router-dom'
const reactDomRoot = ReactDOM.createRoot(document.getElementById('root'))
reactDomRoot.render(
<BrowserRouter>
<App />
</BrowserRouter>
)
export default reactDomRoot
import AntTest from '../../commonents/antTest/index.jsx'
let Home = () => {
return <>
<AntTest />
</>
}
export default Home
\ No newline at end of file
import { Spin } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
const VideoBox = (props) => {
let spin = (
<Spin
delay={1000}
spinning={false}
indicator={
<LoadingOutlined
style={{
fontSize: 64
}}
spin
/>
}
/>
)
return (
<div
style={{
width: 200,
height: 330,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#E4E4E4',
padding: 0,
margin: 0,
borderRadius: 10,
cursor: 'pointer',
overflow: 'hidden'
}}>
<video controls width="100%">
<source src={props.src} type="video/mp4" />
</video>
</div>
)
}
export default VideoBox
import React, { useEffect, useState } from 'react'
import { Layout, Flex } from 'antd'
import VideoBox from './components/videoBox'
import { useLocation } from 'react-router-dom'
const { Header, Footer, Content } = Layout
const headerStyle = {
color: '#fff',
height: 64,
paddingInline: 48,
lineHeight: '64px',
backgroundColor: '#4096ff'
}
const contentStyle = {
minHeight: 120,
lineHeight: '120px',
color: '#fff',
backgroundColor: '#0958d9',
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'flex-start',
alignItems: 'flex-start',
alignContent: 'flex-start',
padding: 25,
gap: 25
}
const footerStyle = {
color: '#fff',
backgroundColor: '#4096ff'
}
const layoutStyle = {
borderRadius: 8,
overflow: 'hidden',
width: 'calc(100% - 8px)',
maxWidth: 'calc(100% - 8px)',
minHeight: '100vh'
}
const VideoList = () => {
const { state } = useLocation()
const [videos, setVideos] = useState([])
useEffect(() => {
console.log('state is:', state)
if (state.videos.length) {
setVideos(() => state.videos.map((video, index) => <VideoBox src={video.src} key={index} />))
}
}, [])
return <>{videos}</>
}
const Preview = () => {
return (
<Flex gap="middle" wrap="wrap">
<Layout style={layoutStyle}>
<Header style={headerStyle}>预览数据</Header>
<Content style={contentStyle}>
<VideoList />
</Content>
<Footer style={footerStyle}>Footer</Footer>
</Layout>
</Flex>
)
}
export default Preview
import React from 'react'
import { InboxOutlined } from '@ant-design/icons'
import { message, Upload, Card } from 'antd'
import $eventbus from '../../../../utils/EventBus.js'
const { Dragger } = Upload
const UploadVideo = (props) => {
let lock = false
const opts = {
name: 'file',
multiple: false,
directory: true,
action: 'https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188',
beforeUpload(file) {
console.log('++++++++++')
console.log(file)
if (!lock) {
$eventbus.emit('upload_fold_name', {
group: props.group,
name: file.webkitRelativePath.split('/')[0]
})
lock = true
}
},
onChange(info) {
const { status } = info.file
if (status !== 'uploading') {
console.log(info.file, info.fileList)
}
if (status === 'done') {
message.success(`${info.file.name} file uploaded successfully.`)
} else if (status === 'error') {
message.error(`${info.file.name} file upload failed.`)
}
},
onDrop(e) {
console.log('Dropped files', e.dataTransfer.files)
}
}
return (
<Card
title={props.title}
bordered={false}
style={{
flexBasis: 'max(400px, calc(44%))',
marginTop: 25
}}>
<Dragger {...opts}>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">单击或拖动文件夹到此区域进行上传</p>
<p className="ant-upload-hint">支持单次或批量上传。</p>
</Dragger>
</Card>
)
}
export default UploadVideo
import React, { useState, useEffect } from 'react'
import { Layout, Flex, Card, Button } from 'antd'
import { UploadOutlined } from '@ant-design/icons'
import UploadVideo from './components/uploadVideo'
import $eventbus from '../../utils/EventBus.js'
import { useNavigate } from 'react-router-dom'
const { Header, Footer, Content } = Layout
const headerStyle = {
color: '#fff',
height: 64,
paddingInline: 48,
lineHeight: '64px',
backgroundColor: '#4096ff'
}
const contentStyle = {
minHeight: 120,
lineHeight: '120px',
color: '#fff',
backgroundColor: '#0958d9',
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-around',
flexWrap: 'wrap',
padding: 25,
gap: 25
}
const footerStyle = {
color: '#fff',
backgroundColor: '#4096ff'
}
const layoutStyle = {
borderRadius: 8,
overflow: 'hidden',
width: 'calc(100% - 8px)',
maxWidth: 'calc(100% - 8px)'
}
const Upload = () => {
const navigate = useNavigate()
const [loading, setLoading] = useState(false)
const [folds, setFolds] = useState([])
const fetchData = async () => {
setLoading(true)
// const res = await fetch('http://10.20.1.10:port/upload', {
// method: "POST",
// mode: "cors",
// body: JSON.stringify(folds)
// })
// if(res.status == 200) {
// setLoading(false)
// const videos = res.body
// navigate('/preview', { state: { videos } })
// } else {
// console.error('res.status:', res.status)
// }
setTimeout(() => {
const videos = [
{
src: '/test.mp4'
},
{
src: '/test.mp4'
},
{
src: '/test.mp4'
}
]
setLoading(false)
navigate('/preview', { state: { videos } })
}, 3000)
}
useEffect(() => {
const recvFoldData = (fold) => {
console.log('======================')
setFolds((prevFolds) => {
const newFolds = [...prevFolds]
newFolds.push(fold)
return newFolds
})
}
$eventbus.on('upload_fold_name', recvFoldData)
return () => {
$eventbus.off('upload_fold_name', recvFoldData)
}
}, [])
return (
<Flex gap="middle" wrap="wrap">
<Layout style={layoutStyle}>
<Header style={headerStyle}>上传数据</Header>
<Content style={contentStyle}>
<UploadVideo title="上传第一组视频:" group={0} />
<UploadVideo title="上传第二组视频:" group={1} />
<Card
bordered={false}
style={{
display: 'flex',
justifyContent: 'space-around',
flexBasis: 'max(calc(95%), min(400px, calc(100%)))',
marginTop: 25
}}>
<Button type="primary" size="large" icon={<UploadOutlined />} loading={loading} onClick={() => fetchData()}>
上传处理视频
</Button>
</Card>
</Content>
<Footer style={footerStyle}>Footer</Footer>
</Layout>
</Flex>
)
}
export default Upload
import Home from '../pages/home/index.jsx'
import Upload from '../pages/upload/index.jsx'
import Preview from '../pages/preview/index.jsx'
import { Navigate } from 'react-router-dom'
let routes = [
{
path: '/',
element: <Navigate to="/upload" />
},{
path: '/home',
element: <Home />
},
{
path: '/upload',
element: <Upload />
},
{
path: '/preview',
element: <Preview />
}
]
export default routes
\ No newline at end of file
import mitt from 'mitt'
const $eventbus = mitt()
export default $eventbus
\ No newline at end of file
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment