实现思路
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 contextlibimport api
import mcrud
import amauth
import envenv.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.dataconsole.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>