数据库期末项目开发心得(持续更新中)
数据库期末项目开发心得
文章目录
- 数据库期末项目开发心得
- 1、架构的实践案例
- **(1)[(23条消息) Vue + Spring Boot 项目实战(一):项目简介_Evan 的博客-CSDN博客_vue实战](https://learner.blog.csdn.net/article/details/88925013)**
- **(2)**[(23条消息) Vue + Spring Boot 项目实战(二):使用 CLI 搭建 Vue.js 项目_Evan 的博客-CSDN博客](https://learner.blog.csdn.net/article/details/88926242)
- **(3)**[(23条消息) Vue + Spring Boot 项目实战(三):前后端结合测试(登录页面开发)_Evan 的博客-CSDN博客_vue前端开发实战](https://learner.blog.csdn.net/article/details/88955387)
- **(4)[(25条消息) Vue + Spring Boot 项目实战(四):数据库的引入_Evan-Nightly的博客-CSDN博客](https://learner.blog.csdn.net/article/details/89294300)**
- (5)[(25条消息) Vue + Spring Boot 项目实战(五):使用 Element 辅助前端开发_Evan-Nightly的博客-CSDN博客](https://learner.blog.csdn.net/article/details/89298717)
- (6)[(25条消息) Vue + Spring Boot 项目实战(六):前端路由与登录拦截器_Evan-Nightly的博客-CSDN博客](https://learner.blog.csdn.net/article/details/89422585)
- (8)[(25条消息) Vue + Spring Boot 项目实战(八):数据库设计与增删改查_Evan-Nightly的博客-CSDN博客](https://learner.blog.csdn.net/article/details/92413933)
- (9)[(25条消息) Vue + Spring Boot 项目实战(九):核心功能的前端实现_Evan-Nightly的博客-CSDN博客](https://learner.blog.csdn.net/article/details/95310666)
- 2、项目的基本框架
- 3、项目技术栈
- 前端
- 1、vue:
- 2、vuex:(保存登录状态)
- 3、element学习参考:(编写开发)
- 4、axios学习参考:
- 5、mockjs:拦截ajax请求生成数据用来调试前端:
- 6、qs:
- 7、echarts:
- 后端:
- 其他知识的一些学习:
- 1、jwt:
- 2、http
- 4、技术细节
- 1、前端脚手架安装
- 2、前端基本配置
- 3、注册页面的编写,mock调试的过程
- 4、验证码(参考网上的代码)
- 5.导航栏
- 6、用户基本信息的修改
- 7、echarts引入(实验室)
- 8、论文库首页
- 1、提交新的论文
- 2、下拉表单子组件
- 3、面包屑导航栏
- 4、论文首页
- 5、论文内容
1、架构的实践案例
(1)(23条消息) Vue + Spring Boot 项目实战(一):项目简介_Evan 的博客-CSDN博客_vue实战
开发一个线上图书馆的示例
前端用vue+ElementUI,后端用spring框架
(2)(23条消息) Vue + Spring Boot 项目实战(二):使用 CLI 搭建 Vue.js 项目_Evan 的博客-CSDN博客
(3)(23条消息) Vue + Spring Boot 项目实战(三):前后端结合测试(登录页面开发)_Evan 的博客-CSDN博客_vue前端开发实战
*关于正向代理和反向代理:原博客对反向代理作出了详细的解释和说明:
(4)(25条消息) Vue + Spring Boot 项目实战(四):数据库的引入_Evan-Nightly的博客-CSDN博客
(a)数据库:ENGINE=InnoDB(默认的引擎就是InnoDB但是为了规范话在建表查询的同时声明了这个引擎)
AUTO_INCREMENT=2,表示自增的大小,自增会每次增加2
(b)什么是pojo?:POJO,即"Plain Old Java Object"。我们可以认为POJO是一个"纯粹"的Java对象,而什么是"纯粹"的Java对象?就是以Java语言规范为基础设计的对象就是一个POJO。从而进一步我们可以理解POJO是指没有实现相关第三方接口、继承相关的第三方类的Java对象,即非侵入的Java对象。
原文连接https://blog.csdn.net/weixin_46015825/article/details/113798732
(c)注解(25条消息) Spring常用注解_一筒君的博客-CSDN博客_spring常用注解
@Entity//实体
@Table(name="")//表
@
(5)(25条消息) Vue + Spring Boot 项目实战(五):使用 Element 辅助前端开发_Evan-Nightly的博客-CSDN博客
Element_UI的官网: http://element-cn.eleme.io/#/zh-CN
(6)(25条消息) Vue + Spring Boot 项目实战(六):前端路由与登录拦截器_Evan-Nightly的博客-CSDN博客
(8)(25条消息) Vue + Spring Boot 项目实战(八):数据库设计与增删改查_Evan-Nightly的博客-CSDN博客
(a)建表的完整数据:https://github.com/Antabot/White-Jotter/tree/master/wj/src/main/resources
/*Book中*/
@ManyToOne
@JoinColumn(name="cid")
private Category category;/*Dao中的查询语句之所以可以根据category查询来源于上述注解的作用*/
List<Book> findAllByCategory (Category category);
©orElse解决空指针异常:orElse - 搜索 (bing.com)
(d)@RestController=@Controller+@ResponseBody
【SpringBoot】 http请求注解之@RestController - 简书 (jianshu.com)
(25条消息) GetMapping 和 PostMapping_大鹏小站的博客-CSDN博客_getmapping和postmapping
(9)(25条消息) Vue + Spring Boot 项目实战(九):核心功能的前端实现_Evan-Nightly的博客-CSDN博客
(a)@CrossOrigin:(25条消息) 注解@CrossOrigin详解_MobiusStrip的博客-CSDN博客_crossorigin注解
(25条消息) 注解@CrossOrigin详解_MobiusStrip的博客-CSDN博客_crossorigin注解
2、项目的基本框架
3、项目技术栈
前端
vue学习的参考博客:
1、vue:
(22条消息) Vue学习之从入门到神经(万字长文 建议收藏)_白大锅的博客-CSDN博客_vue学习
(22条消息) 狂神说Vue笔记整理_one peice的博客-CSDN博客_狂神说vue笔记
2、vuex:(保存登录状态)
VueX(Vue状态管理模式) - 简书 (jianshu.com)(登录login保存登录状态)
3、element学习参考:(编写开发)
element-ui组件库:组件 | Element
4、axios学习参考:
Axios 中文文档_w3cschool
5、mockjs:拦截ajax请求生成数据用来调试前端:
mock.js的使用方法 - 简书 (jianshu.com)
6、qs:
查询参数序列化和解析库
7、echarts:
https://echarts.apache.org/zh/index.html
后端:
Spring boot+Spring Security 学习参考:Spring All master https://mrbird.cc
Mybatis:
其他知识的一些学习:
1、jwt:
【深度知识】JSON Web令牌(JWT)的原理,流程和数据结构 - 云+社区 - 腾讯云 (tencent.com)
2、http
HTTP 响应头信息 | 菜鸟教程 (runoob.com)
4、技术细节
1、前端脚手架安装
(26条消息) 在IDEA中对vue项目第一次导入需要的操作_程序媛小白白的博客-CSDN博客
npm i -g cnpm --registry=https://registry.npm.taobao.org
npm i -g vue-cli
vue init webpack $项目名$(记得cd 项目文件夹)
cnpm install
npm run dev
因为我们的项目还需要使用axios库:基于promise的http库
element-ui辅助前端页面开发
qs:数据序列化
mockjs:生成随机数据
vuex:保存数据状态
还需要安装
npm install --save axios
npm i element-ui -S
cnpm install qs --save
cnpm install mockjs --save-dev
cnpm install vuex --save
那么基本的前端开发环境就搭建好了
2、前端基本配置
因为要使用Element框架辅助开发,axios进行请求,mockjs进行调试,Vuex保存数据,所以要进行相关设置
创建store文件夹,添加一个index.js
src/store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'//挂载vuex对象
Vue.use(Vuex)export default new Vuex.Store({//存放的状态state:{},//状态操作的集合类似于methodsmutations:{},//异步操作actions:{},//模块化状态管理modules:{}
})
路由设置:src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Index from '../components/Index'
import Login from "../components/Login";Vue.use(Router)const routes=[{path: '/index',name: 'Index',component: Index},{path:'/login',name:'Login',component:Login}
]const router=new Router({mode:'history',base:process.env.BASE_URL,routes
})
export default router
值得注意的是这里的模式要把hash模式改为history模式支持页面的回转
并且设置基路由
src/main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import Element from 'element-ui'
import "element-ui/lib/theme-chalk/index.css"
import axios from 'axios'Vue.prototype.$axios = axios
Vue.config.productionTip = false
Vue.use(Element)require("./mock.js")
/* eslint-disable no-new */
new Vue({el: '#app',router,store,components: { App },template: '<App/>',})
引用了上述的一些基本库,包括axios,Element-ui,Vuex(store文件夹中)
3、注册页面的编写,mock调试的过程
<template><body id="login"><el-form class="login-form":model="loginForm":rules="rules"label-position="left"label-width="80px"ref="loginForm"><h3>欢迎来到论文管理系统</h3><el-row><el-form-item label="用户名" prop="username"><el-input type="text" v-model="loginForm.username" prefix-icon="el-icon-user" size="medium"></el-input></el-form-item></el-row><el-row><el-form-item label="密码" prop="password"><el-input type="password" v-model="loginForm.password" prefix-icon="el-icon-key" size="medium"></el-input></el-form-item></el-row><el-row><el-col :span="10" ><el-form-item label="验证码" prop="code"><el-input type="text" v-model="loginForm.code" prefix-icon="el-icon-edit" style="width: 100px"></el-input></el-form-item></el-col><el-image class="validateCode" :src="validateCode" @click="getValidateCode"></el-image></el-row><el-button type="primary" @click="handleLogin('loginForm')" style="margin-top: 20px">登录</el-button></el-form></body>
</template><script>
export default {name: 'Login',data () {return {loginForm: {username: '',password: '',code: '',token:'',},rules: {username:[{required:true ,message:'请输入用户名',trigger:'blur'}],password:[{required:true ,message:'请输入密码',trigger:'blur'}],code:[{required:true ,message:'请输入验证码',trigger:'blur'},{min:4,max:4 ,message:'验证码长度不正确',trigger:'blur'}],},validateCode:null,}},methods: {//登录handleLogin (formName) {this.$refs[formName].validate((valid) => {if (valid) {this.$axios.post('/login',this.loginForm).then(res=>{const jwt=res.headers['authorization']/*commit(opt,arg)opt是方法名,jwt是参数*/this.$store.commit('SET_TOKEN',jwt)this.$router.push("/index")})} else {return false;}});},//生成验证码getValidateCode(){this.$axios.get('/validateCode').then(res => {this.loginForm.token = res.data.data.tokenthis.validateCode = res.data.data.validateCode})}},//钩子函数:用于渲染验证码created(){this.getValidateCode()}
}
</script><style scoped>
#login{background: url("../assets/loginBackground.jpg") no-repeat ;background-position: center;height: 100%;width: 100%;background-size: cover;position: fixed;
}
html,body{margin:0;padding:0;
}
.login-form{border-radius: 20px;margin: 200px auto;width:350px;border:2px solid lightblue;padding:20px;background: #ffffff;
}
/deep/ .el-form-item__error {color: #e6a23c;width: 100px;
}
</style>
页面UI直接先抄Element-ui库里的源代码进行修改
页面设计一部分参考了上面的设计
后续有可能添加登录方式以后在改设计
src/mock.js
// 引入mockjs
const Mock = require('mockjs')
// 获取 mock.Random 对象// 参考:https://github.com/nuysoft/Mock/wiki/Mock.Random
const Random = Mock.Random
let Result = {code: 200,msg: '操作成功',data: null
}
/**** Mock.mock( url, post/get , function(options));* url 表示需要拦截的 URL,* post/get 需要拦截的 Ajax 请求类型* 用于生成响应数据的函数 */
// 获取验证码图片base64编码以及一个随机码
Mock.mock('/validateCode', 'get', () => {Result.data = {token: Random.string(32), // 获取一个32位的随机字符串,validateCode: Random.dataImage("120x40", "1111")//生成验证码为11111的base64图片编码}return Result
})Mock.mock('/login', 'post', () => {//无法在handler中传入数据jwtreturn Result
})
测试了两个属性
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'//挂载vuex对象
Vue.use(Vuex)export default new Vuex.Store({//存放的状态state:{token:''},//状态操作的集合类似于methodsmutations:{SET_TOKEN:(state,token)=>{state.token =tokenlocalStorage.setItem("token",token)}},//异步操作actions:{},//模块化状态管理modules:{}
})
4、验证码(参考网上的代码)
(45条消息) Vue实现验证码功能_吴冬雪~的博客-CSDN博客_vue验证码怎么做
具体的思路是写一个静态的js文件,通过这个js文件配置验证码的各个属性
/更新 这个地方是有bug的,每次刷新的时候,验证码的refresh不对数组进行清空,所以需要把数组清空一下/
src/assets/js/validateCode.js
function GVerify (options) { // 创建一个图形验证码对象,接收options对象为参数this.options = { // 默认options参数值id: '', // 容器IdcanvasId: 'validateCanvas', // canvas的IDwidth: '80', // 默认canvas宽度height: '30', // 默认canvas高度type: 'number', // 图形验证码默认类型blend:数字字母混合类型、number:纯数字、letter:纯字母code: ''//网上的评论说设置成null更好,但是实际操作下来因为涉及到数组拷贝所以不能直接设置成null}if (Object.prototype.toString.call(options) === '[object Object]') { // 判断传入参数类型for (var i in options) { // 根据传入的参数,修改默认参数值this.options[i] = options[i]}} else {this.options.id = options}this.options.numArr = '0,1,2,3,4,5,6,7,8,9'.split(',')this.options.letterArr = getAllLetter()this._init()this.refresh()
}GVerify.prototype = {/** 版本号**/version: '1.0.0',/** 初始化方法**/_init: function () {var con = document.getElementById(this.options.id)var canvas = document.createElement('canvas')// this.options.width = con.offsetWidth > 0 ? con.offsetWidth : '30'// this.options.height = con.offsetHeight > 0 ? con.offsetHeight : '30'this.options.width = '160'this.options.height = '40'canvas.id = this.options.canvasIdcanvas.width = this.options.widthcanvas.height = this.options.heightcanvas.style.cursor = 'pointer'canvas.innerHTML = '您的浏览器版本不支持canvas'con.appendChild(canvas)var parent = thiscanvas.onclick = function () {parent.refresh()}},/** 生成验证码**/refresh: function () {var canvas = document.getElementById(this.options.canvasId)if (canvas.getContext) {var ctx = canvas.getContext('2d')}ctx.textBaseline = 'middle'ctx.fillStyle = randomColor(180, 240)ctx.fillRect(0, 0, this.options.width, this.options.height)//清空验证码值this.options.code=[]if (this.options.type === 'blend') { // 判断验证码类型var txtArr = this.options.numArr.concat(this.options.letterArr)} else if (this.options.type === 'number') {var txtArr = this.options.numArr} else {var txtArr = this.options.letterArr}for (var i = 1; i <= 4; i++) {var txt = txtArr[randomNum(0, txtArr.length)]this.options.code += txtctx.font = randomNum(this.options.height / 2, this.options.height) + 'px SimHei' // 随机生成字体大小ctx.fillStyle = randomColor(50, 160) // 随机生成字体颜色ctx.shadowOffsetX = randomNum(-3, 3)ctx.shadowOffsetY = randomNum(-3, 3)ctx.shadowBlur = randomNum(-3, 3)ctx.shadowColor = 'rgba(0, 0, 0, 0.3)'var x = this.options.width / 5 * ivar y = this.options.height / 2var deg = randomNum(-30, 30)/** 设置旋转角度和坐标原点**/ctx.translate(x, y)ctx.rotate(deg * Math.PI / 180)ctx.fillText(txt, 0, 0)/** 恢复旋转角度和坐标原点**/ctx.rotate(-deg * Math.PI / 180)ctx.translate(-x, -y)}/** 绘制干扰线**/for (var i = 0; i < 4; i++) {ctx.strokeStyle = randomColor(40, 180)ctx.beginPath()ctx.moveTo(randomNum(0, this.options.width), randomNum(0, this.options.height))ctx.lineTo(randomNum(0, this.options.width), randomNum(0, this.options.height))ctx.stroke()}/** 绘制干扰点**/for (var i = 0; i < this.options.width / 4; i++) {ctx.fillStyle = randomColor(0, 255)ctx.beginPath()ctx.arc(randomNum(0, this.options.width), randomNum(0, this.options.height), 1, 0, 2 * Math.PI)ctx.fill()}},/** 验证验证码**/validate: function (code) {var code = code.toLowerCase()var v_code = this.options.code.toLowerCase()if (code == v_code) {return true} else {return false}}
}
/** 生成字母数组**/
function getAllLetter () {var letterStr = 'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z'return letterStr.split(',')
}
/** 生成一个随机数**/
function randomNum (min, max) {return Math.floor(Math.random() * (max - min) + min)
}
/** 生成一个随机色**/
function randomColor (min, max) {var r = randomNum(min, max)var g = randomNum(min, max)var b = randomNum(min, max)return 'rgb(' + r + ',' + g + ',' + b + ')'
}export {GVerify
}
src/components/login/Login.vue
将之前测试的验证码的逻辑更改一下:
html部分注意v_container中加载了验证码
script部分值得注意的是mounted函数,还有handleLogin函数
<template><body id="login"><el-form class="login-form":model="loginForm":rules="rules"label-position="left"label-width="80px"ref="loginForm"><h3>欢迎来到论文管理系统</h3><el-row><el-form-item label="用户名" prop="username"><el-input type="text" v-model="loginForm.username" prefix-icon="el-icon-user" ></el-input></el-form-item></el-row><el-row><el-form-item label="密码" prop="password"><el-input type="password" v-model="loginForm.password" prefix-icon="el-icon-key" show-password></el-input></el-form-item></el-row><el-row><el-col :span="10" ><el-form-item label="验证码" prop="code"><el-input type="text" v-model="loginForm.code" prefix-icon="el-icon-edit" style="width: 80px"></el-input></el-form-item></el-col><!-- 生成验证码 --><div id="v_container"></div></el-row><el-row type="flex" justify="end"><el-button type="primary" @click="handleLogin('loginForm')" style="margin-top: 20px">登录</el-button><el-link href="/register" icon="el-icon-view" class="link" style="margin-top: 20px">还未注册?点这里</el-link></el-row></el-form></body>
</template><script>
import { GVerify } from '../../assets/js/validateCode';
import Element from "element-ui";
export default {name: 'Login',data () {var usernameValidate=(rule,value,callback)=>{if(value==='')callback(new Error('请输入用户名'))callback()};var passwordValidate=(rule,value,callback)=>{if(value==='')callback(new Error('请输入密码'))callback()};var codeValidate=(rule,value,callback)=>{if(value==='')callback(new Error('请输入验证码'))callback()}return {loginForm: {username: '',password: '',code: '',jwt:'',},rules: {username:[{validator:usernameValidate,trigger:'blur'}],password:[{validator:passwordValidate,trigger:'blur'}],code:[{validator:codeValidate,trigger:'blur'},{min:4,max:4,message:'验证码长度不正确',trigger:'blur'}],},validateCode:null,}},methods: {//登录handleLogin (formName) {this.$refs[formName].validate((valid) => {if (valid) {//this.loginForm.password=this.$md5(this.loginForm.password)this.$axios.post('/user/login',this.loginForm).then(res=>{var that = this// 获取验证码var validateCode = this.loginForm.codevar validateFlag = this.validateCode.validate(validateCode)if (!validateFlag) {that.$message.error('验证码错误')return false;}const jwt=res.data.data.jwt/*commit(opt,arg)opt是方法名,jwt是参数*/this.$store.commit('SET_TOKEN',jwt)this.$router.push("/index")}).catch(failRes=>{this.$message.error(failRes)})} else {return false;}})},//生成验证码// getValidateCode(){// this.$axios.get('/validateCode').then(res => {// this.loginForm.token = res.data.data.token// this.validateCode = res.data.data.validateCode// })// }},//钩子函数:用于渲染验证码mounted(){this.validateCode =new GVerify('v_container')}
}
</script><style scoped>
#login{background: url('../../../src/assets/loginBackground.jpg') no-repeat center;height: 100%;width: 100%;background-size: cover;position: fixed;
}
html,body{margin:0;padding:0;
}
.login-form{border-radius: 20px;margin: 200px auto;width:350px;border:2px solid lightblue;padding:20px;background: #ffffff;
}
/deep/ .el-form-item__error {color: #e6a23c;width: 200px;display: flex;left:0;
}.link{margin-left: 30px;
}#v_container {width: auto;height: auto;display: inline-flex;position: relative;
}</style>
5.导航栏
src/components/common/Navmenu.vue
<template xmlns:el-col="http://www.w3.org/1999/html"><div class="page-container"><el-row><el-col :span="7" class="menu-col"><el-menu:default-active="$route.path"routertext-color="#222"ref="navMenu"active-text-color="red"style="min-width: 1300px"class="index-menu"mode="horizontal"><el-menu-item v-for="route in routes":key="route.path":index="route.path":class="$route.path==route.path?'is-active':''"><div><i v-bind:class="route.icon"></i>{{ route.navItem }}</div></el-menu-item></el-menu></el-col><el-col :offset="2" :span="8"><el-input type="text" v-model="keywords" prefix-icon="el-icon-search" class="menu-search" placeholder="论文/科研方向" @keyup.enter.native="search"></el-input></el-col><el-col :span="7"><el-dropdown @command="handleCommand"><span class="el-dropdown-link"><el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></el-avatar>{{username}}<i class="el-icon-arrow-down el-icon--right"></i></span><el-dropdown-menu slot="dropdown"><el-dropdown-item command="/team">我的小组</el-dropdown-item><el-dropdown-item command="/comments">我的评论</el-dropdown-item><el-dropdown-item command="/library">我上传的论文</el-dropdown-item><el-dropdown-item command="/note">我的笔记本</el-dropdown-item><el-dropdown-item command="/space">个人空间</el-dropdown-item><el-dropdown-item command="logout" divided>登出</el-dropdown-item></el-dropdown-menu></el-dropdown></el-col></el-row></div>
</template><script>
export default {name: "NavMenu",data() {return {routes: [{path: '/index', navItem: '首页', icon: 'el-icon-s-home'},{path: '/labor', navItem: '我的实验室', icon: 'el-icon-s-help'},{path: '/library', navItem: '论文库', icon: 'el-icon-s-management'},{path: '/space', navItem: '我的', icon: 'el-icon-user-solid'}],username:'',keywords:''}},methods:{handleCommand(command){if(command==='logout')this.logout()this.$router.push(command)},logout(){this.$axios.post('/user/logout',{jwt:window.localStorage.getItem('token')}).then(res=>{if(res.data.data===1) {this.$store.commit('clear')this.$router.replace('/index')}else{this.$message.error('注销失败')}})},},mounted() {this.username=this.$store.state.username}
}
</script>
<style scoped>
.page-container{display: inline-block;
}div,span{caret-color: transparent;
}.el-dropdown-link {cursor: pointer;color: #42b983;
}.menu-search {margin-top: 10px;caret-color: #000000;
}/deep/ .el-input__inner { /*或者 .s2>>>.el-input__inner */border-radius: 40px; /*输入框圆角值*/
}
</style>
值得注意的是:
1、menu打开路由模式:第三行的router
<el-menu:default-active="$route.path"routertext-color="#222"ref="navMenu"active-text-color="red"style="min-width: 1300px"class="index-menu"mode="horizontal">
2、动态匹配导航栏路由项:
<el-menu-item v-for="route in routes":key="route.path":index="route.path":class="$route.path==route.path?'is-active':''">
这里用$route.path和页面数据里的path进行path进行判断,可以使页面刷新不易失动态匹配导航栏的路由项
6、用户基本信息的修改
src/components/Space/Info
<template><el-formref="userForm"class="info-form":model="userForm"label-width="100px"><h3>用户资料</h3><el-row><el-form-item label="用户名"><el-input v-model="userForm.username" :disabled="true"></el-input></el-form-item></el-row><el-row><el-col :span="20"><el-form-item label="密码"><el-input type="password" v-model="userForm.password" :disabled="true"></el-input></el-form-item></el-col><el-button type="primary" @click="passVisible = true">更改</el-button></el-row><el-dialogtitle="密码修改":visible.sync="passVisible"width="30%"><el-formref="changePassForm"class="change-form":model="changePassForm":rules="changePassRules"label-width="80px"><el-form-item label="旧密码" prop="oldPass"><el-input type="password"v-model="changePassForm.oldPass"autocomplete="off"prefix-icon="el-icon-key"placeholder="请输入旧密码"show-password></el-input></el-form-item><el-form-item label="新密码" prop="newPass"><el-input type="password"v-model="changePassForm.newPass"autocomplete="off"prefix-icon="el-icon-key"placeholder="请输入新密码"show-password></el-input></el-form-item><el-form-item label="确认密码" prop="checkPass"><el-input type="password"v-model="changePassForm.checkPass"autocomplete="off"prefix-icon="el-icon-key"placeholder="请再次输入新密码"show-password></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button type="primary" @click="formSubmit('changePassForm')">更改</el-button><el-button @click="reset">取 消</el-button></span></el-dialog><el-row><el-col :span="20"><el-form-item label="电话"><el-input v-model="userForm.phone" :disabled="true"></el-input></el-form-item></el-col><el-button type="primary" @click="phoneVisible = true">更改</el-button></el-row><el-dialogtitle="电话更改":visible.sync="phoneVisible"width="30%"><el-formref="changePhoneForm"class="change-form":model="changePhoneForm":rules="changePhoneRules"label-width="80px"><el-form-item label="电话" prop="phone"><el-input type="text"v-model="changePhoneForm.phone"autocomplete="off"prefix-icon="el-icon-mobile"placeholder="请输入电话号码"style="width: 100%"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button type="primary" @click="formSubmit('changePhoneForm')">更改</el-button><el-button @click="reset">取 消</el-button></span></el-dialog><el-row><el-col :span="20"><el-form-item label="邮箱"><el-input v-model="userForm.email" :disabled="true"></el-input></el-form-item></el-col><el-button type="primary" @click="emailVisible = true">更改</el-button></el-row><el-dialogtitle="邮箱验证":visible.sync="emailVisible"width="30%"><el-formref="changeEmailForm"class="change-form":model="changeEmailForm":rules="changeEmailRules"label-width="80px"><el-form-item label="邮箱" prop="email"><el-input type="text"v-model="changeEmailForm.email"autocomplete="off"prefix-icon="el-icon-message"placeholder="请输入邮箱号"style="width: 68%"></el-input><el-button type="primary" style="width: 30%" @click="send('changeEmailForm')" :disabled="emailButtonDis">{{ buttonName }}</el-button></el-form-item><el-form-item label="验证码" prop="validateCode"><el-input type="text"v-model="changeEmailForm.validateCode"autocomplete="off"prefix-icon="el-icon-check"placeholder="请输入邮箱中的验证码"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button type="primary" @click="formSubmit('changeEmailForm')">验证</el-button><el-button @click="reset">取 消</el-button></span></el-dialog><el-alerttitle="邮箱已验证"type="success"show-icon:closable="false"style="width: 150px; margin-left: 40px"v-if="this.userForm.email!==null"></el-alert><el-alerttitle="邮箱未验证"type="warning"show-icon:closable="false"style="width: 150px; margin-left: 40px"v-if="this.userForm.email===null"></el-alert></el-form>
</template><script>
export default {name: "Info",data() {var oldPassValidate = (rule, value, callback) => {this.$axios.post('/user/checkPassword', {id: window.localStorage.getItem('id'),password: this.changePassForm.oldPass}).then(res => {if (res.data.data === true) {callback()}elsecallback(new Error('密码错误'))}).catch(failRes => {callback(new Error('系统错误')+failRes)})}var passwordValidate = (rule, value, callback) => {var reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[,.#%'+*\-:;^_`0-9A-Za-z]{6,16}$/if (value === '') {callback(new Error('请输入密码'))} else if (!reg.test(value)) {callback(new Error('密码必须为6-16位,必须含有包含数字和英文字母'))} else if (this.changePassForm.checkPass !== '') {this.$refs.changePassForm.validateField('checkPass');}callback()};//自定义的重复密码校验var checkPassValidate = (rule, value, callback) => {if (value === '') {callback(new Error('请再次输入密码'))} else if (value !== this.changePassForm.newPass) {callback(new Error('两次输入密码不一致'))} else {callback()}};var phoneValidate = (rule, value, callback) => {if(value==='') callback(new Error('请输入电话号码'))var phoneReg = /^((13|14|15|16|17|18|19)\d{9})$/if (!phoneReg.test(value))callback(new Error('请输入正确的手机号码格式'))elsecallback()};var emailValidate = (rule, value, callback) => {if (value === '') callback(new Error('请输入邮箱'))var reg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/if (!reg.test(value))callback(new Error('请输入正确的邮箱格式'))callback()};var codeValidate = (rule, value, callback) => {if (value === '')callback(new Error('请输入验证码'))this.$axios.post('/email/check',{id:window.localStorage.getItem('id'),code:this.changeEmailForm.validateCode}).then(res=>{if(res.data.data===true)callback()elsecallback(new Error('验证码错误'))}).catch(failRes=>{this.$message.error('系统异常')callback()})};return {userForm: {username: '',password: '',phone: '',email: '',},changePassForm: {oldPass: '',newPass: '',checkPass: ''},changePassRules: {oldPass: [{validator: oldPassValidate, trigger: 'blur'}],newPass: [{validator: passwordValidate, trigger: 'blur'}],checkPass: [{validator: checkPassValidate, trigger: 'blur'}],},changePhoneForm: {phone:''},changePhoneRules: {phone:[{validator: phoneValidate, trigger:'blur'}]},changeEmailForm: {email: '',validateCode: ''},changeEmailRules: {email: [{validator: emailValidate, trigger: 'blur'}],validateCode: [{validator: codeValidate, trigger: 'blur'},{min: 6, max: 6, message: '验证码长度不正确', trigger: 'blur'}]},buttonName: '发送验证码',emailButtonDis: false,passVisible: false,phoneVisible:false,emailVisible: false,}},mounted() {let url='/user/info/id='+localStorage.getItem('id')this.$axios.get(url).then(res=>{if(res.data.code===200){this.userForm.username=this.$store.state.usernamethis.userForm.password=res.data.data.passwordthis.userForm.email=res.data.data.emailthis.userForm.phone=res.data.data.phone}})},methods: {send(formName) {const _this=thislet timerIdthis.$refs[formName].validateField('email', error => {if (!error) {this.$axios.post('/email/send', {id: window.localStorage.getItem('id'),email: this.changeEmailForm.email}).then(res => {this.$message({showClose:true,message:'发送成功,有效期5分钟',type:'success'})let count = 60_this.changeEmailForm.validateCode=''_this.emailButtonDis = true_this.buttonName=`${count--}s`timerId=window.setInterval(()=>{_this.buttonName=`${count--}s`if(count<=0){window.clearInterval(timerId)_this.buttonName='发送验证码'_this.emailButtonDis=false}},1000)setTimeout(() => {this.emailButtonDis = false}, 60000)}).catch(failRes => {this.$message.error(failRes)this.reset()})} elsereturn false})},formSubmit(formName) {this.$refs[formName].validate((valid) => {if (valid) {var pass = this.userForm.passwordvar phone = this.userForm.phonevar email = this.userForm.emailif (formName === 'changePassForm') {pass = this.changePassForm.newPass} else if (formName === 'changeEmailForm') {email = this.changeEmailForm.email}else{phone =this.changePhoneForm.phone}this.$axios.post('/user/update', {id: window.localStorage.getItem('id'),password: pass,phone: phone,email: email}).then(res => {if (res.data.code === 200) {this.$message.info('修改成功')this.userForm.password = passthis.userForm.email = emailthis.userForm.phone = phonethis.reset()this.passVisible = false} else {this.$message.error('修改失败')return false}}).catch(() => {})} elsereturn false})},reset() {this.changePassForm.oldPass = ''this.changePassForm.newPass = ''this.changePassForm.checkPass = ''this.changeEmailForm.email = ''this.changeEmailForm.validateCode = ''this.emailButtonDis = falsethis.passVisible = falsethis.emailVisible = false}}
}
</script><style scoped>
.info-form {width: 500px
}.change-form {width: 100%
}/deep/ .el-form-item__error {color: #e6a23c;width: 200px;display: flex;left: 0;
}
</style>
1、定时器的制作:
为了限制邮箱验证码的反复发送设置了一个定时器用来限制邮箱验证码的发送:
let count = 60//倒计时
_this.changeEmailForm.validateCode=''//清空表单
_this.emailButtonDis = true//禁用按钮
_this.buttonName=`${count--}s`//按钮显示文案
/*window.setInterval(func,count)可以实现每隔count的时间执行一次func
*/
timerId=window.setInterval(()=>{_this.buttonName=`${count--}s`if(count<=0){window.clearInterval(timerId)_this.buttonName='发送验证码'_this.emailButtonDis=false}
},1000)
/*setTimeout(func,count)在count结束后执行func
*/
setTimeout(() => {this.emailButtonDis = false
}, 60000)
2、各个表单的验证
这次验证加入了和后端的交互
var codeValidate = (rule, value, callback) => {if (value === '')callback(new Error('请输入验证码'))this.$axios.post('/email/check',{id:window.localStorage.getItem('id'),code:this.changeEmailForm.validateCode}).then(res=>{if(res.data.data===true)callback()elsecallback(new Error('验证码错误'))}).catch(failRes=>{this.$message.error('系统异常')callback()})};
7、echarts引入(实验室)
安装
npm init
npm install echarts --save
引入
import echarts from 'echarts'
//需要挂载到Vue原型上
Vue.prototype.$echarts = echarts
遇到了数据动态渲染问题,先run
(54条消息) vue+echarts 实现点击切换数据(监听值的变化重新渲染)_程序员xiaoQ的博客-CSDN博客_echarts 数据切换
8、论文库首页
首先对于论文库必须上传论文所以必须要有一个提交的表单处理新的论文
1、提交新的论文
src/components/library/EditForm
这部分有很多问题难住我了,截止到2022/05/08的晚上我也不知道怎么去处理这个表单的摘要部分和研究方向部分,引用部分的需求仍然需要我去和助教交流,暂时把基本的提交给搞定了;
明天可能需要考虑如何去优化摘要还有研究方向
可以考虑把研究方向变成一个下拉表单
更新到5/30
因为疫情事情太多了导致更新的很慢,这个表单修改了好多bug而且又按照设计精化过一遍,现在这份应该是没有问题的
论文有校验规则,有多选下拉表单,作者还可以按照回车进行分割
<template><div><i class="el-icon-circle-plus-outline" @click="dialogFormVisible=true"></i><el-dialogtitle="添加论文/修改论文数据":visible.sync="dialogFormVisible"@close="clear"width="30%"><el-form v-model="form"style="text-align: left"ref="paperForm":model="form":rules="rules"label-width="80px"><el-form-item label="论文名" prop="title"><el-input v-model="form.title"auto-complete="off"style="caret-color: black; " placeholder="无需添加《》"></el-input></el-form-item><el-form-item label="作者" prop="author"><el-selectv-model="form.author"placeholder="依此使用回车键入作者的名字"filterablemultipleallow-createstyle="width: 100%;caret-color:black":popper-append-to-body="false"default-first-optionref="authorSelect"></el-select></el-form-item><el-form-item label="发表会议" prop="conference"><el-input v-model="form.conference" auto-complete="off" style="caret-color: black"placeholder="请输入发表会议"></el-input></el-form-item><el-form-item label="发表日期" prop="releaseDate"><el-date-picker v-model="form.releaseDate"type="date"value-format="yyyy-MM-dd"placeholder="请选择发布日期"style="width: 100%"></el-date-picker></el-form-item><el-form-item label="论文类型" prop="type"><el-select v-model="form.type"style="width: 100%"placeholder="请选择论文类型"><el-option value="0" label="理论证明型"></el-option><el-option value="1" label="综述型"></el-option><el-option value="2" label="实验型"></el-option><el-option value="3" label="⼯具型"></el-option><el-option value="4" label="数据集型"></el-option></el-select></el-form-item><el-form-item label="论文链接" prop="paperLink"><el-input placeholder="请输入内容" v-model="form.paperLink" style="caret-color: black"><template slot="prepend">Https://</template></el-input></el-form-item><el-form-item label="研究方向" prop="directions"><tree-select:lazy="true":props="props":multiple="true":collapse="true":options="optionData"v-model="form.directions":clearable="true":accordion="false":expandedNode="false"style="width: 100%"@getValue="getValue($event)"@load="loadNodes"></tree-select></el-form-item><el-form-item prop="abstract" label="摘要"><el-inputtype="textarea":autosize="{ minRows: 2, maxRows: 4}"placeholder="请输入摘要"v-model="form.abstract"style="caret-color: black"resize="none"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button type="primary" @click="onSubmit('paperForm')">确定</el-button><el-button @click="clear">取消</el-button></span></el-dialog></div>
</template><script>import TreeSelect from "../utils/TreeSelect";export default {name: "EditForm",components: {TreeSelect},data() {//论文名校验var titleValidate = (rule, value, callback) => {if (value === '')callback(new Error('请输入论文名'))callback()};//作者校验var authorValidate = (rule, value, callback) => {if (value.length===0)callback(new Error('请输入至少一个作者名字'))callback()};//会议校验var conferenceValidate = (rule, value, callback) => {if (value === '')callback(new Error('请输入会议名称'))callback()};//日期校验var dateValidate = (rule, value, callback) => {if (value === '')callback(new Error('请选择论文发表日期'))callback()};//类型校验var typeValidate = (rule, value, callback) => {if (value === '')callback(new Error('请选择论文类型'))callback()};//URL校验var linkValidate = (rule, value, callback) => {if (value === '')callback(new Error('请输入论文链接地址'))callback()};//研究方向校验var directionValidate = (rule, value, callback) => {if (value.length===0)callback(new Error('请输入至少选择一个论文方向'))callback()};//摘要校验var abstractValidate = (rule, value, callback) => {if (value.length===0)callback(new Error('请输入摘要内容'))callback()};return {//表单属性dialogFormVisible: false,formLabWidth: '80px',form: {id: '',title: '',author: [],conference: '',releaseDate: '',type: '',abstract: '',paperLink: '',directions: []},/*表单的验证方式*/rules: {title: [{validator: titleValidate, trigger: 'blur'},{min: 2, max: 30, trigger: 'blur'}],author: [{validator: authorValidate, trigger: 'blur'}],conference:[{validator:conferenceValidate,trigger:'blur'}],releaseDate:[{validator:dateValidate,trigger:'blur'}],type:[{validator:typeValidate,trigger:'blur'}],paperLink:[{validator:linkValidate,trigger:'blur'}],directions:[{validator:directionValidate,trigger:'blur'}],abstract:[{validator:abstractValidate,trigger:'blur'}]},/*研究方向节点属性*/props: {value: 'id',label: 'name',pid: 'parentId',children: 'children',isLeaf: 'leaf'},selectList: []}},methods: {/*清空*/clear() {this.form = {id: '',title: '',author: [],conference: '',releaseDate: '',type: '',abstract: '',paperLink: '',directions: []}this.dialogFormVisible= false},/*加载方向非懒加载*/// getAllDirection(){// this.$axios.get('/direction/all').then(res=>{// this.selectList=res.data.data.directionList// }).catch(failRes=>{// this.$message.error(failRes)// })// },/*提交*/onSubmit(formName) {var author=this.form.author.join(' ')var directionId=JSON.parse(JSON.stringify(this.form.directions))this.$refs[formName].validate((valid) => {if (valid) {this.$axios.post('/paper/upload', {title: this.form.title,author: author,conference: this.form.conference,releaseDate: this.form.releaseDate,type: this.form.type,paperLink: this.form.paperLink,directionId: directionId,id: this.$store.state.id,uploadDate: this.currentDate(),abstract: this.form.abstract}).then(res => {this.dialogFormVisible = falsethis.$emit('onSubmit')}).catch(() => {})} else {return false}})},/*加载时间*/currentDate() {let d = new Date();let year = d.getFullYear();let month = d.getMonth();month = month + 1 > 12 ? 1 : month + 1;month = month > 9 ? month : "0" + month.toString();let day = d.getDate();let hour = d.getHours();hour = hour > 9 ? hour : "0" + hour.toString();let minute = d.getMinutes();minute = minute > 9 ? minute : "0" + minute.toString();let second = d.getSeconds();return `${year}-${month}-${day} ${hour}:${minute}:${second}`;},/*懒加载节点*/loadNodes(node, resolve) {let self = thislet treeData = []if (node.level === 0) {self.$axios.get(`/direction/getChildren/0`).then(res => {res.data.data.forEach(e => {e.id = e.directionIdtreeData.push(e)})resolve(treeData)}).catch(fail => {resolve([])})} else {self.$axios.get(`/direction/getChildren/${node.data.id}`).then(res => {res.data.data.forEach(e => {e.id = e.directionIdtreeData.push(e)})resolve(treeData)}).catch(fail => {resolve([])})}},//调试用getValue(value){this.form.directions=valueconsole.log(this.form.directions)}},computed: {/*遍历selectList的所有选项,然后将所有的子元素挂载在父元素之下,然后返回前一层*/optionData() {let cloneData = JSON.parse(JSON.stringify(this.selectList))return cloneData.filter(father => {let branchArr = cloneData.filter(child => father.id == child.parentId); // 返回每一项的子级数组branchArr.length > 0 ? (father.children = branchArr) : ""; //给父级添加一个children属性,并赋值return father.parentId == 0; //返回第一层})}},mounted() {// this.$refs.authorSelect.blur()}
}
</script><style scoped>
.el-icon-circle-plus-outline {margin: 0 0 0 20px;font-size: 30px;float: right;caret-color: transparent;
}div, span {caret-color: transparent;
}/*隐藏右侧下拉表单箭头*/
/deep/ .el-select .el-input .el-select__caret {display: none !important;
}/*隐藏下拉表单
*/
/deep/ .el-select-dropdown {display: none !important;
}</style>
2、下拉表单子组件
5.19号自己手写了一个下拉表单,当然也参考了博客上的写法,将el-tree和el-select包装起来,并且实现了懒加载、只有子节点能够现实的多种设置
注释和复用性有些差,到时候回来在refine一下
注意一下这个地方需要安装一下less的样式加载器的,需要进行less的预加载
src/utils/TreeSelect.vue
<template><el-select:value="valueTitle":clearable="clearable":multiple="multiple":collapse-tags="collapse"@remove-tag="handleRemoveTag"@clear="clearHandle"><el-inputclass="selectInput":placeholder="placeholder"v-model="filterText"></el-input><el-option :value="valueId" :label="label" class="options"><el-tree id="tree-option"ref="selectTree":accordion="accordion":data="options":show-checkbox="multiple":check-strictly="true":props="props":node-key="props.value":default-expanded-keys="defaultExpandedKey":default-expand-all="expand":auto-expand-parent="expandParent":expand-on-click-node="expandNode":lazy="lazy":load="loadNodes":filter-node-method="filterNode"@node-click="handleCheckClick"@check-change="handleCheckClick"></el-tree></el-option></el-select>
</template><script>
export default {name: "TreeSelect",props:{//配置项props:{type:Object,default:()=>{return{value:'id',label:'title',children:'children',isLeaf:'leaf'}}},//选项列表的数据options:{type:Array,default:()=>{return []}},//初始值value:{type: [String, Number, Boolean, Array],default:()=>{return null}},// 宽度width:{type:String,default:()=>{return '270px'}},// 是否多选multiple:{type:Boolean,default:()=>{return true}},// 是否展示复选框checkbox:{type:Boolean,default:()=>{return false}},// 多选是否将选中值按文字表示collapse:{type:Boolean,default:()=>{return true}},// 是否可清空clearable:{type:Boolean,default:()=>{return true}},// 是否显示checkshow:{type:Boolean,default:()=>{return false}},// 是否自动展开accordion:{type:Boolean,default:()=>{return false}},// 搜索框的提示placeholder:{type:String,default:()=>{return "检索关键字"}},// 是否展开所有节点expand:{type:Boolean,default:()=>{return false}},// 展开子节点是否需要展开父节点expandParent:{type:Boolean,default:() =>{return true}},// 是否在点击节点时展开或者收缩节点expandNode:{type:Boolean,default:() =>{return true}},lazy:{type:Boolean,default:()=>{return false}}},data(){return{valueTitle:'',//当前选中的labelvalueId:this.value,//当前选中的valuedefaultExpandedKey:[],filterText:'',label:''}},mounted() {this.initTreeSelect()},methods:{/*初始化函数*/initTreeSelect(){if(this.valueId && !this.multiple)//单选{var node = this.$refs.selectTree.getNode(this.valueId);if(node){this.valueTitle = node.data[this.treeProps.label]; // 初始化显示this.defaultExpandedKeys = [this.valueId]; // 设置默认展开this.$nextTick(() => {this.$refs.selectTree.setCurrentKey(this.valueId); // 设置默认选中})}}else if(this.valueId && this.multiple) {//多选this.defaultExpandedKeys =this.defaultExpandedKeythis.$nextTick(()=>{this.$refs.selectTree.setChecked(this.valueId); // 设置默认选中})}this.initScroll()},/*初始化滚动条*/initScroll(){this.$nextTick(()=>{let scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0]let scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar')scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;'scrollBar.forEach(ele => ele.style.width = 0)})},/*单选*/handleNodeClick(node){this.valueTitle = node[this.props.label]this.valueId =node[this.props.value]//返回父组件this.$emit('getValue',this.valueId)this.defaultExpandedKey=[]},/*多选,节点发生变化时回调*/handleCheckClick(data,checked,childrenCheck){this.valueId = this.$refs.selectTree.getCheckedKeys()var checkedNodes = this.$refs.selectTree.getCheckedNodes()this.valueTitle = checkedNodes.map((node)=>{return node[this.props.label]})this.defaultExpandedKeys =[]this.$emit('getValue',this.valueId)},/*清除选中的选项/checkbox*/clearHandle(){this.valueTitle=''//当前选中的labelthis.valueId=''//当前选中的valuethis.defaultExpandedKey=[]if(this.multiple){this.$refs.selectTree.setChecked([])}else{this.$refs.selectTree.setCurrentKey(null)}this.$emit('getValue',null)},/*多选中除去任意选中*/handleRemoveTag(val){var checkNodes = this.$refs.selectTree.getCheckedNodes();var node=checkNodes.find(node => node[this.props.label] ===val)this.$refs.selectTree.setChecked(node[this.props.value],false)},/*过滤方法*/filterNode(value, data) {if (!value) return truereturn data.name.indexOf(value) !== -1},/*加载节点方法*/loadNodes(node,resolve){this.$emit('load',node,resolve)}},watch:{value(){this.valueId=this.valuethis.initTreeSelect()},filterText(val) {this.$refs.selectTree.filter(val);}},computed:{selectStyle(){return{width:`${this.width}`}}}}
</script><style scoped>
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {height: auto;max-height: 274px;padding: 0;overflow: hidden;overflow-y: auto;
}.el-scrollbar .el-select-dropdown__item.selected {font-weight: normal;
}/*横向滚动条*/
.el-scrollbar__bar.is-horizontal {height: 6px;left: 2px;
}/*纵向滚动条*/
.el-scrollbar__bar.is-vertical {/*width: 6px;*//*top: 2px;*/display: none;
}/*字体和大小*/
.custom-tree-node {font-family:"Microsoft YaHei";font-size: 14px;position: relative;
}/*原生 el-tree-node的div是块级元素,需要改为inline-block,才能显示滚动条 */
.treeSelect .el-tree >.el-tree-node {display: inline-block;min-width: 100%;
}ul li ::v-deep .el-tree .el-tree-node__content {height: auto;padding: 0 20px;
}.el-tree-node__label {font-weight: normal;
}.el-tree ::v-deep .is-current .el-tree-node__label {color: #1B65B9;font-weight: 700;
}.el-tree ::v-deep .is-current .el-tree-node__children .el-tree-node__label {color: #606266;font-weight: normal;
}</style><style lang="less">
.el-tree-node {.is-leaf + .el-checkbox .el-checkbox__inner {display:inline-block;}.el-checkbox__input> .el-checkbox__inner {display:none;}
}
</style>
3、面包屑导航栏
这个主要用于通过研究方向分类查询论文
5.10目前正在调试
更新到5.30号,基础逻辑已经搞定了,现在就是和和后端数据交互仍然有问题,
更新到6.1号,现在面包屑导航栏已经基本没有问题,可以根据方向显示论文,点击论文库返回首页
src/library/Breadcrumb.vue
<template><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item v-for="(item,i) in breadList":key="i":index="item.name"><el-dropdown :hide-on-click="false"@command="handleCommand"><span @click="paperReload" style="font-weight: bolder;cursor: pointer" v-if="i===0">{{item.name}}</span><span class="el-dropdown-link" v-else>{{ item.name }}</span><el-dropdown-menu slot="dropdown"><el-dropdown-item v-for="(child,j) in item.children":key="j":index="child.name":command="child">{{child.name}}</el-dropdown-item></el-dropdown-menu></el-dropdown></el-breadcrumb-item></el-breadcrumb></template><script>
export default {inject:['reload'],name: "Breadcrumb",data() {return {breadList:[{directionId:0,parentId:-1,name:'论文库',leaf:false,index:0,children:[]}],currentId:0,activeIndex:0}},methods:{/*处理面包导航下拉表单*/handleCommand(command) {//把list回溯到command的父节点处let parentId=command.parentIdlet newList=[]for(let i=0;i<this.breadList.length;i++){newList.push(this.breadList[i])if(this.breadList[i].directionId===parentId){this.activeIndex=ibreak}}this.breadList=newList//选中的选项index为父节点index+1command.index=this.activeIndex+1//获取子节点if(!command.leaf) this.getChildren(command.directionId)this.breadList.push(command)this.currentId=command.directionIdthis.activeIndex++//向父组件传commandId的值this.$emit('selectChange',command.directionId)},/*获得子节点*/getChildren(id){this.$axios.get(`/direction/getChildren/${id}`).then(res=>{this.breadList[this.activeIndex].children=res.data.data}).catch(failRes=>{})},/*刷新界面*/paperReload(){this.reload()}},mounted(){this.getChildren(this.currentId)}
}
</script><style scoped>
div, span {caret-color: transparent;
}</style>
4、论文首页
然后就是现阶段的论文首页了,仍然在调试阶段
更新到5.30,现在后端那边没有给出按分类处理论文的接口,
更新到6.1,对整个论文库进行大改,路由发生大改,新建了一批论文库的子页面,同时论文库首页由Library编程LibraryIndex,而Library是其中的一个子路由
library经过优化可以通过主要的loadAllPapers函数来加载所需要的论文,并且可以按照相关的排序
router/main.js(部分)
{path:'/library',name:'LibraryIndex',redirect: '/library/papers',component: LibraryIndex,meta:{requireAuth:true},children: [{path:'/library/papers',name:'Library',component: Library,meta: {requireAuth: true}},{path:'/library/content',name:'Content',component: Content,meta: {requireAuth: true}}]},
src/library/LibraryIndex.vue
<template><el-container><el-aside width="200px"><side-menu></side-menu></el-aside><el-main><router-view></router-view></el-main></el-container>
</template><script>
import SideMenu from "./SideMenu";
import Library from "./Library";
import Content from "./Content";export default {name: "LibraryIndex",components:{SideMenu,Library,Content}
}
</script><style scoped></style>
src/library/LibraryIndex.vue
<template><el-container><el-header height="100px"><el-row><el-col :span="10"><breadcrumb @selectChange="loadPapersById($event)"></breadcrumb></el-col><el-col><edit-form ref="edit" @onSubmit="loadAllPapers"></edit-form></el-col></el-row><el-divider></el-divider><el-row :gutter="20" type="flex" justify="end"><el-col :xs="8" :sm="6" :md="4" :lg="3"><div><el-dropdown @command="handleSort"><el-button type="primary">{{ this.sortMethod.name }}<i class="el-icon-arrow-down el-icon--right"></i></el-button><el-dropdown-menu slot="dropdown"><el-dropdown-item v-for="(option,i) in this.sortOption":key="i":index="option.name":command="option">{{ option.name }}</el-dropdown-item></el-dropdown-menu></el-dropdown></div></el-col><el-col :xs="8" :sm="6" :md="4" :lg="3"><div style="margin-left: 20px"><el-button type="primary" @click="changeOrder">{{ this.sortOrder.name }}</el-button></div></el-col></el-row></el-header><el-main><el-tooltip effect="dark" placeholder="right"v-for="item in papers.slice((currentPage-1)*pageSize,currentPage*pageSize)":key="item.id"><p slot="content" style="font-size: 14px;margin-bottom: 6px;">{{ item.title }}</p><p slot="content" style="font-size: 13px;margin-bottom: 6px"><span>作者:{{ item.author }}</span><span>发布时间:{{ item.releaseDate }}</span></p><p slot="content" style="font-size: 13px;margin-bottom: 6px"><span>上传者:{{ item.uploader }}</span><span>上传时间:{{ item.uploadDate }}</span></p><!-- <p slot="content" style="font-size: 13px;margin-bottom: 6px">--><!-- <span v-for="did in item.directionId" >{{directionName(did)}}</span>--><!-- </p>--><p slot="content" style="font-size: 13px;margin-bottom: 6px"><span>{{ item.conference }}</span><span>{{ item.type }}</span></p><el-card class="paper" shadow="hover"><div slot="header" class="paper-title"><el-link target="_blank" type="primary" @click="handlePaperLink(item.paperLink)"><span style="font-size: x-large">{{ item.title }}</span></el-link></div><div class="paper-info"><i class="el-icon-user"/> <span class="item">{{ item.author }}</span><i class="el-icon-paperclip"/><span class="item">{{ item.conference }}</span><i class="el-icon-date"/><span class="item">{{ item.releaseDate }}</span></div><!-- <div class="paper-domain">--><!-- <span v-for="dir in item.domains" v-if="dir.leaf===true">{{dir.name}}</span>--><!-- </div>--><div class="paper-abstract"><span>{{ item.abstract | ellipsis }}</span></div><div class="paper-footer">上传者:{{ item.uploader }},上传时间:{{ item.uploadDate }}</div></el-card></el-tooltip><el-row><el-pagination@current-change="handleCurrentChange":current-page="currentPage":page-size="pageSize":total="papers.length"class="pagination"></el-pagination></el-row><el-empty description="还没有论文哦" v-if="papers.length===0"></el-empty></el-main></el-container>
</template><script>
import Breadcrumb from "./Breadcrumb";
import EditForm from "./EditForm";export default {name: "Library",components: {Breadcrumb, EditForm},data() {return {//论文相关currentPage: 1,pageSize: 15,papers: [],//方向相关currentDirection: 0,//排序相关sortMethod: {name: '上传时间', command: 'releaseDate'},sortOrder: {name: '升序', order: 'asc'},sortOption: [{name: '上传时间', command: 'releaseDate'},{name: '发布时间', command: 'releaseDate'},]}},filters: {ellipsis(value) {if (!value) return '';if (value.length > 200)return value.slice(0, 200) + '...';return value}},methods: {/*加载所有论文*/loadAllPapers() {this.$axios.get(`/paper/getAll/directionId=${this.currentDirection}/sortMethod=${this.sortMethod.command}/order=${this.sortOrder.order}`).then(res => {this.papers = res.data.data}).catch(failRes => {this.papers=[]console.log(failRes)})},/*按照方向加载论文*/loadPapersById(value) {this.currentDirection = valuethis.loadAllPapers()},/*处理页号*/handleCurrentChange(page) {this.currentPage = page},//排序相关/*处理排序方式*/handleSort(command) {this.sortMethod = commandthis.loadAllPapers()},changeOrder() {if (this.sortOrder.order === 'asc') {this.sortOrder.order = 'desc'this.sortOrder.name = '降序'} else {this.sortOrder.order = 'asc'this.sortOrder.name = '升序'}this.loadAllPapers()},//查看论文相关信息handlePaperLink(url){this.$router.push({name:'Content',params:{url:url}})}},mounted() {this.loadAllPapers()}
}
</script><style scoped>
.paper-title {display: flex;right: 0;
}.paper-info {display: flex;right: 0;margin-bottom: 20px;
}.paper-domain {display: flex;right: 0;margin-bottom: 20px;
}.paper-abstract {display: inline-block;text-align: left;right: 0;margin-bottom: 20px;
}.paper-footer {display: flex;right: 0;color: #888888;font-size: x-small;
}.item {margin-left: 10px;margin-right: 10px;
}.pagination {position: fixed;bottom: 5%;right: 10%;
}
</style>
5、论文内容
主要方式是library界面通过传参把相关的url参数传到论文内容中来,然后这个页面主要通过ifram进行页面加载,具体代码如下
更新到6.1:iframe确实不太会用明天再学一下,然后把笔记本的抽屉给加上看看能不能行
src/library/Content.vue
<template><div><iframe :src="url" id="paperContent" scrolling="no" frameborder="0"class="content"></iframe></div></template><script>
export default {name: "Content",data () {return {url:'',}},mounted(){/*初始化链接*/this.url=this.$route.params.url/*** iframe-宽高自适应显示*/function changeMobsfIframe(){const mobsf = document.getElementById('paperContent');const deviceWidth = document.documentElement.clientWidth;const deviceHeight = document.documentElement.clientHeight;mobsf.style.width = (Number(deviceWidth)-360) + 'px'; //数字是页面布局宽度差值mobsf.style.height = (Number(deviceHeight)-100) + 'px'; //数字是页面布局高度差}changeMobsfIframe()window.onresize = function(){changeMobsfIframe()}}
}
</script><style scoped>
div{caret-color: transparent;
}
.content{position:absolute;top:64px;left: 240px;right:0px;bottom:100px;height: auto;width: auto;transform-origin: 90% 83%;
}
</style>
console.log(failRes)})
},
/*按照方向加载论文*/
loadPapersById(value) {this.currentDirection = valuethis.loadAllPapers()
},
/*处理页号*/
handleCurrentChange(page) {this.currentPage = page
},//排序相关/*处理排序方式*/
handleSort(command) {this.sortMethod = commandthis.loadAllPapers()
},changeOrder() {if (this.sortOrder.order === 'asc') {this.sortOrder.order = 'desc'this.sortOrder.name = '降序'} else {this.sortOrder.order = 'asc'this.sortOrder.name = '升序'}this.loadAllPapers()
},//查看论文相关信息
handlePaperLink(url){this.$router.push({name:'Content',params:{url:url}})
}
},
mounted() {
this.loadAllPapers()
}
}
##### 5、论文内容主要方式是library界面通过传参把相关的url参数传到论文内容中来,然后这个页面主要通过ifram进行页面加载,具体代码如下****更新到6.1:iframe确实不太会用明天再学一下,然后把笔记本的抽屉给加上看看能不能行**src/library/Content.vue**
数据库期末项目开发心得(持续更新中)相关推荐
- 尚品汇项目笔记(持续更新中)
项目网络教学视频链接:尚硅谷VUE项目实战,前端项目-尚品汇(大型\重磅)_哔哩哔哩_bilibili 目录 一. 使用vue-cli脚手架去初始化项目 二.项目的其他配置 三.项目路由分析 四.创建 ...
- 2018年阿里巴巴重要开源项目汇总(持续更新中)...
摘要: 云栖社区特在2018年年末,将阿里巴巴的一些重要的开源项目进行整理,希望对大家有所帮助. 开源展示了人类共同协作,成果分享的魅力,每一次技术发展都是站在巨人的肩膀上,技术诸多创新和发展往往就是 ...
- 转发 ---- 2018年阿里巴巴重要开源项目汇总(持续更新中)
转发自segmentfault https://segmentfault.com/a/1190000017346799 前端 1.数据驱动的高交互可视化图形语法 AntV - G2 G2 是一套基于 ...
- 【数据库及其应用】(持续更新中)
数据库及其应用 第1章SQL基本语言 1.1 SQL数据的定义 1.1.1 数据库的建立和删除 建数据库 CREATE DATABASE database_name /*此处省略路径.文件长度等等参数 ...
- 后端开发规范(持续更新中...)
java 对于持久层的实体,使用包装类型,不使用基本类型,也不要设置默认值. 答:有些ORM框架中使用实体去更新字段,会将默认值更新到数据库中.然而数据库中是有有效值的. 设计中类尽量采用单一职责原则 ...
- 数据库分库分表(持续更新中)
今天学习了数据库分表分库,感觉记录下一些东西以便以后的查看. 1.数据库建立索引,可以加快表数据的查询,但是过多的索引,会占用大量的内存,维护难度较大,因为索引底层的算法是B-tree,树的特点就是查 ...
- Android开发学习持续更新中
Android开发 单个Activity界面内的操作 控件1TextView控件使用 控件2Button控件使用 1首先对于android的按键格式 2对按键监听事件进行绑定 控件3EditText文 ...
- hbase数据库的一些基本操作(持续更新中)
进入到hbase安装目录的bin文件下,运行./start-hbase.sh 既可以启动,启动不了是因为配置原因,具体自己搜索,输入hbase shell 即可进入hbase'数据库的命令环境. 1. ...
- 优秀的开源项目一览(持续更新中...)
涵盖三个版本: 1.springboot单体项目: 2.springboot+vue前后端分离版本: 3.springCloudAlibaba微服务版本+前后端分离. 项目名称 Snowy快速开发平台 ...
最新文章
- 中国数字化进程比发达国家快,小程序让我感到自豪 | IT领袖峰会
- ASP.NET页面的生命周期
- python的pypi安装_python pip及安装包安装
- php radio js,如何使用JavaScript设置radio选中的示例
- VS code gopls requires a module at the root of your workspace
- python调用c++动态库_python调用c++开发的动态库
- NYOJ759 你知道这个规律吗
- 大小端详解(判断+转换)
- hightopo学习笔记--2D编辑器使用
- Android 特效直播实现原理解析
- MCU方案分享----按摩贴方案
- 无线路由器的五种工作模式
- 如何设置html背景,如何给html设置背景
- 二、循环神经网络(RNN与LSTM)
- 布法罗纽约州立大学计算机排名,2019年QS世界大学排名纽约州立大学布法罗分校排名第313...
- SQL Server Arithmetic overflow error converting nvarchar to data type numeric
- Try{}里有一个return语句,那么紧跟在这个try后面的finally{}里的code会不会执行,什么时候执行,在return之前还是之后?
- ensp——防火墙安全策略配置实验
- 【Linux杂篇】经常登录Linux,用户密码背后的知识了解一下
- WSL2使用USB设备
热门文章
- python表示分数_python分数怎么表示
- ERTMS架构及UIC对高速铁路的描述
- 艺术风格转换之《A Neural Algorithm of Artistic Style》
- 在Android上使用libgdx-box2d物理引擎
- 小米手机如此难买:真缺货,假缺货?
- 【C++中单引号‘ ‘的意义及作用】
- python爬取自如网房源信息
- realme x2 深度测试打不开_Realme X2/X2 Pro支持深度测试-官方解锁BootLoader图文教程...
- 内容创业进入淘汰期,电商平台成为最后一波红利?
- 关于MySQL的理解?