1.根据ui大致完成登录界面。
This commit is contained in:
parent
99d12b483d
commit
fcaae1860c
@ -6,14 +6,21 @@
|
||||
"@emotion/react": "^11.11.0",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.11.16",
|
||||
"@mui/lab": "^5.0.0-alpha.133",
|
||||
"@mui/material": "^5.13.3",
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"md5": "^2.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "^8.0.7",
|
||||
"react-router-dom": "^6.11.2",
|
||||
"react-scripts": "5.0.1",
|
||||
"sha1": "^1.1.1",
|
||||
"sha256": "^0.2.0",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -1,43 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>纽曼AI语记</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
|
BIN
public/logo.png
Normal file
BIN
public/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
@ -11,11 +11,6 @@
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
|
148
src/LoginPage.js
148
src/LoginPage.js
@ -1,13 +1,64 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Button from '@mui/material/Button';
|
||||
import TextField from '@mui/material/TextField';
|
||||
|
||||
import yzs from "./business/request.js";
|
||||
import styles from './LoginPage.module.css';
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { setAddress } from "./business/ipSlice.js"
|
||||
import { setFlushToken, setAccessToken, setUserInfo, selectFlushToken } from "./business/userSlice.js"
|
||||
import logo from './assets/logo.png'; // Tell webpack this JS file uses this image
|
||||
import { Container, Tab, Box } from '@mui/material';
|
||||
import TabPanel from '@mui/lab/TabPanel';
|
||||
import { TabList } from '@mui/lab';
|
||||
import TabContext from '@mui/lab/TabContext';
|
||||
import DynamicCodeForm from './components/DynamicCodeForm.js';
|
||||
import PasswordForm from './components/PasswordForm.js';
|
||||
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||
|
||||
const theme = createTheme({
|
||||
status: {
|
||||
danger: '#e53e3e',
|
||||
},
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#FF595A',
|
||||
darker: '#FF595A',
|
||||
},
|
||||
neutral: {
|
||||
main: '#64748B',
|
||||
contrastText: '#fff',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default function () {
|
||||
const [value, setValue] = useState("1");
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
|
||||
const ip = useSelector(state => state.ip.value)
|
||||
const flushToken = useSelector(selectFlushToken)
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const getIp = async () => {
|
||||
// Connect ipapi.co with fetch()
|
||||
const response = await fetch("https://ipapi.co/json/")
|
||||
const data = await response.json()
|
||||
// Set the IP address to the constant `ip`
|
||||
dispatch(setAddress(data.ip));
|
||||
}
|
||||
|
||||
// Run `getIP` function above just once when the page is rendered
|
||||
useEffect(() => {
|
||||
getIp()
|
||||
}, [])
|
||||
|
||||
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
@ -18,33 +69,76 @@ export default function () {
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
console.log(`Username: ${username}\nPassword: ${password}`);
|
||||
console.log(`Username: ${username}\nPassword: ${password} ip: ${ip}`);
|
||||
|
||||
yzs.login(ip.payload).then(token => {
|
||||
dispatch(setFlushToken(token));
|
||||
yzs.get_access_token(ip.payload, token).then(token => {
|
||||
// yzs.update_access_token(ip.payload, token);
|
||||
dispatch(setAccessToken(token));
|
||||
yzs.get_user_info(ip.payload, token).then(info => {
|
||||
dispatch(setUserInfo(info));
|
||||
let passportId = info.passportId;
|
||||
yzs.user_select(ip.payload, token).then(info => {
|
||||
yzs.get_record_list(token, passportId)
|
||||
})
|
||||
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<form className={styles.form} onSubmit={handleSubmit}>
|
||||
<TextField
|
||||
name="username"
|
||||
label="请输入手机号码"
|
||||
variant="outlined"
|
||||
value={username}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<TextField
|
||||
name="password"
|
||||
label="请输入密码"
|
||||
type="password"
|
||||
variant="outlined"
|
||||
value={password}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
登录
|
||||
</Button>
|
||||
</form>
|
||||
<div className={styles.loginPage}>
|
||||
<div className={styles.title}>
|
||||
<img className={styles.titleIcon} src={logo} />
|
||||
<h1 className={styles.titleText}>纽曼AI语记</h1>
|
||||
</div>
|
||||
|
||||
<div className={styles.loginFrame}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<Container component="form" className={styles.form} onSubmit={handleSubmit}
|
||||
sx={{
|
||||
width: 360,
|
||||
height: 418,
|
||||
backgroundColor: 'white',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
boxShadow: "0px 5px 20px 0px rgba(146,0,1,0.1)",
|
||||
borderRadius: 4,
|
||||
|
||||
}}
|
||||
>
|
||||
<TabContext value={value}>
|
||||
<Box>
|
||||
|
||||
<TabList
|
||||
|
||||
aria-label="basic tabs example" value={value} onChange={handleChange} >
|
||||
<Tab label="手机动态码登录" value="1" />
|
||||
<Tab label="账号密码登录" value="2" />
|
||||
</TabList>
|
||||
</Box>
|
||||
|
||||
<TabPanel value="1" >
|
||||
<DynamicCodeForm />
|
||||
</TabPanel>
|
||||
<TabPanel value="2" >
|
||||
<PasswordForm />
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
|
||||
|
||||
|
||||
</Container>
|
||||
</ThemeProvider >
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,4 +1,29 @@
|
||||
.form {
|
||||
.loginPage {
|
||||
background-image: url(./assets/background@2x.png);
|
||||
background-size: cover;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 72px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.titleIcon {
|
||||
width: 54px;
|
||||
height: 57px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.titleText {
|
||||
color: #FF595A;
|
||||
}
|
||||
|
||||
|
||||
.loginFrame {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
BIN
src/assets/background@2x.png
Normal file
BIN
src/assets/background@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 493 KiB |
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
src/assets/logo@2x.png
Normal file
BIN
src/assets/logo@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
22
src/business/ipSlice.js
Normal file
22
src/business/ipSlice.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
|
||||
export const ipSlice = createSlice({
|
||||
name: 'ip',
|
||||
initialState: {
|
||||
value: ""
|
||||
},
|
||||
reducers: {
|
||||
setAddress: (state, ip) => {
|
||||
// Redux Toolkit allows us to write "mutating" logic in reducers. It
|
||||
// doesn't actually mutate the state because it uses the Immer library,
|
||||
// which detects changes to a "draft state" and produces a brand new
|
||||
// immutable state based off those changes
|
||||
state.value = ip;
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
export const { setAddress } = ipSlice.actions
|
||||
|
||||
export default ipSlice.reducer
|
170
src/business/request.js
Normal file
170
src/business/request.js
Normal file
@ -0,0 +1,170 @@
|
||||
const appKey = "k5hfiei5eevouvjohkapjaudpk2gakpaxha22fiy";
|
||||
const appSecret = "e65ffb25148b08207088148d5bce114d";
|
||||
|
||||
function constructParameter(body) {
|
||||
let params = [];
|
||||
for (let key in body) {
|
||||
params.push(body[key].toString());
|
||||
}
|
||||
params.sort();
|
||||
let digest = "";
|
||||
for (let param of params) {
|
||||
console.log(param)
|
||||
digest += param;
|
||||
}
|
||||
let sha1 = require('sha1');
|
||||
body.signature = sha1(digest).toUpperCase();
|
||||
|
||||
let p = '';
|
||||
for (let key in body) {
|
||||
p += key;
|
||||
p += "=";
|
||||
p += encodeURIComponent(body[key]);
|
||||
p += "&";
|
||||
}
|
||||
p = p.slice(0, -1);
|
||||
return p;
|
||||
}
|
||||
|
||||
const yzs = {
|
||||
get_access_token: function (ip, flushToken) {
|
||||
let body = {};
|
||||
body.subsystemId = 16;
|
||||
body.clientId = ip;
|
||||
body.timestamp = parseInt(new Date().getTime() / 1000);
|
||||
body.flushToken = flushToken;
|
||||
|
||||
return fetch("http://116.198.37.53:8080/rest/v2/token/get_access_token", {
|
||||
method: "POST",
|
||||
body: constructParameter(body),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
},
|
||||
}).then(response => response.json()).then((json) => {
|
||||
console.log(json);
|
||||
return json.result.accessToken;
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
update_access_token: function (ip, accessToken) {
|
||||
let body = {};
|
||||
body.subsystemId = 16;
|
||||
body.clientId = ip;
|
||||
body.timestamp = parseInt(new Date().getTime() / 1000);
|
||||
body.accessToken = accessToken;
|
||||
|
||||
return fetch("http://116.198.37.53:8080/rest/v2/token/refresh_access_token", {
|
||||
method: "POST",
|
||||
body: constructParameter(body),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
},
|
||||
}).then(response => response.json()).then((json) => {
|
||||
console.log(json);
|
||||
return json.result.accessToken;
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
|
||||
get_user_info: function (ip, accessToken) {
|
||||
let body = {};
|
||||
body.subsystemId = 16;
|
||||
body.clientId = ip;
|
||||
body.timestamp = parseInt(new Date().getTime() / 1000);
|
||||
body.accessToken = accessToken;
|
||||
|
||||
return fetch("http://116.198.37.53:8080/rest/v2/user/get_user_info", {
|
||||
method: "POST",
|
||||
body: constructParameter(body),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
},
|
||||
}).then(response => response.json()).then((json) => {
|
||||
console.log(json);
|
||||
return json.result;
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
user_select: function (ip, accessToken) {
|
||||
let sha256 = require('sha256');
|
||||
let timestamp = new Date().getTime();
|
||||
let sig = appKey + timestamp.toString() + appSecret;
|
||||
sig = sha256(sig).toUpperCase();;
|
||||
|
||||
|
||||
let url = `/api/app/app-voice-recorder/rest/v1/user/select?accessToken=${encodeURIComponent(accessToken)}&phoneUdid=${encodeURIComponent(ip)}`;
|
||||
console.log("url: ", url)
|
||||
|
||||
return fetch(url, {
|
||||
headers: {
|
||||
'appKey': appKey,
|
||||
'timestamp': timestamp,
|
||||
'signature': sig,
|
||||
},
|
||||
}).then(response => {
|
||||
console.log(response);
|
||||
response.text()
|
||||
}).then((json) => {
|
||||
console.log(json)
|
||||
return json;
|
||||
});
|
||||
},
|
||||
|
||||
get_record_list: function (accessToken, passportId) {
|
||||
let sha256 = require('sha256');
|
||||
let timestamp = new Date().getTime();
|
||||
let sig = appKey + timestamp.toString() + appSecret;
|
||||
sig = sha256(sig).toUpperCase();;
|
||||
|
||||
|
||||
let url = `/api/app/app-voice-recorder/rest/v1/trans/info/list?accessToken=${encodeURIComponent(accessToken)}&passportId=${passportId}`;
|
||||
console.log("url: ", url)
|
||||
|
||||
return fetch(url, {
|
||||
headers: {
|
||||
'appKey': appKey,
|
||||
'timestamp': timestamp,
|
||||
'signature': sig,
|
||||
},
|
||||
}).then(response => {
|
||||
console.log(response);
|
||||
response.text()
|
||||
}).then((json) => {
|
||||
console.log(json)
|
||||
return json;
|
||||
});
|
||||
},
|
||||
|
||||
login: function (ip) {
|
||||
let md5 = require('md5');
|
||||
let body = {};
|
||||
body.subsystemId = 16;
|
||||
body.clientId = ip;
|
||||
body.timestamp = parseInt(new Date().getTime() / 1000);
|
||||
body.account = "13682423271";
|
||||
body.password = md5("yzs123456");
|
||||
|
||||
return fetch("http://116.198.37.53:8080/rest/v2/user/login", {
|
||||
method: "POST",
|
||||
body: constructParameter(body),
|
||||
// mode: "no-cors",
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
},
|
||||
}).then(response => response.json()).then((json) => {
|
||||
console.log("flushToken: ", json.result.flushToken);
|
||||
return json.result.flushToken;
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
export default yzs;
|
10
src/business/store.js
Normal file
10
src/business/store.js
Normal file
@ -0,0 +1,10 @@
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
import ipReducer from "./ipSlice.js"
|
||||
import userReducer from "./userSlice.js"
|
||||
|
||||
export default configureStore({
|
||||
reducer: {
|
||||
ip: ipReducer,
|
||||
user: userReducer,
|
||||
}
|
||||
})
|
33
src/business/userSlice.js
Normal file
33
src/business/userSlice.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
|
||||
export const userSlice = createSlice({
|
||||
name: 'user',
|
||||
initialState: {
|
||||
flushToken: "",
|
||||
accessToken: "",
|
||||
info: {},
|
||||
},
|
||||
reducers: {
|
||||
setFlushToken: (state, token) => {
|
||||
// Redux Toolkit allows us to write "mutating" logic in reducers. It
|
||||
// doesn't actually mutate the state because it uses the Immer library,
|
||||
// which detects changes to a "draft state" and produces a brand new
|
||||
// immutable state based off those changes
|
||||
state.flushToken = token;
|
||||
},
|
||||
setAccessToken: (state, token) => {
|
||||
state.accessToken = token;
|
||||
},
|
||||
setUserInfo: (state, info) => {
|
||||
state.info = info;
|
||||
// state.createTime = info.createTime;
|
||||
// state.userName = info.userName;
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
export const { setFlushToken, setAccessToken, setUserInfo } = userSlice.actions
|
||||
export const selectFlushToken = (state) => state.user.flushToken
|
||||
|
||||
export default userSlice.reducer
|
90
src/components/DynamicCodeForm.js
Normal file
90
src/components/DynamicCodeForm.js
Normal file
@ -0,0 +1,90 @@
|
||||
import { Container, TextField, InputAdornment, Link, Button, Stack, Typography } from "@mui/material";
|
||||
import { CheckBox } from '@mui/icons-material';
|
||||
import React, { useState } from 'react';
|
||||
import PhoneIphoneIcon from '@mui/icons-material/PhoneIphone';
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
|
||||
export default function () {
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
if (name === 'username') setUsername(value);
|
||||
if (name === 'password') setPassword(value);
|
||||
|
||||
};
|
||||
|
||||
return <Container disableGutters={true}
|
||||
sx={{
|
||||
width: 300,
|
||||
height: 200,
|
||||
}}
|
||||
>
|
||||
|
||||
<TextField
|
||||
name="username"
|
||||
label="请输入手机号码"
|
||||
variant="outlined"
|
||||
value={username}
|
||||
color="primary"
|
||||
fullWidth
|
||||
onChange={handleInputChange}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<PhoneIphoneIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
// sx={{ paddingTop: 4 }}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
name="password"
|
||||
label="请输入验证码"
|
||||
type="password"
|
||||
variant="outlined"
|
||||
value={password}
|
||||
onChange={handleInputChange}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<LockIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
|
||||
endAdornment: <InputAdornment position="end">
|
||||
<Link>发送动态码</Link>
|
||||
</InputAdornment>
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
sx={{
|
||||
backgroundColor: "#FF595A",
|
||||
'&:hover': {
|
||||
backgroundColor: '#FF595A',
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: '#FF595A',
|
||||
},
|
||||
}}
|
||||
>
|
||||
注册/登录
|
||||
</Button>
|
||||
<Container>
|
||||
<Stack direction="row" spacing={1}
|
||||
sx={{ paddingTop: 2 }}
|
||||
>
|
||||
<CheckBox color="primary" />
|
||||
<Typography>同意 <Link>《纽曼隐私协议》</Link></Typography>
|
||||
</Stack>
|
||||
</Container>
|
||||
|
||||
</Container>
|
||||
}
|
74
src/components/PasswordForm.js
Normal file
74
src/components/PasswordForm.js
Normal file
@ -0,0 +1,74 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Container, TextField, Button, InputAdornment } from "@mui/material";
|
||||
import PhoneIphoneIcon from '@mui/icons-material/PhoneIphone';
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
|
||||
export default function () {
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
if (name === 'username') setUsername(value);
|
||||
if (name === 'password') setPassword(value);
|
||||
|
||||
};
|
||||
|
||||
return <Container disableGutters={true}
|
||||
sx={{
|
||||
width: 300,
|
||||
height: 200,
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
name="username"
|
||||
label="请输入手机号码"
|
||||
variant="outlined"
|
||||
value={username}
|
||||
fullWidth
|
||||
onChange={handleInputChange}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<PhoneIphoneIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
// sx={{ paddingTop: 4 }}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
name="password"
|
||||
label="请输入密码"
|
||||
type="password"
|
||||
variant="outlined"
|
||||
value={password}
|
||||
onChange={handleInputChange}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<LockIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
sx={{
|
||||
backgroundColor: "#FF595A",
|
||||
'&:hover': {
|
||||
backgroundColor: '#FF595A',
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: '#FF595A',
|
||||
},
|
||||
}}
|
||||
>
|
||||
登录
|
||||
</Button>
|
||||
</Container>
|
||||
}
|
10
src/index.js
10
src/index.js
@ -2,15 +2,19 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import store from './business/store';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { Provider } from "react-redux";
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter >
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
<Provider store={store}>
|
||||
<BrowserRouter >
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
|
27
src/setupProxy.js
Normal file
27
src/setupProxy.js
Normal file
@ -0,0 +1,27 @@
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||
|
||||
module.exports = function (app) {
|
||||
app.use(
|
||||
'/api/app/app-voice-recorder/rest/v1/trans/info/list',
|
||||
createProxyMiddleware({
|
||||
target: 'http://ai-api.uat.hivoice.cn',
|
||||
changeOrigin: true,
|
||||
logger: console,
|
||||
onProxyReq: (proxyReq, req, res) => {
|
||||
proxyReq.setHeader('appKey', 'k5hfiei5eevouvjohkapjaudpk2gakpaxha22fiy');
|
||||
// console.log("proxyReq", req)
|
||||
},
|
||||
})
|
||||
);
|
||||
app.use(
|
||||
'/api/app/app-voice-recorder/rest/v1/user/select',
|
||||
createProxyMiddleware({
|
||||
target: 'http://ai-api.uat.hivoice.cn',
|
||||
changeOrigin: true,
|
||||
logger: console,
|
||||
onProxyReq: (proxyReq, req, res) => {
|
||||
proxyReq.setHeader('appKey', 'k5hfiei5eevouvjohkapjaudpk2gakpaxha22fiy');
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user