本来想一口吃成个胖子,但发现埋头学的效果不佳(其实是学不会。。。redux、antdpro都看不懂。。。不得不感慨一句:学历如天谴那)。于是决定先用已有的知识体系把项目搭起来再说,边搭边学吧。另外,本人虽是python出身,也学过一定时间的基础知识,并有实际全职码python代码的工作经历,但对于flask框架其实也是一知半解,很多知识点并未深入,也得边写边学。第一次练习的项目,肯定有很多问题,欢迎指正。
antd的配置和使用必须参照官网,因为antd在不断迭代中,关键是不像下兼容,不按最新的配置方式来,用不了,所以必须严格按官网来。这里就不赘述了
现在最流行的调用后端接口使用的库就是axiox,但我们需要对其做一下简单封装,这样能更好的排查问题以及更好的使用。
import axios from 'axios'
import {message} from 'antd'
export default function ajax(url, data = {}, type = 'GET') {
return new Promise((resolve, reject) => {
let promise
// 1. 执行异步ajax请求
if (type === 'GET') { // 发GET请求
promise = axios.get(url, { // 配置对象
params: data // 指定请求参数
})
} else { // 发POST请求
promise = axios.post(url, data)
}
// 2. 如果成功了, 调用resolve(value)
promise.then(response => {
resolve(response.data)
// 3. 如果失败了, 不调用reject(reason), 而是提示异常信息
}).catch(error => {
message.error('请求出错了: ' + error.message)
})
})
}
/* 接口配置 */
import ajax from './ajax'
const BASE = '/api'
export const reqLogin = (username, password) => ajax(BASE + '/login', {username, password}, 'POST')
以后所有的后端接口,都放在该index中进行配置即可
代理的配置这里就不赘述,直接分享代码
const proxy = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
proxy('/api', {
target: 'http://localhost:5000',
changeOrigin: true,
pathRewrite: {'^/api': ''}
})
)
}
import React, {Component} from 'react';
import './App.less'
import Login from "./pages/user/Login";
import {BrowserRouter, Route, Switch} from "react-router-dom";
import Welcome from "./pages/Welcome";
class App extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route path='/login' component={Login}/>
<Route path='/' component={Welcome}/>
</Switch>
</BrowserRouter>
);
}
}
export default App;
import {Button, Checkbox, Form, Input, message} from 'antd';
import {LockOutlined, UserOutlined} from '@ant-design/icons';
import './index.less';
import React, {Component} from 'react';
import {Redirect} from "react-router-dom";
import {reqLogin} from "../../../api";
import memoryUtils from "../../../utils/memoryUtils";
import storageUtils from "../../../utils/storageUtils";
class Login extends Component {
onFinish = (values) => {
// 请求登陆
const {username, password} = values
reqLogin(username, password).then(result => {
console.log(result);
if (result.status === 1) { // 登陆成功
// 提示登陆成功
message.success('登陆成功')
// 保存user
const user = result.data;
memoryUtils.user = user // 保存在内存中
storageUtils.saveUser(user) // 保存到local中
// 跳转到管理界面 (不需要再回退回到登陆)
this.props.history.replace('/')
} else { // 登陆失败
// 提示错误信息
message.error(result.error)
}
})
}
render() {
// 如果用户已经登陆, 自动跳转到管理界面
const user = memoryUtils.user
if (user && user._id) {
return <Redirect to='/'/>
}
return (
<div className="container">
<div className="content">
<div className="top">
<div className="header">
<span className="title">smallfish platform</span>
</div>
<div className="desc">苏州太湖新城最酷的测试平台</div>
</div>
<div className="main">
<Form
name="normal_login"
className="login-form"
initialValues={{
remember: true,
}}
onFinish={this.onFinish}
>
<Form.Item
name="username"
rules={[
{min: 4, message: '用户名至少4位'},
{max: 20, message: '用户名最多20位'},
{pattern: /^[a-zA-Z0-9_]+$/, message: '用户名必须是英文、数字或下划线组成'},
{required: true, whitespace: true, message: '请输入用户名!'},
]}
>
<Input prefix={<UserOutlined className="site-form-item-icon"/>} placeholder="Username"
allowClear={true}/>
</Form.Item>
<Form.Item
name="password"
rules={[
{
required: true,
message: '请输入密码!',
},
]}
>
<Input.Password
prefix={<LockOutlined className="site-form-item-icon"/>}
type="password"
placeholder="Password"
/>
</Form.Item>
<Form.Item>
<Form.Item name="remember" valuePropName="checked" noStyle>
<Checkbox>记住密码</Checkbox>
</Form.Item>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="login-form-button">
登录
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
);
}
}
export default Login;
这个看着代码很多,其实登录组件是选用的antd组件库里的,可以根据自己的实际需求进行选配。
还是那句,antd再不断迭代中,V3/V4和现在V5使用的方法都在发生变更(其实如果你也是python出身,这个痛点是一直伴随着我们的,我也想说一声【坑那】)
现在antd表单提交的方法是onFinish,入参是表单提交的object
小知识点:message是antd内置的提示框,挺好用的
因为没学会redux,但我们是需要对用户信息进行保存的,要不然无法把控住用户的登录状态,所以自建cache管理仓库(哈哈哈,吹牛的啦,就是把一些关键信息进行本地保存,毕竟我们做的是小系统,自己知道存在哪儿,到时能取到即可)
所以,新建utils目录,下面先来两个对象保存变量
/*
用来在内存保存一些数据的工具模块
*/
const memoryUtils = {
user: {}, // 保存当前登陆的user
}
export default memoryUtils;
/*进行local数据存储管理的工具模块*/
import store from 'store'
const USER_KEY = 'user_key'
const storageUtils = {
/*保存user*/
saveUser(user) {
// localStorage.setItem(USER_KEY, JSON.stringify(user))
store.set(USER_KEY, user)
},
/*读取user*/
getUser() {
// return JSON.parse(localStorage.getItem(USER_KEY) || '{}')
return store.get(USER_KEY) || {}
},
/*删除user*/
removeUser() {
// localStorage.removeItem(USER_KEY)
store.remove(USER_KEY)
}
}
export default storageUtils;
import os
# 项目的绝对路径
PROJECTPATH = os.path.abspath('smallfish_platform')
配置肯定少不了,项目的路径先配置一下,后面再慢慢加
from properties.commonVar import LOCAL
def getMysqlConfig(env: object = LOCAL, serverName: object = '') -> object:
# 脚本运行依赖的数据库配置
if env == LOCAL:
db_config = {
'host': '127.0.0.1',
'user': 'root',
'passwd': '12345678',
'db': 'db_platform',
"port": 3306,
'charset': 'utf8'
}
return db_config
数据库不可能使用一个,LOCAL这个是我们运行环境的数据库,但作为平台,我们测试对象的数据库我们也是需要进行链接的,所以先写了个模型出来,后面可以追加配置
配置的各种变量参数,放置在这个里面
# 数据库服务器标识
LOCAL = 'run'
数据库的使用,当然可以直接写sql语句,不说low不low吧,效率极低,不信你可以进行测试,所以选用flask自带的flask_sqlalchemy
将mysql与app进行绑定
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config.mysqlConfig import getMysqlConfig
from properties.commonVar import LOCAL
app = Flask(__name__)
db_config_local = getMysqlConfig(LOCAL)
db_local = f"mysql+pymysql://{db_config_local['user']}:{db_config_local['passwd']}@{db_config_local['host']}:{db_config_local['port']}/{db_config_local['db']}?charset=utf8"
SQLALCHEMY_BINDS = {
LOCAL: db_local
}
# 配置默认数据库
app.config['SQLALCHEMY_DATABASE_URI'] = db_local
# 绑定多个数据库
app.config['SQLALCHEMY_BINDS'] = SQLALCHEMY_BINDS
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 将数据库绑定在flask,这样就可以直接通过flask对数据库进行操作
db = SQLAlchemy(app)
import hashlib
from models.database import db
from properties.commonVar import LOCAL
class TbUser(db.Model):
__tablename__ = 'tb_user'
__bind_key__ = LOCAL
__table_args__ = {'comment': '用户基础表'}
id = db.Column(db.INTEGER, primary_key=True, comment='唯一ID')
login_name = db.Column(db.String(255), nullable=False, comment='登录用户名')
password = db.Column(db.String(64), nullable=False, comment='登录密码')
status = db.Column(db.INTEGER, nullable=False, server_default=db.text("'1'"), comment='用户状态-0无效1有效')
token = db.Column(db.String(64), nullable=False, comment='用户token')
last_login_time = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP"),
comment='最后登录时间')
failure_time = db.Column(db.INTEGER, nullable=False, server_default=db.text("'0'"), comment='失效时间-时间戳')
create_time = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP"), comment='创建时间')
update_time = db.Column(db.DateTime, nullable=False,
server_default=db.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
comment='修改时间')
def check_password(self, password):
m = hashlib.md5()
b = password.encode(encoding='utf-8')
m.update(b)
str_md5 = m.hexdigest()
if self.password == str_md5:
return True
else:
return False
class TbUserInfo(db.Model):
__tablename__ = 'tb_user_info'
__bind_key__ = LOCAL
__table_args__ = {'comment': '用户信息表'}
id = db.Column(db.INTEGER, primary_key=True, comment='唯一ID')
user_id = db.Column(db.INTEGER, nullable=False, server_default=db.text("'0'"), comment='用户唯一标识')
user_name = db.Column(db.String(64), nullable=False, comment='用户姓名')
user_role = db.Column(db.String(64), nullable=False, comment='用户岗位')
user_desc = db.Column(db.String(1024), nullable=False, comment='用户详情')
create_time = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP"), comment='创建时间')
update_time = db.Column(db.DateTime, nullable=False,
server_default=db.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
comment='修改时间')
if __name__ == '__main__':
# 验证过,执行多次并不会删除已有数据,可以批量执行
# db.create_all(bind=[LOCAL])
password = '123456'
m = hashlib.md5()
b = password.encode(encoding='utf-8')
m.update(b)
str_md5 = m.hexdigest()
print(str_md5)
这个就是创建了两张user表,password做了加密处理
这里就是对外的接口了
from flask import jsonify, request
from sqlalchemy import and_
from models.database import app
from models.local.db_platform import TbUser
@app.route('/login', methods=['POST'])
def login():
if not request.json or 'username' not in request.json or 'password' not in request.json:
return jsonify({"status": 0, "error": "入参错误"})
username: str = request.json.get("username").strip()
password: str = request.json.get("password").strip()
users: list = TbUser.query.filter(
and_(TbUser.login_name == username, TbUser.status == 1)).all()
if len(users) == 0:
return jsonify({"status": 0, "error": "没有该用户"})
elif len(users) > 1:
return jsonify({"status": 0, "error": f"用户:{username},重名了"})
user: TbUser = users[0]
if user.check_password(password):
return jsonify({"status": 1})
else:
return jsonify({"status": 0, "error": "密码错误"})
if __name__ == '__main__':
app.run(debug=True)
# login()
flask-sqlalchemy这个库是依赖于sqlalchemy库的,所以也得安装sqlalchemy