diff --git a/package.json b/package.json
index 423aa96..5b90753 100644
--- a/package.json
+++ b/package.json
@@ -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": {
diff --git a/public/index.html b/public/index.html
index aa069f2..85173bf 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,43 +1,43 @@
-
-
-
-
-
-
-
-
-
-
-
-
- React App
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ 纽曼AI语记
+
+
+
+
+
+
+
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 0000000..13be521
Binary files /dev/null and b/public/logo.png differ
diff --git a/public/logo512.png b/public/logo512.png
deleted file mode 100644
index a4e47a6..0000000
Binary files a/public/logo512.png and /dev/null differ
diff --git a/public/manifest.json b/public/manifest.json
index 080d6c7..62edef1 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -11,11 +11,6 @@
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
- },
- {
- "src": "logo512.png",
- "type": "image/png",
- "sizes": "512x512"
}
],
"start_url": ".",
diff --git a/src/LoginPage.js b/src/LoginPage.js
index 41e2048..c12a105 100644
--- a/src/LoginPage.js
+++ b/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 (
-
+
+
+
+
纽曼AI语记
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
\ No newline at end of file
diff --git a/src/LoginPage.module.css b/src/LoginPage.module.css
index eb5b217..c94deee 100644
--- a/src/LoginPage.module.css
+++ b/src/LoginPage.module.css
@@ -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;
diff --git a/src/assets/background@2x.png b/src/assets/background@2x.png
new file mode 100644
index 0000000..6e11d7e
Binary files /dev/null and b/src/assets/background@2x.png differ
diff --git a/src/assets/logo.png b/src/assets/logo.png
new file mode 100644
index 0000000..13be521
Binary files /dev/null and b/src/assets/logo.png differ
diff --git a/src/assets/logo@2x.png b/src/assets/logo@2x.png
new file mode 100644
index 0000000..902c038
Binary files /dev/null and b/src/assets/logo@2x.png differ
diff --git a/src/business/ipSlice.js b/src/business/ipSlice.js
new file mode 100644
index 0000000..9f58ffb
--- /dev/null
+++ b/src/business/ipSlice.js
@@ -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
\ No newline at end of file
diff --git a/src/business/request.js b/src/business/request.js
new file mode 100644
index 0000000..9474310
--- /dev/null
+++ b/src/business/request.js
@@ -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;
\ No newline at end of file
diff --git a/src/business/store.js b/src/business/store.js
new file mode 100644
index 0000000..725f359
--- /dev/null
+++ b/src/business/store.js
@@ -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,
+ }
+})
\ No newline at end of file
diff --git a/src/business/userSlice.js b/src/business/userSlice.js
new file mode 100644
index 0000000..1eb6d33
--- /dev/null
+++ b/src/business/userSlice.js
@@ -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
\ No newline at end of file
diff --git a/src/components/DynamicCodeForm.js b/src/components/DynamicCodeForm.js
new file mode 100644
index 0000000..e17d31c
--- /dev/null
+++ b/src/components/DynamicCodeForm.js
@@ -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
+
+
+
+
+ ),
+ }}
+ />
+
+
+
+ ),
+
+ endAdornment:
+ 发送动态码
+
+ }}
+ />
+
+
+
+
+ 同意 《纽曼隐私协议》
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/components/PasswordForm.js b/src/components/PasswordForm.js
new file mode 100644
index 0000000..34e4d17
--- /dev/null
+++ b/src/components/PasswordForm.js
@@ -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
+
+
+
+ ),
+ }}
+ />
+
+
+
+ ),
+ }}
+ />
+
+
+}
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 52a3c75..3382ffc 100644
--- a/src/index.js
+++ b/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(
-
-
-
+
+
+
+
+
);
diff --git a/src/setupProxy.js b/src/setupProxy.js
new file mode 100644
index 0000000..4cbd576
--- /dev/null
+++ b/src/setupProxy.js
@@ -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');
+ },
+ })
+ );
+};
\ No newline at end of file