lichong 9 months ago
parent
commit
3eb3d3e5a8
  1. 163
      api/main.py
  2. 5
      front/package.json
  3. 11
      front/src/api/login.js
  4. 20
      front/src/api/user.js
  5. 20
      front/src/router/index.js
  6. 24
      front/src/views/login.vue
  7. 200
      front/src/views/user.vue
  8. 2
      front/vite.config.js

163
api/main.py

@ -1,18 +1,122 @@
from flask import Flask from flask import Flask, request, jsonify
from pymongo import MongoClient from pymongo import MongoClient
from bson import ObjectId
import hashlib
import json
from flask import Flask, render_template, request
from flask_socketio import SocketIO, Namespace, emit
app = Flask(__name__) app = Flask(__name__)
client = MongoClient("mongodb://localhost:27019/") client = MongoClient("mongodb://localhost:27019/")
db = client["back"] db = client["back"]
collection = db["users"] collection = db["users"]
# sockitIo
socketio = SocketIO()
# 解决跨域问题
socketio.init_app(app, cors_allowed_origins="*")
# 用来存放客户端的 sid,即 session id
# 可以不单独定义字典存放 sid与namespace,flask-socketio 默认将 sid 存放在 room 中
socket_pool = {}
# Websocket 通过namespace 和 sid 标识具体客户端
# 第一个 Websocket 类
class MyCustomNamespace(Namespace):
name_space = "/msg"
# 连接成功调用的方法
def on_connect(self):
global socket_pool
print("connect..")
print("-----------------")
print(self.server.manager)
print(self.socketio)
print("-----------------")
print(request.namespace)
socket_pool[request.sid] = self.name_space
print(socket_pool)
# 断开连接调用的方法
def on_disconnect(self):
global socket_pool
print("disconnect...")
del socket_pool[request.sid]
print(socket_pool)
# 往 接收客户端标消息识为 'message' 的方法
def on_message(self, data):
print(data)
# 把消息发送到客户端的 'response' 标识的方法中, 一般是 on_response()
emit("response", "123", to=request.sid)
print("--------------")
print(request.sid)
print("--------------")
print(socketio.server.manager.rooms)
# 往 接收客户端标消息识为 'hello' 的方法
def on_hello(self, data):
print("hello world")
# 发送消息
def send(self, data):
# 向 namespace中的所有 Websocket 连接广播消息, namespace参数不能少, to缺省是广播模式
emit("response", data, namespace=self.name_space)
# 向 sid 所标识的客户端 单播
emit("response", data, namespace=self.name_space, to=request.sid)
# 为了详细展示不同类,这里就不做继承
# 第二个 Websocket 类
class MyCustomNamespace1(Namespace):
name_space = "/vedio"
def on_connect(self):
global socket_pool
print("connect..")
print(request.namespace)
socket_pool[request.sid] = self.name_space
print(socket_pool)
def on_disconnect(self):
global socket_pool
print("disconnect...")
del socket_pool[request.sid]
print(socket_pool)
def on_message(self, data):
print(data)
emit("response", "123")
self.send("asd")
# 发送消息
def send(self, data):
emit("response", data, namespace=self.name_space)
@app.route("/sendMsg")
def sendMsg():
for sid, namespace_value in socket_pool.items():
print(sid)
emit("response", "123456", namespace=namespace_value, to=sid)
print("ok")
return "ok"
# md5加密
def md5_encrypt(data):
md5 = hashlib.md5()
md5.update(data.encode("utf-8"))
return md5.hexdigest()
# 初始化数据 # 初始化数据
def initData(): def initData():
isAdmin = collection.find_one({"isAdmin": 1, "isExit": 1}) admin = collection.find_one({"isAdmin": 1, "isExit": 1})
if isAdmin is None: if admin is None:
# 学校名称、学校代号、专业名称、专业代号、年级、班级、学生姓名、学号、成绩、作弊情况(作弊类型、作弊时间、作弊图片)、考试类型、考试科目、考试时间段、是否是管理员、是否删除、创建时间、更新时间 # 学校名称、学校代号、专业名称、专业代号、年级、班级、学生姓名、学号、密码、成绩、作弊情况(作弊类型、作弊时间、作弊图片)、考试类型、考试科目、考试时间段、是否是管理员、是否删除、创建时间、更新时间
# xuexiaomingcheng、xuexiaodaihao、zhuanyemingcheng、zhuanyedaihao、nianji、banji、xueshengxingming、xuehao、chengji、zuobiqingkuang(zuobileixing、zuobishijian、zuobitupian)、kaoshileixing、kaoshikemu、kaoshishijianduan、isAdmin、isExit、chuangjianshijian、gengxinshijian # xuexiaomingcheng、xuexiaodaihao、zhuanyemingcheng、zhuanyedaihao、nianji、banji、xueshengxingming、xuehao、mima、chengji、zuobiqingkuang(zuobileixing、zuobishijian、zuobitupian)、kaoshileixing、kaoshikemu、kaoshishijianduan、isAdmin、isExit、chuangjianshijian、gengxinshijian
user = { user = {
"xuexiaomingcheng": "", "xuexiaomingcheng": "",
"xuexiaodaihao": "", "xuexiaodaihao": "",
@ -22,6 +126,7 @@ def initData():
"banji": "", "banji": "",
"xueshengxingming": "老师", "xueshengxingming": "老师",
"xuehao": "0000", "xuehao": "0000",
"mima": md5_encrypt("0000"),
"chengji": "", "chengji": "",
"zuobiqingkuang": [], "zuobiqingkuang": [],
"kaoshileixing": "", "kaoshileixing": "",
@ -33,18 +138,46 @@ def initData():
"gengxinshijian": "", "gengxinshijian": "",
} }
collection.insert_one(user) collection.insert_one(user)
print("管理账号'老师'已被初始化新建")
else: else:
print(888, isAdmin) print(f"管理账号'{admin['xueshengxingming']}'已存在")
# 判断是否是管理员
def isAdmin(id):
mongoId = ObjectId(id)
userObj = collection.find_one({"_id": mongoId, "isExit": 1})
# 检查 userObj 是否为 None
if userObj is None:
return False
# 默认返回 False 如果 isAdmin 字段不存在
adminBool = userObj.get("isAdmin", False)
return bool(adminBool)
# 测试 # 用户登录
@app.route("/") @app.route("/userLogin", methods=["post"])
def hello(): def login():
return "Hello World!" resData = request.data
# 检测是否有数据
if not resData:
return {"code": 200, "msg": "请填写信息后登录", "list": [], "hasError": True}
# 获取到POST过来的数据,转为json形式
userJson = json.loads(resData)
res = collection.find_one({"xuehao": userJson["xuehao"], "mima": userJson["mima"]})
if res is None:
return {"code": 200, "msg": "暂无此用户", "list": [], "hasError": True}
# serialized_data = []
# for item in data:
# item["_id"] = str(item["_id"]) # 将ObjectId转换为字符串
# serialized_data.append(item)
# 将ObjectId转换为字符串
res["_id"] = str(res["_id"])
return {"code": 200, "msg": "登陆成功", "list": [res], "hasError": False}
# 新增用户 # 新增用户
@app.route("/insert") @app.route("/addUser", methods=["post"])
def insert_data(): def insert_data():
user = {"name": "John Doe", "age": 25, "city": "New York"} user = {"name": "John Doe", "age": 25, "city": "New York"}
collection.insert_one(user) collection.insert_one(user)
@ -52,7 +185,7 @@ def insert_data():
# 查询用户 # 查询用户
@app.route("/query") @app.route("/getUser", methods=["post"])
def query_data(): def query_data():
users = collection.find() users = collection.find()
result = "" result = ""
@ -62,7 +195,7 @@ def query_data():
# 更新用户 # 更新用户
@app.route("/update") @app.route("/updateUser", methods=["post"])
def update_data(): def update_data():
query = {"name": "John Doe"} query = {"name": "John Doe"}
new_data = {"$set": {"age": 30, "city": "San Francisco"}} new_data = {"$set": {"age": 30, "city": "San Francisco"}}
@ -71,7 +204,7 @@ def update_data():
# 删除用户 # 删除用户
@app.route("/delete") @app.route("/delUser", methods=["post"])
def delete_data(): def delete_data():
query = {"name": "John Doe"} query = {"name": "John Doe"}
collection.delete_one(query) collection.delete_one(query)
@ -80,4 +213,4 @@ def delete_data():
if __name__ == "__main__": if __name__ == "__main__":
initData() initData()
app.run() app.run(debug=True)

