⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:项目。
1、前言
本文介绍的是用python实现的前后端学生管理系统
,前后端分离
,较为简单易上手。使用的技术点为:HTML+CSS+JavaScript+Flask+sqlite+Vue+Axios。
如果想了解前后端不分离的情况,请看我这篇文章:
python实现前后端学生管理系统(前后端不分离)
2、思路总览
下面是关于实现的功能的总览:
首页
①管理系统的首页:(如有需求,可以自行使用前端框架美化)
其中:
注册和登陆是链接:
注册链接到注册功能页面
登陆链接到登陆功能页面
注册
②注册
注册功能页面,注册功能页面自定义,需要包含如下注册信息并对输入内容进行校验:
注册信息 | 验证规则 |
---|---|
姓名 | 姓名缩写,不能为空 |
学号 | 数字类型,不能为空 |
学院 | 不能为空 |
专业 | 不能为空 |
班级 | 不能为空 |
年龄 | 17-22间的任意数字,数字类型,不能为空 |
电话 | 数字类型,不能为空 |
邮件格式 | |
用户名 | 字母大小写、数字、下划线四种类型至少包含三种,字母开始,长度至少6位 |
密码 | 字母大小写、数字、下划线四种类型至少包含三种,长度至少8位 |
确认密码 | 重复密码 |
基本流程:验证成功后,这些注册信息都需要写入sqlite的数据库stu.db的students表中(该表需要先单独创建,字段为上面的注册信息,名字自定义,类型要满足需求,另外用户名设置为主键,这样表中的用户名取值就不能相同)。然后跳转到注册成功页面,该页面有返回首页链接。
登录
③登录
登陆功能页面,该页面需要输入用户名和密码,输入的用户名和密码与students表中已有记录进行比对,若用户名和对应密码在表中存在则跳转到登陆成功页面,否则提示用户名不存在或密码错误,不跳转。登陆成功页面直接以表格方式显示自己注册的所有信息,如下:
管理员
④管理员账号
创建一个admin表,字段包括管理员名和密码,自己通过sql语句插入三条记录,即创建三个管理员账号。
网站有个路径为/admin,供管理员账号登陆的页面:
登录之后展示全部信息:
3、详细代码
3.1、项目目录
3.2、static
static文件夹需要导入两个框架,一个是vue.js,一个属axios.js(这个是本项目实现前后端分离的关键:发送异步请求)
需要这两个框架文件可以点赞收藏加关注,私聊我发给你。
3.3、templates
3.3.1、01.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>xzl</title>
<style>
h1 {color:black}
a:link {color:#9932CC;align-items:center}
</style>
</head>
<body>
<div id="app">
<br>
<h1 style="font-size:60px">欢迎访问本站</h1><br>
<h1>
<a href="/newStudent" @click="register">注册</a><br><br>
<a href="/enter" @click="login">登录</a><br>
</h1>
</div>
</body>
</html>
3.3.2、02.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>xzl</title>
<style>
</style>
</head>
<body>
<div id="app" style="text-align:center;">
<h1>用户登录</h1><br>
<form @submit.prevent="login">
用户名: <input type="text" v-model="username" name="user"><br><br>
密码: <input type="password" v-model="password" name="pwd"><br><br>
<input type="submit" value="登录" /><br><br>
</form>
<a href="/" style="color:#3CBC8D;">返回首页</a>
</div>
<script src="static/js/vue.js"></script>
<script src="static/js/axios-0.18.0.js"></script>
<script>
new Vue({
el: '#app',
data: {
username: '',
password: '',
message: ''
},
methods: {
login() {
axios.post('/check', {
username: this.username,
password: this.password
})
.then(response => {
this.message = response.data.message;
if (response.data.redirect) {
alert(this.message)
// 进行页面跳转
window.loc1ation.href = response.data.redirect;
}else {
alert(this.message)
}
})
.catch(error => {
console.error(error);
// 在这里处理请求错误
});
}
}
});
</script>
</body>
</html>
3.3.3、03.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>xzl</title>
<script>
function submitForm(event) {
event.preventDefault();
const name = document.getElementById('name').value;
const num = document.getElementById('number').value;
const academy = document.getElementById('academy').value;
const major = document.getElementById('major').value;
const Class = document.getElementById('Class').value;
const age = document.getElementById('age').value;
const phone = document.getElementById('phone-number').value;
const email = document.getElementById('email').value;
const user = document.getElementById('user').value;
const pwd = document.getElementById('password').value;
const repwd = document.getElementById('surePassword').value;
if (pwd !== repwd) {
document.getElementById('message').textContent = '两次密码不一致';
return;
}
const data = {
name: name,
num: num,
academy: academy,
major: major,
Class: Class,
age: age,
phone: phone,
email: email,
user: user,
pwd: pwd
};
fetch('/addrec', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
if (result.success) {
alert(result.message);
document.getElementById('message').textContent = '';
document.getElementById('name').value = '';
document.getElementById('number').value = '';
document.getElementById('academy').value = '';
document.getElementById('major').value = '';
document.getElementById('Class').value = '';
document.getElementById('age').value = '';
document.getElementById('phone-number').value = '';
document.getElementById('email').value = '';
document.getElementById('user').value = '';
document.getElementById('password').value = '';
document.getElementById('surePassword').value = '';
} else {
document.getElementById('message').textContent = result.message + ' ' + result.reason;
}
})
.catch(error => {
console.error('Error:', error);
document.getElementById('message').textContent = '请求失败';
});
}
var flag = false;
function checkName() {
var name = document.getElementById("name").value;
if (name.length == 0 || name == null) {
document.getElementById("name-error").innerHTML = "名字不能为空";
flag = false;
}else {
document.getElementById("name-error").innerHTML = "";
flag=true;
}
}
function checkNumber() {
var number = document.getElementById("number").value;
if (number.length == 0 || number == null) {
document.getElementById("number-error").innerHTML = "学号不能为空";
flag = false;
} else {
var regex = /^\d+$/;
if (!regex.test(number)) {
document.getElementById("number-error").innerHTML = "学号必须是数字";
flag = false;
} else {
document.getElementById("number-error").innerHTML = "";
flag=true;
}
}
}
function checkAcademy() {
var academy = document.getElementById("academy").value;
if (academy.length == 0 || academy == null) {
document.getElementById("academy-error").innerHTML = "学院不能为空";
flag = false;
} else {
document.getElementById("academy-error").innerHTML = "";
flag=true;
}
}
function checkMajor() {
var flag = false;
var major = document.getElementById("major").value;
if (major.length == 0 || major == null) {
document.getElementById("major-error").innerHTML = "专业不能为空";
flag = false;
} else {
document.getElementById("major-error").innerHTML = "";
flag=true;
}
}
function checkCLASS() {
var classValue = document.getElementById("Class").value;
if (classValue.length == 0 || classValue == null) {
document.getElementById("Class-error").innerHTML = "班级不能为空";
flag = false;
} else {
document.getElementById("Class-error").innerHTML = "";
flag=true;
}
}
function checkAge() {
var age = document.getElementById("age").value;
if (age.length == 0 || age == null) {
document.getElementById("age-error").innerHTML = "年龄不能为空";
flag = false;
} else if (isNaN(age)) {
document.getElementById("age-error").innerHTML = "年龄必须为数字";
flag = false;
} else if (age < 17 || age > 22) {
document.getElementById("age-error").innerHTML = "年龄必须在17-22之间";
flag = false;
} else {
document.getElementById("age-error").innerHTML = "";
flag=true;
}
}
function checkPhoneNumber() {
var phoneNumber = document.getElementById("phone-number").value;
if (phoneNumber.length == 0 || phoneNumber == null) {
document.getElementById("phone-number-error").innerHTML = "电话号码不能为空";
flag = false;
} else if (isNaN(phoneNumber)) {
document.getElementById("phone-number-error").innerHTML = "电话号码必须为数字";
flag = false;
} else {
document.getElementById("phone-number-error").innerHTML = "";
flag = true;
}
}
function checkEmail() {
var email = document.getElementById("email").value;
if (email.length == 0 || email == null) {
document.getElementById("email-error").innerHTML = "邮箱不能为空";
flag = false;
} else {
var regex = /\S+@\S+\.\S+/;
if (!regex.test(email)) {
document.getElementById("email-error").innerHTML = "邮箱格式有误";
flag = false;
} else {
document.getElementById("email-error").innerHTML = "";
flag=true;
}
}
}
function checkUser() {
var user = document.getElementById("user").value;
if (user.length < 6) {
document.getElementById("user-error").innerHTML = "用户名长度不能少于6位";
flag = false;
} else {
var regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d|_)[a-zA-Z\d_]{6,}$/;
if (!regex.test(user)) {
document.getElementById("user-error").innerHTML = "用户名必须包含字母大小写、数字、下划线四种类型中的至少三种";
flag = false;
} else {
document.getElementById("user-error").innerHTML = "";
flag=true;
}
}
}
function checkPassword() {
var password = document.getElementById("password").value;
if (password.length < 8) {
document.getElementById("password-error").innerHTML = "密码长度不能少于8个字符";
flag = false;
} else {
var regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d|_)[a-zA-Z\d_]{8,}$/;
if (!regex.test(password)) {
document.getElementById("password-error").innerHTML = "密码必须包含字母大小写、数字、下划线四种类型中的至少三种";
flag = false;
} else {
document.getElementById("password-error").innerHTML = "";
flag = true;
}
}
}
function checkSurePassword() {
var password1 = document.getElementById("password").value;
var password2 = document.getElementById("surePassword").value;
if(password1!=password2){
document.getElementById("surePassword-error").innerHTML = "两次密码不一致";
flag = false;
}else {
document.getElementById("surePassword-error").innerHTML = "";
flag=true;
}
}
function checkForm() {
var nameValid = checkName();
var numberValid = checkNumber();
var academyValid = checkAcademy();
var majorValid = checkMajor();
var classValid = checkCLASS();
var ageValid = checkAge();
var phoneNumberValid = checkPhoneNumber();
var emailValid = checkEmail();
var userValid = checkUser();
var passwordValid = checkPassword();
var surePasswordValid = checkSurePassword();
if(flag){
alert("提交成功")
return true;
}else{
alert("提交失败")
return false;
}
}
</script>
</head>
<body>
<h1>欢迎注册</h1>
<form onsubmit="submitForm(event)">
<h3>请填写你的信息</h3>
<h4>
姓名: <input type="text" name="name" id="name" onblur="checkName()">
<span id="name-error" style="color: red;"></span><br>
学号: <input type="text" name="num" id="number" onblur="checkNumber()">
<span id="number-error" style="color: red;"></span><br>
学院: <input type="text" name="academy" id="academy" onblur="checkAcademy()">
<span id="academy-error" style="color: red;"></span><br>
专业: <input type="text" name="major" id="major" onblur="checkMajor()">
<span id="major-error" style="color: red;"></span><br>
班级: <input type="text" name="Class" id="Class" onblur="checkCLASS()">
<span id="Class-error" style="color: red;"></span><br>
年龄: <input type="text" name="age" id="age" onblur="checkAge()">
<span id="age-error" style="color: red;"></span><br>
电话: <input type="text" name="phone" id="phone-number" onblur="checkPhoneNumber()">
<span id="phone-number-error" style="color: red;"></span><br>
Email: <input type="text" name="email" id="email" onblur="checkEmail()">
<span id="email-error" style="color: red;"></span><br>
用户名: <input type="text" name="user" id="user" onblur="checkUser()">
<span id="user-error" style="color: red;"></span><br>
密码: <input type="password" name="pwd" id="password" onblur="checkPassword()">
<span id="password-error" style="color: red;"></span><br>
确认密码: <input type="password" name="repwd" id="surePassword" onblur="checkSurePassword()">
<span id="surePassword-error" style="color: red;"></span><br>
</h4>
<input type="submit" value="提交">
<span id="message" style="color: red;"></span><br>
<h3><a href="/" style="color:#3CBC8D;">返回首页</a></h3>
</form>
</body>
</html>
3.3.4、04.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>xzl</title>
<style>
#customers
{
width:88%;
border-collapse:collapse;
margin: auto;
}
th {
width:200px;
font-size: 1.1em;
text-align: left;
padding-top: 5px;
padding-bottom: 4px;
background-color: blue;
color: #ffffff;
white-space: nowrap;
}
td
{
border:1px solid #98bf21;
}
</style>
</head>
<body>
<div style="text-align:center;">
<h1>学生信息表</h1><br>
<table id="students-table">
<thead>
<th>name</th>
<th>num</th>
<th>academy</th>
<th>major</th>
<th>Class</th>
<th>age</th>
<th>phone</th>
<th>email</th>
<th>user</th>
</thead>
<tbody>
</tbody>
</table>
<br>
<a href="/" style="color:#3CBC8D;">返回首页</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
var user = '{{ user }}';
axios.get('/List',{
params:{
user : user
}
})
.then(function(response) {
var student = response.data; // 获取学生信息数组
var tableBody = document.querySelector('#students-table tbody');
//students.forEach(function(student) {
var row = '<tr>' +
'<td>' + student.name + '</td>' +
'<td>' + student.num + '</td>' +
'<td>' + student.academy + '</td>' +
'<td>' + student.major + '</td>' +
'<td>' + student.Class + '</td>' +
'<td>' + student.age + '</td>' +
'<td>' + student.phone + '</td>' +
'<td>' + student.email + '</td>' +
'<td>' + student.user + '</td>' +
'</tr>';
tableBody.innerHTML += row;
//});
})
.catch(function(error) {
console.log(error);
});
});
</script>
</body>
</html>
3.3.5、05.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>xzl</title>
<style>
</style>
</head>
<body>
<div id="app" style="text-align:center;">
<h1>管理员登录</h1><br>
<form @submit.prevent="loginAdmin">
用户名: <input type="text" v-model="username" name="user"><br><br>
密码: <input type="password" v-model="password" name="pwd"><br><br>
<input type="submit" value="登录" /><br><br>
</form>
<a href="/" style="color:#3CBC8D;">返回首页</a>
</div>
<script src="static/js/vue.js"></script>
<script src="static/js/axios-0.18.0.js"></script>
<script>
new Vue({
el: '#app',
data: {
username: '',
password: '',
message: ''
},
methods: {
loginAdmin() {
axios.post('/checkAdmin', {
username: this.username,
password: this.password
})
.then(response => {
this.message = response.data.message;
if (response.data.redirect) {
alert(this.message)
// 进行页面跳转
window.location.href = response.data.redirect;
}else {
alert(this.message)
}
})
.catch(error => {
console.error(error);
// 在这里处理请求错误
});
}
}
});
</script>
</body>
</html>
3.3.6、06.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>xzl</title>
<style>
#customers
{
width:98%;
border-collapse:collapse;
margin: auto;
}
th {
width:200px;
font-size: 1.1em;
text-align: left;
padding-top: 5px;
padding-bottom: 4px;
background-color: #ADD8E6;
color: #ffffff;
white-space: nowrap;
}
td
{
border:1px solid #98bf21;
}
</style>
</head>
<body>
<div style="text-align:center;">
<h1>管理员权限:学生信息表</h1><br>
<table id="students-table-admin">
<thead>
<th>姓名</th>
<th>学号</th>
<th>学院</th>
<th>专业</th>
<th>班级</th>
<th>年龄</th>
<th>电话</th>
<th>邮箱</th>
<th>用户名</th>
<th>密码</th>
<th>delete</th>
</thead>
<tbody>
</tbody>
</table>
<br>
<a href="/" style="color:#3CBC8D;">返回首页</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
axios.get('/listAdmin',{
})
.then(function(response) {
var students = response.data.students; // 获取学生信息数组
var tableBody = document.querySelector('#students-table-admin tbody');
students.forEach(function(student) {
var row = '<tr>' +
'<td>' + student.name + '</td>' +
'<td>' + student.num + '</td>' +
'<td>' + student.academy + '</td>' +
'<td>' + student.major + '</td>' +
'<td>' + student.Class + '</td>' +
'<td>' + student.age + '</td>' +
'<td>' + student.phone + '</td>' +
'<td>' + student.email + '</td>' +
'<td>' + student.user + '</td>' +
'<td>' + student.pwd + '</td>' +
'<td><a href="/delete/' + student.user + '">删除</a></td>' +
'</tr>';
tableBody.innerHTML += row;
});
})
.catch(function(error) {
console.log(error);
});
});
</script>
</body>
</html>
3.4、sqlite.py
sqlite.py文件的代码是用来生成数据库表单和管理员账号密码的:
import sqlite3 as sql
conn=sql.connect('database.db')
print("opened database successfully")
conn.execute('CREATE TABLE students(name TEXT,\
num INT,\
academy TEXT,\
major TEXT,\
Class TEXT,\
age INT,\
phone INT,\
email TEXT,\
user TEXT PRIMARY KEY,\
pwd TEXT)')
conn.execute('CREATE TABLE admins(admin TEXT PRIMARY KEY,pwd TEXT)')
cur=conn.cursor()
cur.execute("INSERT INTO admins (admin,pwd) VALUES('xzl1','xzl1')")
cur.execute("INSERT INTO admins (admin,pwd) VALUES('xzl2','xzl2')")
cur.execute("INSERT INTO admins (admin,pwd) VALUES('xzl3','xzl3')")
conn.commit()
print("Table created successfully")
conn.close()
运行成功之后,会在代码文件的同目录下生成database.db数据库文件:文章来源:https://www.toymoban.com/news/detail-554917.html
3.5、flaskAxios.py
这个是该项目的后端服务器代码,基于flask实现。类似于Java的注解web开发。其中相较于之前发布过的前后端不分离的版本,做出了一些改动,比如前后端交互的数据格式改为json,前后端响应json之后再去响应。文章来源地址https://www.toymoban.com/news/detail-554917.html
# -*- coding: utf-8 -*-
# @Author:︶ㄣ释然
# @Time: 2023/6/19 9:25
from flask import Flask, render_template, request, redirect, url_for, jsonify
import sqlite3 as sql
app = Flask(__name__)
@app.route('/')
def home():
return render_template('01.html')
@app.route('/newStudent')
def new_student():
return render_template('03.html')
@app.route('/enter')
def enter():
return render_template('02.html')
@app.route('/check', methods=['POST'])
def check():
if request.method == 'POST':
date = request.get_json()
# user = request.form.get('user')
# pwd = request.form.get('pwd')
user = date['username']
pwd = date['password']
with sql.connect("database.db") as con:
cur = con.cursor()
cur.execute('select pwd from students where user = ?', (user,))
psw = cur.fetchall()
print("psw:", psw)
if not psw:
msg = "用户名错误"
print(msg)
message = {'message': msg}
return jsonify(message)
elif psw[0][0] == pwd:
msg = "登录成功"
print(msg)
return jsonify({'message': msg, 'redirect': url_for('toHtml', user=user)})
else:
msg = "密码错误"
print(msg)
return jsonify({'message': msg})
@app.route('/addrec', methods=['POST'])
def addrec():
try:
name = request.json['name']
num = request.json['num']
academy = request.json['academy']
major = request.json['major']
Class = request.json['Class']
age = request.json['age']
phone = request.json['phone']
email = request.json['email']
user = request.json['user']
pwd = request.json['pwd']
with sql.connect("database.db") as con:
cur = con.cursor()
cur.execute("INSERT INTO students (name, num, academy, major, Class, age, phone, email, user, pwd) \
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(name, num, academy, major, Class, age, phone, email, user, pwd))
con.commit()
return jsonify({'success': True, 'message': '增加记录成功'})
except sql.IntegrityError:
return jsonify({'success': False, 'message': '增加记录失败', 'reason': '可能原因为用户名重复'})
except Exception as e:
return jsonify({'success': False, 'message': '增加记录失败', 'reason': str(e)})
# @app.route('/List/<user>')
# def List(user):
# con =sql.connect("templates/database.db")
# con.row_factory=sql.Row
# cur=con.cursor()
# cur.execute('select name,num,academy,major,Class,age,phone,email,user,pwd from students where user=(?)',(user,))
# rows=cur.fetchall()
# return render_template("testxzl04.html",rows=rows)
@app.route('/toHtml/<user>')
def toHtml(user):
return render_template("04.html", user=user)
@app.route('/toHtmlAdmin')
def toHtmlAdmin():
return render_template("06.html")
@app.route('/List')
def List():
user = request.args.get('user')
con = sql.connect("database.db")
con.row_factory = sql.Row
cur = con.cursor()
cur.execute('SELECT name, num, academy, major, Class, age, phone, email, user, pwd FROM students WHERE user = ?', (user,))
rows = cur.fetchall()
students = []
print(rows)
for row in rows:
student = {
'name': row['name'],
'num': row['num'],
'academy': row['academy'],
'major': row['major'],
'Class': row['Class'],
'age': row['age'],
'phone': row['phone'],
'email': row['email'],
'user': row['user']
}
students.append(student)
print(students[0])
# return redirect(url_for('show_students', students_json=students))
return jsonify(students[0])
@app.route('/students')
def show_students():
students_json = request.args.get('students_json')
print(students_json)
return render_template('04.html', students_json=students_json)
# return jsonify(students_json)
@app.route('/listAdmin')
def listAdmin():
con = sql.connect("database.db")
con.row_factory = sql.Row
cur = con.cursor()
cur.execute('select name,num,academy,major,Class,age,phone,email,user,pwd from students ')
rows = cur.fetchall()
students = []
print(rows)
for row in rows:
student = {
'name': row['name'],
'num': row['num'],
'academy': row['academy'],
'major': row['major'],
'Class': row['Class'],
'age': row['age'],
'phone': row['phone'],
'email': row['email'],
'user': row['user'],
'pwd': row['pwd']
}
students.append(student)
print(students)
# return render_template("testxzl06.html", rows=rows)
jsonData = {'students': students}
print(jsonData)
return jsonify(jsonData)
@app.route('/delete/<user>')
def delete(user):
try:
with sql.connect("database.db") as con:
cur = con.cursor()
cur.execute('delete from students where user=(?)', (user,))
con.commit()
print("删除记录成功")
msg = "删除记录成功"
# return redirect(url_for('listAdmin', msg=msg))
return render_template("06.html")
except:
con.rollback()
print("删除记录失败")
msg = "删除记录失败"
# return redirect(url_for('listAdmin', msg=msg))
return render_template("06.html")
finally:
con.close()
@app.route('/admin')
def admin():
return render_template('05.html')
@app.route('/checkAdmin', methods=['POST'])
def checkAdmin():
if request.method == 'POST':
data = request.get_json()
user = data['username']
pwd = data['password']
# user = request.form['user']
# pwd = request.form['pwd']
with sql.connect("database.db") as con:
cur = con.cursor()
cur.execute('select pwd from admins where admin=(?)', (user,))
psw = cur.fetchall()
print("psw:", psw)
if not psw:
msg = "管理员用户名错误"
print(msg)
message = {'message': msg}
# return render_template("testxzl05.html",msg=msg)
return jsonify(message)
elif psw[0][0] == pwd:
msg = "登录成功"
print(msg)
# return redirect(url_for('listAdmin'))
return jsonify({'message': msg, 'redirect': url_for('toHtmlAdmin', user=user)})
else:
msg = "管理员密码错误"
print(msg)
# return render_template("testxzl05.html",msg=msg)
return jsonify({'message': msg})
if __name__ == '__main__':
app.run(debug=True)
到了这里,关于python实现前后端学生管理系统(前后端分离)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!