页面布局
首先有一个简单的表单,一个必填的输入框,一个 textarea 类型输入框,还有两个多选的下拉框
页面效果如下:
表结构
再来看看表结构是怎样的
这里主要用到了三张表:
- Project 表,存储项目信息
- Rel_Testsuit_Project 表,测试集和项目的关系表
- Rel_User_Project 表,用户和项目的关系表
另外其实还有 User 和 Testsuit 表,但是在本例子中并不涉及
Project 表:
class Project(db.Model): __tablename__ = 'projects' id = db.Column(db.Integer, primary_key=True) create_time = db.Column(db.Date) projectname = db.Column(db.String(64), unique=True, index=True) description = db.Column(db.Text)
Rel_Testsuit_Project 表
class Rel_Testsuit_Project(db.Model): __tablename__ = 'rel_testsuit_project' id = db.Column(db.Integer, primary_key=True) testsuit_id = db.Column(db.Integer) project_id = db.Column(db.Integer)
Rel_User_Project 表
class Rel_User_Project(db.Model): __tablename__ = 'rel_user_project' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer) project_id = db.Column(db.Integer)
Vue 代码
再来看看页面布局的代码
在新创建的 .vue 文件中,添加布局代码
<el-dialog title="新增项目" :visible.sync="dialogFormVisible"> <el-form :model="form" ref="form"> <el-form-item label="项目名称" :label-width="formLabelWidth" prop="projectname" :rules="[{required: true, message: '项目名称不能为空', trigger: 'change'}]"> <el-input v-model="form.projectname" placeholder="输入项目名称"> </el-input> </el-form-item> <el-form-item label="项目描述" :label-width="formLabelWidth"> <el-input type="textarea" :autosize="{ minRows: 2, maxRows: 8}" v-model="form.description" auto-complete="off"></el-input> </el-form-item> <el-form-item label="测试集" :label-width="formLabelWidth" prop="suitname"> <el-select v-model="form.suitname" multiple placeholder="请选择测试集" v-on:focus="getSuit()"> <el-option v-for="item in suits" :key="item.value" :label="item.label" :value="item.label"> </el-option> </el-select> </el-form-item> <el-form-item label="人员" :label-width="formLabelWidth" prop="username"> <el-select v-model="form.username" multiple placeholder="请添加人员" v-on:focus="getUser()"> <el-option v-for="item in userSelection" :key="item.value" :label="item.label" :value="item.label"> </el-option> </el-select> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" :loading="loginLoading" @click="save('form'), resetForm('form')">确 定</el-button> </div> </el-dialog>
在对话框中定义了 :visible.sync="dialogFormVisible",当点击某个按钮,比如:添加,则设置 dialogFormVisible = true,那么该对话框就会展示在页面上了。
其中函数 getSuit 和 getUser 分别从后台回去数据,展示在下拉框中。
下面是脚本代码部分
<script> import ToolBar from '~/components/ToolBar/ToolBar.vue'; import HelpHint from '~/components/HelpHint/HelpHint.vue'; import projectdata from '../../api/testproject.js'; import suitdata from '../../api/testsuit.js'; import dealdata from '../../api/data.js'; export default { data() { return { formLabelWidth: '120px', loginLoading: false, dialogFormVisible: false, userSelection: [], form: { id: '', projectname: '', suitname: [], description: '', username: [], }, projects: [], suits: [], users: [], searchParams:{ projectName:'', postType:'', projectStatus:'published', }, } }, methods: { resetForm(formName){ this.$refs[formName].resetFields(); }, getProject(){ let APP = this; let projectName = this.searchParams.projectName; let projectStatus = this.searchParams.projectStatus; let parm = {projectName: projectName, projectStatus:projectStatus}; projectdata.getProject(APP, parm); }, getSuit(){ let APP = this; suitdata.getSuit(APP); }, getUser(){ let APP = this; dealdata.getuser() .then(res => { if(res.data.code == 200) { APP.userSelection = res.data.selection_data; //console.log(APP.projects); //console.log(APP.userSelection); } }) .catch(function(error){ APP.$message.error("网络错误"); }) }, save(formName){ let APP = this; this.$refs[formName].validate((valid) => { var projectname = this.form['projectname']; var description = this.form['description']; var suitname = this.form['suitname']; var username = this.form['username']; if(valid) { var data = { projectname: projectname, description: description, suitname: suitname, username: username }; //console.log(data); APP.loginLoading = true; dealdata.saveproject(data) .then(res => { if(res.data.code == 200){ APP.loginLoading = false; APP.dialogFormVisible = false; this.$message.success(res.data.message); } else{ APP.loginLoading = false; this.$message.error(res.data.message); } projectdata.getProject(APP); }) .catch(function(error) { APP.loginLoading = false; console.log(APP.$message.error("网络错误")); }) }else{ APP.loginLoading = false; console.log("error save"); if(projectname == ''){ this.$message.error('项目名称不能为空'); return false; }; } }) }, }, mounted(){ this.getProject(); }, components: { ToolBar,HelpHint } } </script>
这里我们只关心 save 函数,组装 data 数据,通过 post 方法提交到后台。同时我们也可以看到,初始阶段,dialogFormVisible 是设置为 false 的。
API 代码
下面我们来看看后台的 API,还是先给出完整的代码。
class ProjectAddView(Resource): def post(self): try: projectinfo = request.get_json() print(projectinfo) if 'projectname' in projectinfo: projectname = request.get_json()['projectname'] else: return {'code': 422, 'message': '请输入名称'} if 'description' in projectinfo: description = request.get_json()['description'] else: description = '' if 'suitname' in projectinfo: suitname = request.get_json()['suitname'] else: suitname = '' if 'username' in projectinfo: username = request.get_json()['username'] else: username = '' ptname = Project.query.filter_by(projectname=projectname).first() if ptname: return {'code': 422, 'message': "该项目已经存在"} else: c_time = datetime.date.today() project = Project(projectname=projectname, description=description, create_time=c_time) db.session.add(project) db.session.commit() projectid = Project.query.filter_by(projectname=projectname).first().id if len(suitname) != 0: for ts in suitname: suitid = TestSuit.query.filter_by(testsuitname=ts).first().id r_ts_pt = Rel_Testsuit_Project(testsuit_id=suitid, project_id=projectid) db.session.add(r_ts_pt) db.session.commit() if len(username) != 0: for u in username: userid = User.query.filter_by(username=u).first().id r_us_pt = Rel_User_Project(user_id=userid, project_id=projectid) db.session.add(r_us_pt) db.session.commit() return {'code': 200, 'message': 'Project创建成功'} except: raise
最主要的部分,就是判断 suitname 和 username,因为这两个变量从前端传过来时都是列表形式的,所以只要判断下其长度是否为0,就能判断出提交表单时,是否选择了这两个选项,如果长度不为0,则循环这两个列表,并分别取出对应表中的 ID 值,保存至中间表中。
这样,就完成了多对多的绑定操作。