理顺框架
This commit is contained in:
parent
62a323bdf9
commit
07347cba09
@ -1,3 +1,56 @@
|
|||||||
|
/* http://meyerweb.com/eric/tools/css/reset/
|
||||||
|
v5.0.1 | 20191019
|
||||||
|
License: none (public domain)
|
||||||
|
*/
|
||||||
|
|
||||||
|
html, body, div, span, applet, object, iframe,
|
||||||
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||||
|
a, abbr, acronym, address, big, cite, code,
|
||||||
|
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||||
|
small, strike, strong, sub, sup, tt, var,
|
||||||
|
b, u, i, center,
|
||||||
|
dl, dt, dd, ol, ul, li,
|
||||||
|
fieldset, form, label, legend,
|
||||||
|
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||||
|
article, aside, canvas, details, embed,
|
||||||
|
figure, figcaption, footer, header, hgroup,
|
||||||
|
main, menu, nav, output, ruby, section, summary,
|
||||||
|
time, mark, audio, video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure,
|
||||||
|
footer, header, hgroup, main, menu, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* HTML5 hidden-attribute fix for newer browsers */
|
||||||
|
*[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
menu, ol, ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
blockquote, q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
blockquote:before, blockquote:after,
|
||||||
|
q:before, q:after {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--max-width: 1100px;
|
--max-width: 1100px;
|
||||||
--border-radius: 12px;
|
--border-radius: 12px;
|
||||||
@ -73,12 +126,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
|
@ -85,7 +85,10 @@ export const LoginComponent = () => {
|
|||||||
{/* </div>*/}
|
{/* </div>*/}
|
||||||
{/*</div>*/}
|
{/*</div>*/}
|
||||||
</Modal>
|
</Modal>
|
||||||
{visible?'xxx':'0000'}
|
{visible ? 'xxx' : '0000'}
|
||||||
<Button type="primary" onClick={() => setVisible(true)}>点击登录</Button>
|
<Button type="primary" onClick={() => {
|
||||||
|
setVisible(true)
|
||||||
|
// Modal.success({ title: 'This is a success message', content: 'bla bla bla...' });
|
||||||
|
}}>点击登录</Button>
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ export const AppRouter = () => (<WebRouter>
|
|||||||
<div className="page-body-content">
|
<div className="page-body-content">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<DefaultPage/>}/>
|
<Route path="/" element={<DefaultPage/>}/>
|
||||||
|
|
||||||
<Route path="*" element={<NotFound/>}/>
|
<Route path="*" element={<NotFound/>}/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
.index{
|
.index {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
.modal{
|
.title{
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.loginWrapper {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-radius: 6px;
|
||||||
|
width: 350px;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,77 @@
|
|||||||
import React from "react";
|
import React, {useRef, useState} from "react";
|
||||||
|
import {IconGithubLogo, IconWeibo} from "@douyinfe/semi-icons";
|
||||||
import css from './index.module.scss'
|
import css from './index.module.scss'
|
||||||
import {LoginComponent} from "../../components/LoginComponent.tsx";
|
import {LoginComponent} from "../../components/LoginComponent.tsx";
|
||||||
|
import {useNavigate} from "react-router-dom";
|
||||||
|
import {useUserinfoStore} from "../../store/userinfoStore.ts";
|
||||||
|
import {Button, Divider, Form, Modal, Notification, Space} from "@douyinfe/semi-ui";
|
||||||
|
import {cx} from "@emotion/css";
|
||||||
|
|
||||||
const DefaultPage: React.FC = () => {
|
const DefaultPage: React.FC = () => {
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const {login} = useUserinfoStore()
|
||||||
|
|
||||||
|
const onFinish = async (values: any) => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const user = await login(values)
|
||||||
|
setVisible(true);
|
||||||
|
navigate('/dashboard')
|
||||||
|
} catch (err) {
|
||||||
|
// 登录失败
|
||||||
|
console.log(err)
|
||||||
|
// 登录失败
|
||||||
|
Notification.error({
|
||||||
|
content: '登录失败,请检查用户名和密码'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const hideModal = () => setVisible(false)
|
||||||
|
|
||||||
return (<div className={css.index}>
|
return (<div className={css.index}>
|
||||||
<LoginComponent/>
|
{/*<LoginComponent/>*/}
|
||||||
|
<div className={css.loginWrapper}>
|
||||||
|
<h2 className={cx('text-center', css.title)}>登录</h2>
|
||||||
|
<Form
|
||||||
|
onSubmit={onFinish}
|
||||||
|
>
|
||||||
|
<Form.Input
|
||||||
|
field="account"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
noLabel
|
||||||
|
rules={[{required: true, message: '用户名不可空'}]}
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
type="password"
|
||||||
|
field="password"
|
||||||
|
placeholder="请输入登录密码"
|
||||||
|
noLabel
|
||||||
|
rules={[{required: true, message: '密码不可空'}]}
|
||||||
|
/>
|
||||||
|
<Form.Checkbox
|
||||||
|
field="remember"
|
||||||
|
value={false}
|
||||||
|
noLabel
|
||||||
|
>下次自动登录</Form.Checkbox>
|
||||||
|
<div className='flex align-center space-between'>
|
||||||
|
<Button block type='primary' theme='solid' loading={loading} htmlType="submit">登录</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
<div style={{padding: '30px 0 20px'}}>
|
||||||
|
<Divider>OR</Divider>
|
||||||
|
<div className="text-center" style={{marginTop: 20}}>
|
||||||
|
<Space>
|
||||||
|
<Button icon={<IconWeibo/>}>GOOGLE</Button>
|
||||||
|
<Button theme='solid' type="primary" icon={<IconGithubLogo/>}>GITHUB</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
export default DefaultPage;
|
export default DefaultPage;
|
||||||
|
@ -35,7 +35,7 @@ Axios.interceptors.request.use(config => {
|
|||||||
const token = Storage.get<string>(APP_CONFIG.LOGIN_TOKEN_KEY)
|
const token = Storage.get<string>(APP_CONFIG.LOGIN_TOKEN_KEY)
|
||||||
// const {token} = useUserinfoStore()
|
// const {token} = useUserinfoStore()
|
||||||
if (token) {
|
if (token) {
|
||||||
config.headers['Authorization'] = `Bearer ${token}`;
|
config.headers['Authorization'] = `${token}`; // Bearer
|
||||||
}
|
}
|
||||||
if (config.data && config.data instanceof FormData) {
|
if (config.data && config.data instanceof FormData) {
|
||||||
config.headers['Content-Type'] = 'multipart/form-data';
|
config.headers['Content-Type'] = 'multipart/form-data';
|
||||||
@ -81,7 +81,7 @@ export function request<T>(url: string, method: RequestMethod, data: any = null,
|
|||||||
if (code == 403) {
|
if (code == 403) {
|
||||||
const state = useUserinfoStore.getState()
|
const state = useUserinfoStore.getState()
|
||||||
// 未登录 显示登录modal
|
// 未登录 显示登录modal
|
||||||
state.showLogin(true);
|
// state.showLogin(true);
|
||||||
}
|
}
|
||||||
reject(new BizError(msg, code))
|
reject(new BizError(msg, code))
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ import {get, post} from "./request.ts";
|
|||||||
import {UserModel} from "../../model";
|
import {UserModel} from "../../model";
|
||||||
|
|
||||||
|
|
||||||
export function login(user: string, password: string) {
|
export function login(username: string, password: string) {
|
||||||
return post<UserModel>('/api/user/info', {user, password})
|
return post<UserModel>('/api/user/login', {username, password})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInfo() {
|
export function getInfo() {
|
||||||
|
@ -74,8 +74,7 @@ export const useUserinfoStore = create<{
|
|||||||
},
|
},
|
||||||
login: (data) => {
|
login: (data) => {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const promise = login(data.account, data.password);
|
login(data.account, data.password).then((ret) => {
|
||||||
promise.then((ret) => {
|
|
||||||
const token = ret.token;
|
const token = ret.token;
|
||||||
Storage.put(LOGIN_TOKEN_KEY, token)
|
Storage.put(LOGIN_TOKEN_KEY, token)
|
||||||
getInfo().then(userinfo => {
|
getInfo().then(userinfo => {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { InitServerOption } from "./types";
|
import { InitServerOption } from "./types";
|
||||||
// import { createServer as createServerOrigin } from "http";
|
// import { createServer as createServerOrigin } from "http";
|
||||||
import express = require("express");
|
// import express = require("express");
|
||||||
|
import * as express from "express";
|
||||||
|
|
||||||
export function createServer(options: Partial<InitServerOption>, callback?: () => void) {
|
export function createServer(options: Partial<InitServerOption>, callback?: () => void) {
|
||||||
// 创建express服务器
|
// 创建express服务器
|
||||||
|
@ -2,7 +2,7 @@ import {Application, Request, Response} from "express";
|
|||||||
import {RouteHandleFunction, RouteHandleFunctionParam} from "../core/types";
|
import {RouteHandleFunction, RouteHandleFunctionParam} from "../core/types";
|
||||||
import {home} from "./home";
|
import {home} from "./home";
|
||||||
import {appList, reportToServer, appEvent, eventData} from "./reportor";
|
import {appList, reportToServer, appEvent, eventData} from "./reportor";
|
||||||
import {loginHandler} from "./user.ts";
|
import {getUserInfo, loginHandler} from "./user.ts";
|
||||||
|
|
||||||
//
|
//
|
||||||
function createRoute(handler: RouteHandleFunction) {
|
function createRoute(handler: RouteHandleFunction) {
|
||||||
@ -28,5 +28,6 @@ export function initRoutes(app: Application) {
|
|||||||
app.all('/api/app/list', createRoute(appList))
|
app.all('/api/app/list', createRoute(appList))
|
||||||
app.all('/api/app/event', createRoute(appEvent))
|
app.all('/api/app/event', createRoute(appEvent))
|
||||||
app.all('/api/app/event-data', createRoute(eventData))
|
app.all('/api/app/event-data', createRoute(eventData))
|
||||||
app.all('/api/login', createRoute(loginHandler))
|
app.all('/api/user/login', createRoute(loginHandler))
|
||||||
|
app.all('/api/user/info', createRoute(getUserInfo))
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createPool } from 'mysql'
|
import {createPool} from 'mysql'
|
||||||
import { DB_CONFIG } from './../config';
|
import {DB_CONFIG} from './../config';
|
||||||
|
|
||||||
export const pool = createPool({
|
export const pool = createPool({
|
||||||
...DB_CONFIG,
|
...DB_CONFIG,
|
||||||
@ -30,19 +30,23 @@ export async function selectArray<T>(sql: string, params: any = null) {
|
|||||||
// })
|
// })
|
||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询单个对象
|
// 查询单个对象
|
||||||
export async function selectOne<T>(sql: string, params: any = null) {
|
export async function selectOne<T>(sql: string, params: any = null) {
|
||||||
// 执行查询sql并返回查询结果
|
// 执行查询sql并返回查询结果
|
||||||
const arr = await selectArray<T>(sql, params);
|
const arr = await selectArray<T>(sql, params);
|
||||||
if (arr.length != 1) throw new Error("查询结果数量不等于1");
|
// if (arr.length != 1) throw new Error("查询结果数量不等于1");
|
||||||
return arr[0];
|
return arr.length > 0 ? arr[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计数据
|
// 统计数据
|
||||||
export async function queryCount(sql: string, params: any = null) {
|
export async function queryCount(sql: string, params: any = null) {
|
||||||
const obj = await selectOne<{ [key: string]: number }>(sql, params);
|
const obj = await selectOne<{ [key: string]: number }>(sql, params);
|
||||||
|
if(!obj) return 0;
|
||||||
const keys = Object.keys(obj);
|
const keys = Object.keys(obj);
|
||||||
return obj[keys[0]];
|
return obj[keys[0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据表名和条件判断数据是否存在
|
// 根据表名和条件判断数据是否存在
|
||||||
export async function isExistByTable(tableName: string, condition: any = {}) {
|
export async function isExistByTable(tableName: string, condition: any = {}) {
|
||||||
let sql = `select count(*) as count from ${tableName} where 1=1`;
|
let sql = `select count(*) as count from ${tableName} where 1=1`;
|
||||||
@ -54,10 +58,12 @@ export async function isExistByTable(tableName: string, condition: any = {}) {
|
|||||||
const count = await queryCount(sql, params);
|
const count = await queryCount(sql, params);
|
||||||
return count > 0;
|
return count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isExist(sql: string, params: any) {
|
export async function isExist(sql: string, params: any) {
|
||||||
const count = await queryCount(sql, params);
|
const count = await queryCount(sql, params);
|
||||||
return count > 0;
|
return count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询多条数据
|
// 查询多条数据
|
||||||
|
|
||||||
function executeSQL<T>(sql: string, params: any) {
|
function executeSQL<T>(sql: string, params: any) {
|
||||||
@ -76,10 +82,12 @@ export async function execute(sql: string, params: any = null) {
|
|||||||
const ret = await executeSQL<{ affectedRows: number }>(sql, params)
|
const ret = await executeSQL<{ affectedRows: number }>(sql, params)
|
||||||
return ret.affectedRows
|
return ret.affectedRows
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function insertAndGetInsertId(tableName: string, data: any = null) {
|
export async function insertAndGetInsertId(tableName: string, data: any = null) {
|
||||||
const ret = await executeSQL<{ insertId: number }>(`insert into \`${tableName}\` set ?`, data)
|
const ret = await executeSQL<{ insertId: number }>(`insert into \`${tableName}\` set ?`, data)
|
||||||
return ret.insertId
|
return ret.insertId
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
selectArray,
|
selectArray,
|
||||||
executeSQL,
|
executeSQL,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { execute, selectArray, pool, queryCount } from './../src/service/mysql'
|
import { execute, selectArray, pool, queryCount } from './../service/service/mysql'
|
||||||
|
|
||||||
// 使用execute执行更新数据
|
// 使用execute执行更新数据
|
||||||
describe('Database', () => {
|
describe('Database', () => {
|
||||||
@ -25,4 +25,4 @@ describe('Database', () => {
|
|||||||
const count = await queryCount('select count(*) from user');
|
const count = await queryCount('select count(*) from user');
|
||||||
console.log('total:', count);
|
console.log('total:', count);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Test } from 'supertest';
|
import {Test} from 'supertest';
|
||||||
import { PORT } from './../config'
|
import {PORT} from './../service/config'
|
||||||
|
|
||||||
const request = require("supertest");
|
const request = require("supertest");
|
||||||
|
|
||||||
// import { createServer } from './../src/core/server'
|
// import { createServer } from './../src/core/server'
|
||||||
@ -12,4 +13,4 @@ const request = require("supertest");
|
|||||||
// initRoutes(app)
|
// initRoutes(app)
|
||||||
|
|
||||||
export const createRequest = () => request(`http://localhost:${PORT}`)
|
export const createRequest = () => request(`http://localhost:${PORT}`)
|
||||||
export const get = (path: string): Test => createRequest().get(path)
|
export const get = (path: string): Test => createRequest().get(path)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
/* ESNext */
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
"module": "CommonJS",
|
"module": "commonjs",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
|
@ -12,11 +12,12 @@ export default defineConfig(({mode}) => {
|
|||||||
'@/': '/front/'
|
'@/': '/front/'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
define: {
|
// define: {
|
||||||
buildVersion: JSON.stringify((new Date()).toLocaleString()),
|
// buildVersion: JSON.stringify((new Date()).toLocaleString()),
|
||||||
mode:JSON.stringify(mode),
|
// mode:JSON.stringify(mode),
|
||||||
},
|
// },
|
||||||
build: {
|
build: {
|
||||||
|
outDir:'static',
|
||||||
// 小于10kb直接base64
|
// 小于10kb直接base64
|
||||||
assetsInlineLimit: 10240,
|
assetsInlineLimit: 10240,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user