5
front/package.json

@ -5,8 +5,7 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build"
"preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
@ -27,4 +26,4 @@
"@vitejs/plugin-vue": "^4.6.2", "@vitejs/plugin-vue": "^4.6.2",
"vite": "^5.0.8" "vite": "^5.0.8"
} }
} }

11
front/src/api/login.js

@ -1,11 +0,0 @@
import _axios from "@/plugins/axios";
//登录、注册
export function loginFun(data) {
return _axios({
url: `/v1/login`,
method: "POST",
data,
});
}

20
front/src/api/user.js

@ -1,11 +1,21 @@
import _axios from "@/plugins/axios"; import _axios from "@/plugins/axios";
//登录、注册
export function loginFun(data) {
return _axios({
url: `/v1/userLogin`,
method: "POST",
data: { ...data },
});
}
//增加用户 //增加用户
export function addUser(data) { export function addUser(data) {
return _axios({ return _axios({
url: `/v1/addUser`, url: `/v1/addUser`,
method: "POST", method: "POST",
data, data: { ...data, id: localStorage.getItem("userId") },
}); });
} }
@ -14,15 +24,15 @@ export function delUser(data) {
return _axios({ return _axios({
url: `/v1/delUser`, url: `/v1/delUser`,
method: "POST", method: "POST",
data, data: { ...data, id: localStorage.getItem("userId") },
}); });
} }
//修改用户 //修改用户
export function getUser(data) { export function updateUser(data) {
return _axios({ return _axios({
url: `/v1/updateUser`, url: `/v1/updateUser`,
method: "POST", method: "POST",
data, data: { ...data, id: localStorage.getItem("userId") },
}); });
} }
//查找用户 //查找用户
@ -30,7 +40,7 @@ export function getUser(data) {
return _axios({ return _axios({
url: `/v1/getUser`, url: `/v1/getUser`,
method: "POST", method: "POST",
data, data: { ...data, id: localStorage.getItem("userId") },
}); });
} }

