实现思路
1、准备zdppy的开发环境
2、使用amauth提供的低代码接口,直接生成login登录接口
3、使用之前开发的登录模板渲染登录界面
4、给登录按钮绑定点击事件
5、给用户名和密码的输入框双向绑定数据
6、使用axios在登录按钮点击的时候,携带用户数据发送POST登录请求
7、处理登录接口的响应
完整代码
后端代码
.env
ZDPPY_MCRUD_HOST=127.0.0.1
ZDPPY_MCRUD_PORT=3306
ZDPPY_MCRUD_USERNAME=root
ZDPPY_MCRUD_PASSWORD=zhangdapeng520
ZDPPY_MCRUD_DATABASE=zdppy_demo
main.py
import contextlib
import api
import mcrud
import amauth
import env
env.load(".env")
db = mcrud.new_env()
amauth.data.init(db, is_init_role=True, is_init_auth=True)
@contextlib.asynccontextmanager
async def lifespan(app):
yield {"db": db}
app = api.Api(
routes=[
api.resp.post("/login", amauth.user.login),
],
middleware=[
api.middleware.cors(),
],
lifespan=lifespan,
)
if __name__ == '__main__':
app.run()
前端代码
package.json
{
"name": "tailwindcss_demo",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"ant-design-vue": "^4.2.3",
"axios": "^1.7.2",
"dayjs": "^1.11.11",
"vue": "^3.4.29",
"vue-router": "^4.4.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.4",
"vite": "^5.3.1"
}
}
vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
})
tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: ["./src/**/*.{html,js,vue}"],
theme: {
extend: {},
},
plugins: [],
}
postcss.config.js
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
index.html
<!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 + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
src/main.js
import {createApp} from 'vue'
import './style.css'
import 'ant-design-vue/dist/reset.css';
import Antd from 'ant-design-vue';
import App from './App.vue';
const app = createApp(App)
app.use(Antd)
app.mount('#app')
src/style.css
@tailwind base;
@tailwind components;
@tailwind utilities;
src/App.vue
<script setup>
import {message} from "ant-design-vue";
import {ref} from "vue";
import axios from "axios";
const username = ref("")
const password = ref("")
const onLoginButtonClick = () => {
axios.post('http://127.0.0.1:8888/login', {
username: username.value,
password: password.value
})
.then(function (response) {
message.success("login success")
console.log("response=", response)
const data = response.data.data
console.log("data=", data)
console.log("token=", data.token)
})
.catch(function (error) {
console.log(error);
message.error(error)
});
}
</script>
<template>
<section>
<span v-for="i in 297" :key="i"></span>
<div class="signin">
<div class="content">
<h2>用户登录</h2>
<div class="form">
<div class="inputBox">
<input type="text" v-model="username"> <i>账号</i>
</div>
<div class="inputBox">
<input type="password" v-model="password"> <i>密码</i>
</div>
<div class="links">
<a href="#">忘记密码</a>
<a href="#">注册</a>
</div>
<div class="inputBox">
<input type="submit" value="立即登录" @click.prevent="onLoginButtonClick">
</div>
</div>
</div>
</div>
</section>
</template>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Quicksand', sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #000;
}
section {
position: absolute;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
gap: 2px;
flex-wrap: wrap;
overflow: hidden;
}
section::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(#000, #0f0, #000);
animation: animate 5s linear infinite;
}
@keyframes animate {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(100%);
}
}
section span {
position: relative;
display: block;
width: calc(6.25vw - 2px);
height: calc(6.25vw - 2px);
background: #181818;
z-index: 2;
transition: 1.5s;
}
section span:hover {
background: #0f0;
transition: 0s;
}
section .signin {
position: absolute;
width: 400px;
background: #222;
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
padding: 40px;
border-radius: 4px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 9);
}
section .signin .content {
position: relative;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 40px;
}
section .signin .content h2 {
font-size: 2em;
color: #0f0;
text-transform: uppercase;
}
section .signin .content .form {
width: 100%;
display: flex;
flex-direction: column;
gap: 25px;
}
section .signin .content .form .inputBox {
position: relative;
width: 100%;
}
section .signin .content .form .inputBox input {
position: relative;
width: 100%;
background: #333;
border: none;
outline: none;
padding: 25px 10px 7.5px;
border-radius: 4px;
color: #fff;
font-weight: 500;
font-size: 1em;
}
section .signin .content .form .inputBox i {
position: absolute;
left: 0;
padding: 15px 10px;
font-style: normal;
color: #aaa;
transition: 0.5s;
pointer-events: none;
}
.signin .content .form .inputBox input:focus ~ i,
.signin .content .form .inputBox input:valid ~ i {
transform: translateY(-7.5px);
font-size: 0.8em;
color: #fff;
}
.signin .content .form .links {
position: relative;
width: 100%;
display: flex;
justify-content: space-between;
}
.signin .content .form .links a {
color: #fff;
text-decoration: none;
}
.signin .content .form .links a:nth-child(2) {
color: #0f0;
font-weight: 600;
}
.signin .content .form .inputBox input[type="submit"] {
padding: 10px;
background: #0f0;
color: #000;
font-weight: 600;
font-size: 1.35em;
letter-spacing: 0.05em;
cursor: pointer;
}
input[type="submit"]:active {
opacity: 0.6;
}
@media (max-width: 900px) {
section span {
width: calc(10vw - 2px);
height: calc(10vw - 2px);
}
}
@media (max-width: 600px) {
section span {
width: calc(20vw - 2px);
height: calc(20vw - 2px);
}
}
</style>
<script setup lang="ts">
</script>