20
front/src/router/index.js

@ -6,24 +6,20 @@ const routes = [
path: "/", path: "/",
name: "home", name: "home",
component: home, component: home,
redirect: "/bug", redirect: "/user",
children: [{ children: [
path: "bug", {
name: "bug", path: "user",
component: () => import("../views/bug.vue"), name: "user",
}, component: () => import("../views/user.vue"),
{ },]
path: "user",
name: "user",
component: () => import("../views/user.vue"),
},]
}, },
{ {
path: "/login", path: "/login",
name: "login", name: "login",
component: () => import("../views/login.vue"), component: () => import("../views/login.vue"),
}, },
{ name: "重定向", path: "/:catchAll(.*)", redirect: "/bug" }, { name: "重定向", path: "/:catchAll(.*)", redirect: "/login" },
]; ];
// 创建路由实例 // 创建路由实例

24
front/src/views/login.vue

@ -7,18 +7,19 @@
<el-button class="button" text @click="regist = false"> <el-button class="button" text @click="regist = false">
登录 登录
</el-button> </el-button>
<el-button class="button" text @click="regist = true"> <!-- <el-button class="button" text @click="regist = true">
注册 注册
</el-button> </el-button> -->
</div> </div>
</template> </template>
<div> <div>
<el-form :model="regForm" label-suffix=":"> <el-form :model="regForm" label-suffix=":" @submit.prevent>
<el-form-item label="账号"> <el-form-item label="账号">
<el-input v-model="regForm.name" placeholder="请输入账号" /> <el-input v-model="regForm.name" placeholder="请输入账号" />
</el-form-item> </el-form-item>
<el-form-item label="密码"> <el-form-item label="密码">
<el-input v-model="regForm.password" type="password" placeholder="请输入密码" show-password /> <el-input v-model="regForm.password" type="password" placeholder="请输入密码" show-password
@keyup.enter="login" />
</el-form-item> </el-form-item>
<el-form-item label="邮箱" v-if="regist"> <el-form-item label="邮箱" v-if="regist">
<el-input v-model="regForm.email" placeholder="请输入邮箱" /> <el-input v-model="regForm.email" placeholder="请输入邮箱" />
@ -40,7 +41,7 @@
<script> <script>
import { import {
loginFun, // loginFun, //
} from "@/api/login"; } from "@/api/user";
import md5 from "md5" import md5 from "md5"
export default { export default {
name: "login", name: "login",
@ -66,8 +67,8 @@ export default {
dealForm() { dealForm() {
return { return {
regist: this.regist, regist: this.regist,
name: this.regForm.name, xuehao: this.regForm.name,
password: md5(this.regForm.password), mima: md5(this.regForm.password),
email: this.regForm.email, email: this.regForm.email,
} }
}, },
@ -75,11 +76,12 @@ export default {
async login() { async login() {
let formData = this.dealForm() let formData = this.dealForm()
let res = await loginFun(formData) let res = await loginFun(formData)
console.log(999, res)
if (!res.hasError) { if (!res.hasError) {
ElMessage.success("登陆成功"); ElMessage.success(res.msg);
localStorage.setItem("userId", res.list._id) localStorage.setItem("userId", this.$_.get(res, ["list", 0, "_id"], ""))
localStorage.setItem("userName", res.list.name) localStorage.setItem("userName", this.$_.get(res, ["list", 0, "xueshengxingming"], ""))
this.$router.push("/bug") this.$router.push("/user")
} else { } else {
ElMessage.error(res.msg); ElMessage.error(res.msg);
localStorage.clear() localStorage.clear()

200
front/src/views/user.vue

@ -12,12 +12,7 @@
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="2"> <el-col :span="2">
<el-upload <el-upload :limit="1" accept="text/csv" :auto-upload="false" :on-change="fileChange">
:limit="1"
accept="text/csv"
:auto-upload="false"
:on-change="fileChange"
>
<template #trigger> <template #trigger>
<el-button type="primary">文件上传</el-button> <el-button type="primary">文件上传</el-button>
</template> </template>
@ -25,11 +20,7 @@
</el-upload> </el-upload>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-input <el-input v-model="searchValue" placeholder="Please input email" @keyup.enter="searchId">
v-model="searchValue"
placeholder="Please input email"
@keyup.enter="searchId"
>
<template #append> <template #append>
<el-button @click="searchId" icon="Search" /> <el-button @click="searchId" icon="Search" />
</template> </template>
@ -37,36 +28,14 @@
</el-col> </el-col>
</el-row> </el-row>
<div v-loading="loading"> <div v-loading="loading">
<vueTable <vueTable ref="userTableParent" :tableHeader="tableHeader" :tableData="tableData" @detailInfo="detailInfo"
ref="userTableParent" @editInfo="editInfo">
:refName="refName"
:tableHeader="tableHeader"
:tableData="tableData"
:currentPage="currentPage"
:pageSize="pageSize"
:pageSizes="pageSizes"
:total="total"
@detailInfo="detailInfo"
@editInfo="editInfo"
@handleSizeChange="handleSizeChange"
@handleCurrentChange="handleCurrentChange"
>
</vueTable> </vueTable>
</div> </div>
<el-dialog <el-dialog v-model="addDialog.show" :title="addDialog.title" width="80%" draggable
v-model="addDialog.show" :close-on-click-modal="formDisabled">
:title="addDialog.title" <changeItem :formData="formData" :formDisabled="formDisabled" :type="addDialog.type" :formHeader="formHeaderLocal">
width="80%"
draggable
:close-on-click-modal="formDisabled"
>
<changeItem
:formData="formData"
:formDisabled="formDisabled"
:type="addDialog.type"
:formHeader="formHeaderLocal"
>
</changeItem> </changeItem>
<template #footer v-if="!formDisabled"> <template #footer v-if="!formDisabled">
<div class="dialog-footer"> <div class="dialog-footer">
@ -83,26 +52,135 @@ import md5 from "md5";
import Papa from "papaparse"; import Papa from "papaparse";
import vueTable from "../component/table.vue"; import vueTable from "../component/table.vue";
import changeItem from "../component/changeItem.vue"; import changeItem from "../component/changeItem.vue";
import _ from "lodash";
import { import {
getUser, //user
addUser, //user addUser, //user
delUser, //user delUser, //user
updateUser, //user
getUser, //user
} from "@/api/user"; } from "@/api/user";
export default { export default {
name: "user", name: "user",
components: { vueTable, changeItem }, components: { vueTable, changeItem },
data() { data() {
return { return {
refName: "userTable", _: _,
loading: false, loading: false,
tableHeader: [ localComponent: [
{ prop: "name", label: "name" }, {
{ prop: "password", label: "password", type: "password" }, prop: "xuexiaomingcheng",
{ prop: "email", label: "email" }, label: "学校名称",
{ prop: "loginTime", label: "loginTime", type: "time", minWidth: 120 }, type: "text",
{ prop: "create_at", label: "create_at", type: "time", minWidth: 120 }, tableShow: true,
{ prop: "update_at", label: "update_at", type: "time", minWidth: 120 }, formShow: true
},
{
prop: "xuexiaodaihao",
label: "学校代号",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "zhuanyemingcheng",
label: "专业名称",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "zhuanyedaihao",
label: "专业代号",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "nianji",
label: "年级",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "banji",
label: "班级",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "xueshengxingming",
label: "学生姓名",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "xuehao",
label: "学号",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "mima",
label: "密码",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "chengji",
label: "成绩",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "zuobiqingkuang",
label: "作弊情况",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "kaoshileixing",
label: "考试类型",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "kaoshikemu",
label: "考试科目",
type: "text",
tableShow: true,
formShow: true
},
{
prop: "kaoshishijianduan",
label: "考试时间段",
type: "datetimerange",
tableShow: true,
formShow: true
},
{
prop: "chuangjianshijian",
label: "创建时间",
type: "text",
tableShow: false,
formShow: false
},
{
prop: "gengxinshijian",
label: "更新时间",
type: "text",
tableShow: false,
formShow: false
},
], ],
tableHeader: [],
tableData: [], tableData: [],
currentPage: 1, currentPage: 1,
pageSize: 16, pageSize: 16,
@ -168,32 +246,24 @@ export default {
watch: {}, watch: {},
computed: {}, computed: {},
async mounted() { async mounted() {
this.dealTableHeader()
this.dealFormHeader()
await this.init(); await this.init();
}, },
beforeUnmount() {}, beforeUnmount() { },
methods: { methods: {
async init(params = {}, isReload = true) { dealTableHeader() {
if (isReload) { this.tableData = _.filter(this.localComponent, o => o.tableShow)
this.searchValue = ""; },
} dealFormHeader() {
this.formHeader = _.filter(this.localComponent, o => o.formShow)
},
async init(params = {}) {
this.loading = true; this.loading = true;
let res = await getUser({ let res = await getUser({
subType: "get",
returnData: [
"name",
"email",
"allowRegist",
"loginTime",
"create_at",
"update_at",
],
pageSize: this.pageSize,
currentPage: this.currentPage,
...params, ...params,
}); });
this.tableData = res.list; this.tableData = res.list;
this.total = res.count;
this.loading = false; this.loading = false;
}, },
async addUserData() { async addUserData() {

2
front/vite.config.js

@ -30,7 +30,7 @@ export default defineConfig({
port: 3000, port: 3000,
proxy: { proxy: {
"/v1": { "/v1": {
target: "http://127.0.0.1:5000/v1", target: "http://127.0.0.1:5000",
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/v1/, ""), rewrite: (path) => path.replace(/^\/v1/, ""),
}, },

Loading…
Cancel
Save