赞
踩
采用vue2+element构建网页主体,node作为本地服务器,mysql作为数据库,echarts做了一丁点可视化.
项目路径
取消lint以及配置代理服务器解决跨域问题
可以在服务器app.js引入cors模块解决跨域,就不用配置代理了.
const {defineConfig} = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, lintOnSave: false, devServer: { proxy: { '/register': { target: 'http://localhost:5000/register', changeOrigin: true, pathRewrite: {'^/register': ''} }, '/updatePass':{ target: 'http://localhost:5000/updatePass', pathRewrite: {'^/updatePass':''} }, '/deleteSpecie':{ target: 'http://localhost:5000/deleteSpecies', changeOrigin: true, pathRewrite: {'^/deleteSpecie':''} }, '/addSpecie':{ target: 'http://localhost:5000/addSpecies', changeOrigin: true, pathRewrite: {'^/addSpecie':''} }, '/selectSpecie':{ target: 'http://localhost:5000/selectSpecies', changeOrigin: true, pathRewrite: {'^/selectSpecie':''} }, '/updateSpecie':{ target: 'http://localhost:5000/updateSpecies', changeOrigin: true, pathRewrite: {'^/updateSpecie':''} }, '/setAvatar':{ target: 'http://localhost:5000/setAvatar', changeOrigin: true, pathRewrite: {'^/setAvatar':''} } } } })
//1.引入express const express = require('express') const mysql = require("mysql"); const multer = require("multer") const cors = require('cors') const bodyParser = require("body-parser"); const fs = require("fs"); const dayjs = require("dayjs"); //2.创建应用对象 const app = express() app.use(bodyParser.urlencoded({extended: false})); //parse application/x-www-form-urlencoded app.use(bodyParser.json()); app.use(cors()) let objMulter = multer({dest: "./public/upload"}); //实例化multer,传递的参数对象,dest表示上传文件的存储路径 app.use(objMulter.any())//any表示任意类型的文件 // app.use(objMulter.image())//仅允许上传图片类型 //静态资源托管 app.use(express.static("./public/upload")); //创建mysql连接对象 const connection = mysql.createConnection({ host: 'localhost', port: 3306, user: 'root', password: '123456', database: 'bigevent', multipleStatements: true }) connection.connect() //修改头像 app.post('/setAvatar', (req, res) => { let oldName = req.files[0].filename;//获取名字 let originalname = req.files[0].originalname;//originnalname其实就是你上传时候文件起的名字 //给新名字加上原来的后缀 let newName = req.files[0].originalname; fs.renameSync('public/upload/' + oldName, './public/upload/' + newName);//改图片的名字注意此处一定是一个路径,而不是只有文件名 // res.send({ // err: 0, // url:"http://localhost:5000/public/upload/" + // newName // }); const url = "http://localhost:5000/" + newName console.log(url) console.log(typeof url) const username = req.body.name console.log(username) const sql = `update account set avatar = '${url}' where username = '${username}'` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) }) res.send(url) }) //检查登录信息 app.get('/accounts', (request, response) => { //设置响应头 允许跨域 response.setHeader('Access-Control-Allow-Origin', "*") //get方式通过params传递的参数可以直接通过request.query获取到 const name = request.query.username const password = request.query.password console.log(request.query.username) console.log(request.query.password) const sql = `select * from bigevent.account where username='${name}' and password='${password}'` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //检查注册用户的用户名是否合法 即在数据库中是否存在这个用户名 app.get('/checkName', (request, response) => { //设置响应头 允许跨域 response.setHeader('Access-Control-Allow-Origin', "*") const name = request.query.username console.log(request.query.username) const sql = `select * from bigevent.account where username='${name}'` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //注册用户 往数据库插入用户信息 app.post('/register', (request, response) => { //设置响应头 允许跨域 response.setHeader('Access-Control-Allow-Origin', "*") const username = request.body.username const password = request.body.password console.log(username) console.log(password) const sql = `insert into account(username,password) values('${username}','${password}')` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(result) }) console.log('有人请求服务器了') }) //修改用户信息 app.post('/updateInfo', (request, response) => { // console.log(1) // console.log(request.body.pass) const age = request.body.age const gender = request.body.gender const newName = request.body.newName const oldName = request.body.oldName const maritalStatus = request.body.maritalStatus console.log(age) console.log(gender) console.log(newName) console.log(oldName) const sql = `update account set gender = '${gender}',age=${age},username='${newName}',marital_status='${maritalStatus}' where username = '${oldName}'` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(result) }) }) //修改用户密码 app.post('/updatePass', (request, response) => { console.log(request.body.pass) const newPass = request.body.pass const username = request.body.username const sql = `update account set password = '${newPass}' where username = '${username}'` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(result) }) }) //获取文章分类 app.get('/getSpecies', (request, response) => { //设置响应头 允许跨域 response.setHeader('Access-Control-Allow-Origin', "*") const sql = `select * from article_species` connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //删除文章分类 app.post('/deleteSpecies', (request, response) => { //设置响应头 允许跨域 // response.setHeader('Access-Control-Allow-Origin', "*") const id = request.body.id console.log(id) const sql = `delete from article_species where id = ${id}` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //添加文章分类 app.post('/addSpecies', (request, response) => { //设置响应头 允许跨域 // response.setHeader('Access-Control-Allow-Origin', "*") const name = request.body.name const nickname = request.body.nickname const sql = `insert into article_species(name,nickname) values('${name}','${nickname}')` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //搜索文章分类 app.post('/selectSpecies', (request, response) => { //设置响应头 允许跨域 // response.setHeader('Access-Control-Allow-Origin', "*") const name = request.body.name const sql = `select * from article_species where name = '${name}'` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //更新分类内容 app.post('/updateSpecies', (request, response) => { //设置响应头 允许跨域 // response.setHeader('Access-Control-Allow-Origin', "*") const name = request.body.name const id = request.body.id const nickname = request.body.nickname console.log(id) console.log(name) console.log(nickname) const sql = `update article_species set name = '${name}', nickname='${nickname}' where id = ${id}` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //搜索文章 app.post('/selectArticles', (request, response) => { //设置响应头 允许跨域 // response.setHeader('Access-Control-Allow-Origin', "*") const specie = request.body.specie const sql = `select * from articles where specie = '${specie}'` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //发布文章 app.post('/publishArticle', (request, response) => { //设置响应头 允许跨域 response.setHeader('Access-Control-Allow-Origin', "*") const title = request.body.title const content = request.body.content const author = request.body.author const pubtime = dayjs().format('YYYY-MM-DD HH:mm:ss') const specie = request.body.specie const sql = `insert into articles(title,specie,pubtime,content,author) values('${title}','${specie}','${pubtime}','${content}','${author}')` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //删除文章 app.post('/deleteArticle', (request, response) => { //设置响应头 允许跨域 // response.setHeader('Access-Control-Allow-Origin', "*") const id = request.body.id console.log(id) const sql = `delete from articles where id = ${id}` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //修改文章 app.post('/editArticle', (request, response) => { //设置响应头 允许跨域 response.setHeader('Access-Control-Allow-Origin', "*") const title = request.body.title const content = request.body.content const id = request.body.id const pubtime = dayjs().format('YYYY-MM-DD HH:mm:ss') const specie = request.body.specie const sql = `update articles set title ='${title}', specie='${specie}',pubtime='${pubtime}',content='${content}' where id = ${id}` console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //根据条件查询用户数目并返回 app.get('/statistics', (request, response) => { //设置响应头 允许跨域 response.setHeader('Access-Control-Allow-Origin', "*") // const sql = `select * from article_species where id = 1` // const sql1 = `select * from article_species where id = 2` connection.query( // 'select count(1) from article_species;' + 'select count(1) from account where age <18;'+ 'select count(1) from account where age >=18 and age <=30;'+ 'select count(1) from account where age >=30 and age <=50;'+ 'select count(1) from account where age >=50 and age <=70;'+ 'select count(1) from account where age >=70;' , (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //根据条件查询文章数目并返回 app.post('/articleStatistics', (request, response) => { //设置响应头 允许跨域 response.setHeader('Access-Control-Allow-Origin', "*") const species = request.body.species; console.log(species) let sql=`` for (const specie of species) { console.log(specie) sql +=`select count(1) from articles where specie = '${specie}';` } console.log(sql) connection.query(sql, (error, result) => { if (error) { console.error(error) return } console.log(result) response.send(JSON.stringify(result)) }) console.log('有人请求服务器了') }) //4.监听端口启动服务 app.listen(5000, () => { console.log('服务已经启动,监听端口号5000') })
import Vue from "vue"; import Router from "vue-router"; import Register from "@/pages/Register"; import Login from "@/pages/Login"; import BackAdmin from "@/pages/BackAdmin"; import ResetPassword from "@/pages/ResetPassword"; import ArticleSpecies from "@/pages/ArticleSpecies"; import setAvatar from "@/pages/setAvatar"; import Statistics from "@/pages/Statistics"; import updateInfo from "@/pages/updateInfo"; import articleList from "@/pages/articleList"; import publishArticle from "@/pages/publishArticle"; import articleDetail from "@/pages/articleDetail"; import editArticle from "@/pages/editArticle"; Vue.use(Router) const router = new Router({ routes:[ { path:'/', component:Login, meta:{ title:'后台管理登录', isAuth:true } }, { path:'/register', component:Register, meta:{ title:'用户注册' } }, { path:'/admin', component:BackAdmin, meta:{ title:'后台管理主页', auth:true }, //设置重定向到统计页面 redirect:{ name:'statistics' }, children:[ { path:'resetPass', name:'resetPass', component:ResetPassword, meta:{ title:'修改密码', auth:true } }, { path:'articleSpecies', name:'species', component:ArticleSpecies, meta:{ title:'文章分类', isAuth:true, auth:true } }, { path:'setAvatar', name:'setAvatar', component:setAvatar, meta:{ title:'修改头像', auth:true } }, { path:'statistics', name:'statistics', component:Statistics, meta:{ title:'统计', auth:true } }, { path:'updateInfo', name:'updateInfo', component:updateInfo, meta:{ title:'修改个人信息', auth:true } }, { path:'articleList', name:'articleList', component:articleList, meta:{ title:'文章列表', auth:true } }, { path:'publishArticle', name:'publishArticle', component:publishArticle, meta:{ title:'发布文章', auth:true } }, { path:'articleDetail', name:'articleDetail', component:articleDetail, meta:{ title:'文章详情', auth:true }, }, { path:'editArticle', name:'editArticle', component:editArticle, meta:{ title:'修改文章', auth:true }, }, ] } ] }) router.beforeEach((to,from,next)=>{ //未登录前往登录后页面跳转到登录页 if(to.meta.auth &&!localStorage.getItem('username')){ router.push('/') return } if(to.meta.title){ document.title = to.meta.title } next() }) //在登录页禁用后退按钮 指定后退页面为登录页 router.afterEach((to,from)=>{ if(to.meta.isAuth){ history.pushState(null, null, location.protocol + '//' + location.host + '/#' + to.path) } }) export default router
<template> <div> <el-container> <el-main> <el-row> <el-input placeholder="请输入用户名" v-model="username" prefix-icon="el-icon-user-solid"> </el-input> </el-row> <el-row> <el-input placeholder="请输入密码" v-model="password" show-password prefix-icon="el-icon-lollipop" @keyup.enter="handleLogin"></el-input> </el-row> <el-row> <el-button type="primary" style="width: 100%" @click="handleLogin" >登录</el-button> </el-row> <el-row> <router-link to="/register">去注册</router-link> </el-row> </el-main> </el-container> </div> </template> <script> import axios from "axios"; export default { name: "Login", data(){ return{ username:'', password:'', timer:null } }, methods:{ //添加按键节流 handleLogin(){ if(this.timer){ return } this.timer = setTimeout(()=>{ this.login() this.timer = null },1000) }, login(){ if((!this.username || !this.password)){ this.$message.error('用户名或密码不能为空!'); }else { axios.get('http://localhost:5000/accounts',{ params:{ username:this.username, password:this.password } }).then( response=>{ console.log(response.data) if(!response.data.length){ this.$message.error('用户名或密码不正确!'); this.password='' this.username='' }else { localStorage.setItem('username',this.username) localStorage.setItem('password',this.password) console.log(response.data[0].avatar) localStorage.setItem('url',response.data[0].avatar) this.$message({ message: '登陆成功~', type: 'success', }) //将数据存入state不合理页面刷新数据消失 // this.$store.dispatch('addName',this.username) // this.$store.dispatch('addPass',this.password) setTimeout(()=>{ this.$router.push('/admin') },2000) } }, error=>{ console.log(error.message) } ) } } }, mounted() { } } </script> <style scoped lang="less"> .el-container { width: 350px; margin: 300px auto 0; //padding: 50px; background-color: rgba(255,255,255,.4); border-radius: 20px; .el-row{ padding: 5px; position: relative; .el-input__inner{ padding: 0 28px!important; } i{ position: absolute; left: 5px; top: 50%; transform: translateY(-50%); z-index: 99; } a{ float: right; color: #fff; text-decoration: none; } } } </style>
<template> <div> <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" class="demo-ruleForm"> <el-form-item prop="name"> <el-input type="text" v-model="ruleForm.name" autocomplete="off" prefix-icon="el-icon-user-solid" placeholder="请输入用户名"></el-input> </el-form-item> <el-form-item prop="pass"> <el-input type="password" v-model="ruleForm.pass" autocomplete="off" placeholder="请输入密码" prefix-icon="el-icon-sugar"></el-input> </el-form-item> <el-form-item prop="checkPass"> <el-input type="password" v-model="ruleForm.checkPass" placeholder="请确认密码" prefix-icon="el-icon-apple" autocomplete="off"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('ruleForm')" style="width: 100%">注册</el-button> <!-- <el-button @click="resetForm('ruleForm')">重置</el-button>--> </el-form-item> <router-link to="/">去登录</router-link> </el-form> </div> </template> <script> import axios from "axios"; export default { name: "Register", data() { const validateName = (rule, value, callback) => { if (!value) { return callback(new Error('用户名不能为空')); } setTimeout(() => { axios.get('http://localhost:5000/checkName',{ params:{ username:this.ruleForm.name } }).then( response=>{ //校验用户名是否合法 console.log(response.data) if(response.data.length){ callback(new Error('该用户已存在')); }else { callback(); } }, error=>{ console.log(error.message) } ) }, 1000); }; const validatePass = (rule, value, callback) => { if (value === '') { callback(new Error('请输入密码')); }else if(value.length<6){ callback(new Error('密码不能少于6位!')); } else { if (this.ruleForm.checkPass !== '') { this.$refs.ruleForm.validateField('checkPass'); } callback(); } }; const validatePass2 = (rule, value, callback) => { if (value === '') { callback(new Error('请再次输入密码')); } else if (value !== this.ruleForm.pass) { callback(new Error('两次输入密码不一致!')); } else { callback(); } }; return { ruleForm: { pass: '', checkPass: '', name:'' }, rules: { name:[ { validator: validateName, trigger: 'blur' } ], pass: [ { validator: validatePass, trigger: 'blur' } ], checkPass: [ { validator: validatePass2, trigger: 'blur' } ], } }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { axios.post('http://localhost:8080/register',{ username:this.ruleForm.name, password:this.ruleForm.pass } ).then( response=>{ console.log(response.data) this.$message({ message: '恭喜你,注册成功', type: 'success' }) //将输入存入本地 localStorage.setItem('username',this.ruleForm.name) localStorage.setItem('password',this.ruleForm.pass) localStorage.setItem('url','') setTimeout(()=>{ this.$router.push('/admin') },1000) }, error=>{ console.log(error.message) } ) } else { console.log('error submit!!'); return false; } }); } } } </script> <style scoped> .el-form{ width: 350px; margin: 250px auto 0; padding: 50px; background-color: rgba(255,255,255,.5); border-radius: 20px; } a{ float: right; color: #fff; text-decoration: none; } </style>
采用element表单组件设置自定义校验规则,十分方便
<template> <div> <el-container style="height: 630px; border: 1px solid #eee;overflow: hidden"> <!-- 左侧导航栏开始--> <el-aside width="200px" style="background-color: rgb(238, 241, 246)"> <el-row style="height: 60px"> <h1>后台管理系统</h1> </el-row> <!-- <el-menu :default-openeds="['1']"> 设置默认打开选项栏 通过唯一标识index指定--> <el-menu> <el-submenu index="1"> <template slot="title"><i class="el-icon-message"></i>首页</template> <el-menu-item-group> <el-menu-item > <router-link :to="{name:'statistics'}" slot="title"> 统计 </router-link> </el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="2"> <template slot="title"><i class="el-icon-menu"></i>文章管理</template> <el-menu-item-group> <el-menu-item :route="{ name:'species'}" > <router-link :to="{ name:'species' }">文章分类</router-link> </el-menu-item> <el-menu-item> <router-link :to="{ name:'articleList' }">文章列表</router-link> </el-menu-item> <el-menu-item> <router-link :to="{ name:'publishArticle' }">发布文章</router-link> </el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="3"> <template slot="title"><i class="el-icon-user-solid"></i>个人信息</template> <el-menu-item> <router-link :to="{ name:'updateInfo' }">修改详细信息 </router-link> </el-menu-item> <el-menu-item> <router-link :to="{ name:'resetPass' }">修改密码 </router-link> </el-menu-item> <el-menu-item> <router-link :to="{ name:'setAvatar' }">修改头像 </router-link> </el-menu-item> </el-submenu> </el-menu> </el-aside> <!-- 下拉列表选项卡--> <el-container> <el-header style="text-align: right; font-size: 12px"> <el-dropdown> <i class="el-icon-setting" style="margin-right: 15px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item> <router-link :to="{name:'updateInfo'}">修改信息</router-link> </el-dropdown-item> <el-dropdown-item> <router-link :to="{name:'resetPass'}">修改密码</router-link> </el-dropdown-item> <el-dropdown-item> <router-link replace to="/">退出</router-link> </el-dropdown-item> </el-dropdown-menu> </el-dropdown> <!-- 头像与姓名--> <el-avatar :src="this.url" style="height: 60px;width: 60px;display: inline-block;margin: 0 10px"></el-avatar> <span style="display: inline-block;height: 60px;float: right;font-weight: 700;font-size: 14px">{{name}}</span> </el-header> <!-- 切换路由显示组件位置--> <el-main style="background-color: rgba(255,255,255,.5);"> <router-view></router-view> </el-main> </el-container> </el-container> <el-footer style="height: 65px">©Designed by Pegnet ⌖2022</el-footer> </div> </template> <script> export default { name: "BackAdmin", data() { return { name:localStorage.getItem('username'), url:localStorage.getItem('url') } }, mounted() { this.$bus.$on('getUrl',()=>{ this.name = localStorage.getItem('username') this.url = localStorage.getItem('url') }) }, beforeDestroy() { localStorage.removeItem('username') // localStorage.removeItem('id') localStorage.removeItem('password') localStorage.removeItem('url') } } </script>
将用户的输入存入本地以便通过路由守卫鉴权,未登录状态通过url访问网址重定向到登录页
<template> <div> <el-container> <el-card class="box-card" style=""> <h1> 用户总数 </h1> <span>{{ userSum }}</span> </el-card> <el-card class="box-card"> <h1> 文章总数 </h1> <span>{{ articleSum }}</span> </el-card> </el-container> <div> <div id="age" style="width: 50%;height: 500px;display: inline-block;margin-top: 20px;float: left"></div> <div id="species" style="width: 50%;height: 500px;display: inline-block;float:right"></div> </div> </div> </template> <script> import * as echarts from 'echarts'; import axios from "axios"; export default { name: "Statistics", data() { return { ageData: [ {value: 0, name: '18岁以下'}, {value: 0, name: '18岁以上30岁以下'}, {value: 0, name: '30岁以上50岁以下'}, {value: 0, name: '50岁以上70岁以下'}, {value: 0, name: '70岁以上'}, ], species: [], speciesData: [], userSum: 0, articleSum: 0 } }, mounted() { //获取年龄统计数据 const age = echarts.init(document.getElementById('age')); const ageOption = { textStyle:{ fontSize: 14, fontWeight: 700, }, title: { text: '用户年龄分布', subtext: '来自数据库', left: 'center' }, tooltip: { trigger: 'item' }, legend: { orient: 'vertical', left: 'left' }, series: [ { name: '用户年龄', type: 'pie', radius: '50%', data: this.ageData, emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } ] } axios.get('http://localhost:5000/statistics').then( response => { console.log(response.data) response.data.forEach((item, index) => { this.ageData[index].value = item[0]['count(1)'] this.userSum += this.ageData[index].value // console.log(this.data[index].value) }) age.setOption(ageOption); } ) //获取文章分类统计数据 const species = echarts.init(document.getElementById('species')); const specieOption = { textStyle:{ fontSize: 14, fontWeight: 700, color:'black' }, title: { text: '文章分类数量', subtext: '来自数据库', left: 'center' }, xAxis: { type: 'category', data: this.species }, yAxis: { type: 'value' }, series: [ { data: this.speciesData, type: 'bar', showBackground: true, backgroundStyle: { color: 'rgba(180, 180, 180, 0.2)' } } ] }; //获取文章分类 axios.get('http://localhost:5000/getSpecies').then( response => { // console.log(response.data) response.data.forEach((item) => { // console.log(item) this.species.push(item.name) }) //获取文章分类文章数量 axios.post('http://localhost:5000/articleStatistics', { species: this.species }).then( response => { // console.log(response.data) response.data.forEach((item, index) => { this.speciesData[index] = item[0]['count(1)'] // console.log(this.data[index].value) this.articleSum += this.speciesData[index] console.log(this.speciesData) species.setOption(specieOption); }) }, error => { console.log(error.message) } ) species.setOption(specieOption); // console.log(this.species) // this.specieData = response.data }, error => { console.log(error.message) } ) } } </script>
<template> <div> <el-row style="float: right;margin-bottom: 10px"> <!-- <el-button type="primary">添加分类</el-button>--> <!-- 添加分类弹出层--> <el-popover placement="left" width="400" trigger="click"> <!-- 提交表单--> <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"> <el-form-item label="分类名" prop="name"> <el-input type="text" v-model="ruleForm.name" autocomplete="off"></el-input> </el-form-item> <el-form-item label="分类别名" prop="nickname"> <el-input type="text" v-model="ruleForm.nickname" autocomplete="off"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('ruleForm')">添加</el-button> <el-button @click="resetForm('ruleForm')">重置</el-button> </el-form-item> </el-form> <!-- <el-button type="primary" plain style="width: 100%" @click="addSpecie">确认添加</el-button>--> <el-button slot="reference" type="primary">添加分类</el-button> </el-popover> </el-row> <!-- 显示分类表格主体--> <transition-group appear name="animate__animated animate__bounce" enter-active-class="animate__bounce" leave-active-class="animate__bounce" > <el-table :data="tableData" style="width: 100%" key="1" > <el-table-column label="序号" width="180" key="2" > <template slot-scope="scope"> <span style="margin-left: 10px">{{ scope.row.id }}</span> </template> </el-table-column> <el-table-column label="分类名称" width="280"> <template slot-scope="scope"> <span style="margin-left: 10px">{{ scope.row.name }}</span> </template> </el-table-column> <el-table-column label="分类别名" width="280"> <template slot-scope="scope"> <div slot="reference" class="name-wrapper"> <el-tag size="medium">{{ scope.row.nickname }}</el-tag> </div> </template> </el-table-column> <!-- 表格操作列--> <el-table-column label="操作"> <!-- 编辑弹出层--> <template slot-scope="scope"> <el-popover placement="left" width="400" trigger="click" > <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"> <el-form-item label="分类名" prop="name"> <!-- <input type="text" :value="scope.row.name">--> <el-input type="text" v-model="ruleForm.name" :placeholder="scope.row.name" autocomplete="off"></el-input> </el-form-item> <el-form-item label="分类别名" prop="nickname"> <el-input type="text" v-model="ruleForm.nickname" :placeholder="scope.row.nickname" autocomplete="off"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('ruleForm',scope.$index, scope.row)">修改</el-button> <el-button @click="resetForm('ruleForm')">重置</el-button> </el-form-item> </el-form> <el-button slot="reference" size="mini" type="warning" style="margin-right: 10px" >编辑 </el-button> </el-popover> <el-popconfirm title="确定删除这个分类吗?" icon="el-icon-info" icon-color="red" @confirm="handleDelete(scope.$index, scope.row)"> <el-button size="mini" slot="reference" type="danger">删除 </el-button> </el-popconfirm> </template> </el-table-column> </el-table> </transition-group> <!-- <el-empty description="暂无数据" v-show="isShow"></el-empty>--> </div> </template> <script> import axios from "axios"; import 'animate.css' export default { name: "ArticleSpecies", data() { //定义检验规则 const validateName = (rule, value, callback) => { // console.log(value) if (value === '') { callback(new Error('请输入分类名')); } else { this.tableData.forEach((val) => { if (value === val.name) { callback(new Error('分类名已存在!')); } // console.log(val) // console.log(val.name) }) callback(); } }; const validateNickname = (rule, value, callback) => { if (value === '') { callback(new Error('请输入分类别名')); } else { callback(); } }; return { tableData: [], isShow: false, timer: null, ruleForm: { name: '', nickname: '', }, rules: { name: [ {validator: validateName, trigger: 'blur'} ], nickname: [ {validator: validateNickname, trigger: 'blur'} ], } } }, watch: { tableData: { // deep:true, handler(newValue, oldValue) { if (!newValue) { this.isShow = true } else { this.isShow = true } } } }, methods: { handleEdit(index, row) { console.log(index, row); }, //删除分类,形参与实参列表必须一一对应 handleDelete(index, row) { //实现节流以及鉴权 if (this.timer) { return } else { if ([1, 2, 3].includes(row.id) && localStorage.getItem('username') !== 'admin') { this.timer = setTimeout(() => { this.$message.error('非管理员不可删除这个分类!'); this.timer = null }, 1000) }else { axios.post('http://localhost:8080/deleteSpecie', { id: row.id }).then( response => { console.log(response.data) //获取当前行的id直接从数据中删除 this.tableData.splice(index, 1) }, error => { console.log(error.message) } ) } } }, //添加分类 addSpecie() { axios.post('http://localhost:8080/addSpecie', { name: this.ruleForm.name, nickname: this.ruleForm.nickname }).then( response => { console.log(response.data) }, error => { console.log(error.message) } ) }, //查询分类 selectSpecie(status, index) { axios.post('http://localhost:8080/selectSpecie', { name: this.ruleForm.name, }).then( response => { if (status === 'add') { this.tableData.push(response.data[0]) } else { this.tableData.splice(index, 1, response.data[0]) } // console.log(response.data) }, error => { console.log(error.message) } ) }, //更新分类 updateSpecie(id, name, nickname) { axios.post('http://localhost:8080/updateSpecie', { id, name, nickname }).then( response => { console.log(response.data) }, error => { console.log(error.message) } ) }, //提交添加分类 submitForm(formName, index = '', row = '') { this.$refs[formName].validate((valid) => { if (valid) { if (!index && !row) { this.addSpecie() this.selectSpecie('add') this.ruleForm.nickname = '' this.ruleForm.name = '' this.$message({ message: '添加成功~', type: 'success' }) } else { // console.log(e.target) console.log(index) console.log(row) if ([1, 2, 3].includes(row.id) && localStorage.getItem('username') !== 'admin'){ this.$message.error('非管理员不可修改这个分类!'); this.ruleForm.nickname = '' this.ruleForm.name = '' }else{ this.updateSpecie(row.id, this.ruleForm.name, this.ruleForm.nickname) this.selectSpecie('edit', index) this.ruleForm.nickname = '' this.ruleForm.name = '' this.$message({ message: '修改成功~', type: 'success' }) } } } else { console.log('error submit!!'); return false; } }); }, //重置表单 resetForm(formName) { this.$refs[formName].resetFields(); } }, //当dom元素挂载完毕执行 mounted() { axios.get('http://localhost:5000/getSpecies').then( response => { console.log(response.data) this.tableData = response.data }, error => { console.log(error.message) } ) } } </script>
<template> <el-container> <el-card class="box-card" style="width: 100%"> <div slot="header" class="clearfix"> <span style="font-weight: 700">文章列表</span> </div> <el-row> <el-select v-model="value" filterable placeholder="请选择"> <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.name"> </el-option> </el-select> <el-button type="primary" @click="searchArticle" style="margin-left: 10px">搜索</el-button> <el-button type="success" plain style="float: right" @click="publishArticle">发表文章</el-button> </el-row> <el-main> <el-table :data="tableData" border style="width: 100%"> <el-table-column prop="title" align="center" label="文章标题" class="name-wrapper" width="250"> </el-table-column> <el-table-column prop="specie" align="center" label="文章分类" width="160"> </el-table-column> <el-table-column prop="pubtime" align="center" :formatter="dateFormatter" label="发布时间" width="320"> </el-table-column> <el-table-column prop="author" align="center" label="作者" width="160"> </el-table-column> <el-table-column label="操作" align="center" width="305"> <template slot-scope="scope"> <!-- <el-popover--> <!-- placement="left"--> <!-- width="800"--> <!-- trigger="click">--> <!-- <p ref="content"></p>--> <!-- <el-button type="primary" slot="reference" size="small" @click="showContent(scope.$index, scope.row)">查看</el-button>--> <!-- </el-popover>--> <el-button type="primary" slot="reference" size="small" @click="showContent(scope.$index, scope.row)">查看</el-button> <!-- 只有文章的作者才有权限编辑和删除文章--> <el-button type="warning" size="small" v-show="scope.row.author === username" @click="editArticle(scope.row)">编辑</el-button> <el-popconfirm title="确定删除这个文章吗?" icon="el-icon-info" icon-color="red" @confirm="deleteArticle(scope.$index,scope.row.id)" > <el-button type="danger" slot="reference" size="small" style="margin-left: 7px" v-show="scope.row.author === username">删除</el-button> </el-popconfirm> </template> </el-table-column> </el-table> </el-main> </el-card> </el-container> </template> <script> import axios from "axios"; import dayjs from "dayjs"; export default { name: "articleList", data() { return { options: [], value: '', tableData: [], timer: null, status:null, username:window.localStorage.getItem('username'), cacheSpecie:localStorage.getItem('cacheSpecie') } }, //dom加载完成从数据库获取所有文章分类并渲染页面 mounted() { axios.get('http://localhost:5000/getSpecies').then( response => { this.options = response.data }, error => { console.log(error.message) } ) //如果有搜索历史优先展示缓存中搜索的结果 if(this.cacheSpecie){ axios.post('http://localhost:5000/selectArticles', { specie: this.cacheSpecie }).then( response => { this.tableData = response.data this.value = this.cacheSpecie this.timer = null }, error => { console.log(error.message) } ) } }, methods: { //根据分类搜索文章列表 searchArticle() { if (this.timer) { return } this.timer = setTimeout(() => { axios.post('http://localhost:5000/selectArticles', { specie: this.value }).then( response => { console.log(response.data) localStorage.setItem('cacheSpecie',this.value) this.tableData = response.data this.timer = null }, error => { console.log(error.message) } ) }, 500) }, //定义格式化函数 dateFormatter(cellvalue) { // console.log('@',cellvalue) // cellvalue为当前行对象 return dayjs(cellvalue.pubtime).format('YYYY-MM-DD HH:mm:ss') }, //前往发布文章页面 publishArticle(){ this.$router.push({ name:'publishArticle' }) }, //查看文章 showContent(index,row){ // console.log(index) // console.log(row.content) this.$router.push({ name:'articleDetail', params:{ article:row } }) // this.$refs.content.innerHTML = row.content }, //修改文章 editArticle(row){ // console.log(index) // console.log(row.content) this.$router.push({ name:'editArticle', params:{ article:row } }) // this.$refs.content.innerHTML = row.content }, //删除文章 deleteArticle(index,id){ console.log(id) axios.post('http://localhost:5000/deleteArticle', { id: id }).then( response => { console.log(response.data) this.tableData.splice(index, 1) }, error => { console.log(error.message) } ) } }, } </script>
只有文章的作者才能修改或删除文章,其余用户只能查看
<template> <el-container> <el-header> <el-input v-model="title" placeholder="请输入标题" style="width: 300px;margin-right: 20px" maxlength="10" show-word-limit ></el-input> <el-select v-model="specie" filterable placeholder="请选择"> <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.name"> </el-option> </el-select> <el-button type="primary" style="margin-left: 20px" @click="publishArticle">发布文章</el-button> </el-header> <div style="border: 1px solid #ccc;height: 400px"> <Toolbar style="border-bottom: 1px solid #ccc" :editor="editor" :defaultConfig="toolbarConfig" :mode="mode" /> <Editor style="height: 500px; overflow-y: hidden;" v-model="content" :defaultConfig="editorConfig" :mode="mode" @onCreated="onCreated" /> </div> </el-container> </template> <script> import {Editor, Toolbar} from '@wangeditor/editor-for-vue' import axios from "axios"; export default { name: "publishArticle", components: {Editor, Toolbar}, data() { return { editor: null, content: '', toolbarConfig: {}, editorConfig: {placeholder: '请输入内容...'}, mode: 'default', // or 'simple' options: [], specie: '', title:'', timer:null } }, methods: { onCreated(editor) { this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错 }, //发布文章 publishArticle(){ if(this.timer){ return; } this.timer = setTimeout(()=>{ if(!this.title){ this.$message({ showClose: true, message: '请输入标题', type: 'warning' }); return this.timer=null } if(!this.specie){ this.$message({ showClose: true, message: '请选择分类', type: 'warning' }); return this.timer=null } if(!this.content){ this.$message({ showClose: true, message: '请输入文章内容', type: 'warning' }); return this.timer=null } console.log(this.title) console.log(this.content) console.log(this.specie) axios.post('http://localhost:5000/publishArticle',{ title:this.title, content:this.content, specie:this.specie, author:localStorage.getItem('username'), }).then( response=>{ console.log(response.data) this.$notify({ title: '成功', message: '发布成功~', type: 'success' }); this.timer=null this.title='' this.content='' this.specie='' }, error=>{ this.$notify({ title: '失败', message: '发布失败!', type: 'warning' }); console.log(error.message) } ) },1000) } }, mounted() { // // 模拟 ajax 请求,异步渲染编辑器 // setTimeout(() => { // this.content = '请输入内容' // }, 1500) axios.get('http://localhost:5000/getSpecies').then( response => { this.options = response.data }, error => { console.log(error.message) } ) }, beforeDestroy() { const editor = this.editor if (editor == null) return editor.destroy() // 组件销毁时,及时销毁编辑器 } } </script> <style scoped> @import "@wangeditor/editor/dist/css/style.css"; </style>
<template> <el-container> <el-header> <el-button type="primary" plain icon="el-icon-arrow-left" style="float: left" @click="toList">返回</el-button> <el-input v-model="title" placeholder="请输入标题" style="width: 300px;margin-right: 20px" maxlength="10" show-word-limit ></el-input> <el-select v-model="specie" filterable placeholder="请选择"> <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.name"> </el-option> </el-select> <el-button type="success" plain style="margin-left: 20px" @click="publishArticle">确认修改</el-button> </el-header> <div style="border: 1px solid #ccc;height: 400px"> <Toolbar style="border-bottom: 1px solid #ccc" :editor="editor" :defaultConfig="toolbarConfig" :mode="mode" /> <Editor style="height: 500px; overflow-y: hidden;" v-model="content" :defaultConfig="editorConfig" :mode="mode" @onCreated="onCreated" /> </div> </el-container> </template> <script> import {Editor, Toolbar} from '@wangeditor/editor-for-vue' import axios from "axios"; export default { name: "editArticle", components: {Editor, Toolbar}, data() { return { editor: null, content: this.$route.params.article.content, toolbarConfig: {}, editorConfig: {placeholder: '请输入内容...'}, mode: 'default', // or 'simple' options: [], specie: this.$route.params.article.specie, title:this.$route.params.article.title, timer:null } }, methods: { onCreated(editor) { this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错 }, //发布文章 publishArticle(){ if(this.timer){ return; } this.timer = setTimeout(()=>{ if(!this.title){ this.$message({ showClose: true, message: '请输入标题', type: 'warning' }); return this.timer=null } if(!this.specie){ this.$message({ showClose: true, message: '请选择分类', type: 'warning' }); return this.timer=null } if(!this.content){ this.$message({ showClose: true, message: '请输入文章内容', type: 'warning' }); return this.timer=null } console.log(this.title) console.log(this.content) console.log(this.specie) axios.post('http://localhost:5000/editArticle',{ title:this.title, content:this.content, specie:this.specie, // author:localStorage.getItem('username'), id:this.$route.params.article.id, }).then( response=>{ console.log(response.data) this.$notify({ title: '成功', message: '修改成功~', type: 'success' }); this.timer=null this.title='' this.content='' this.specie='' }, error=>{ console.log(error.message) } ) },1000) }, toList(){ this.$router.back() } }, mounted() { // // 模拟 ajax 请求,异步渲染编辑器 // setTimeout(() => { // this.content = '请输入内容' // }, 1500) axios.get('http://localhost:5000/getSpecies').then( response => { this.options = response.data }, error => { console.log(error.message) } ) }, beforeDestroy() { const editor = this.editor if (editor == null) return editor.destroy() // 组件销毁时,及时销毁编辑器 } } </script> <style scoped> @import "@wangeditor/editor/dist/css/style.css"; </style>
与发布组件一致,只需要简单赋值修改即可
<template> <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"> <el-form-item label="用户名" prop="username"> <el-input type="text" v-model="ruleForm.username" autocomplete="off"></el-input> </el-form-item> <el-form-item label="性别" style="text-align: left;font-weight: 700"> <el-select v-model="ruleForm.gender" placeholder="请选择性别" value=""> <el-option label="男" value="male"></el-option> <el-option label="女" value="female"></el-option> <el-option label="其他" value="other"></el-option> </el-select> </el-form-item> <el-form-item label="婚况" style="text-align: left"> <el-select v-model="ruleForm.maritalStatus" placeholder="请选择婚况" value=""> <el-option label="已婚" value="unmarried"></el-option> <el-option label="未婚" value="married"></el-option> <el-option label="丧偶" value="widow"></el-option> </el-select> </el-form-item> <el-form-item label="年龄" prop="age"> <el-input v-model.number="ruleForm.age"></el-input> </el-form-item> <el-form-item style="text-align: left"> <el-button type="primary" @click="submitForm('ruleForm')">确认修改</el-button> <el-button @click="resetForm('ruleForm')">重置</el-button> </el-form-item> </el-form> </template> <script> import axios from "axios"; export default { name: "updateInfo", data() { //检验年龄是否合法 const checkAge = (rule, value, callback) => { if (!value) { return callback(new Error('年龄不能为空')); } setTimeout(() => { if (!Number.isInteger(value)) { callback(new Error('请输入数字值')); } else { if (value < 10) { callback(new Error('必须年满10岁')); } else { callback(); } } }, 1000); }; //检验用户名是否合法 const validateName = (rule, value, callback) => { if (value === '') { callback(new Error('请输入用户名')); } else { setTimeout(() => { axios.get('http://localhost:5000/checkName',{ params:{ username:this.ruleForm.username } }).then( response=>{ console.log(response.data) if(response.data.length){ callback(new Error('该用户已存在')); }else { callback(); } }, error=>{ console.log(error.message) } ) }, 1000); // callback(); } }; return { ruleForm: { username: '', age: '', gender:'', maritalStatus:'', oldName :localStorage.getItem('username') }, rules: { username: [ { required:true,validator: validateName, trigger: 'blur' } ], gender: [ { message: '请选择性别', trigger: 'change' } ], age: [ { required:true,validator: checkAge, trigger: 'blur' } ] } }; }, methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { axios.post('http://localhost:5000/updateInfo',{ newName:this.ruleForm.username, oldName:this.ruleForm.oldName, age:this.ruleForm.age, maritalStatus:this.ruleForm.maritalStatus, gender:this.ruleForm.gender, }).then( response=>{ console.log(response.data) localStorage.setItem('username',this.ruleForm.username) this.ruleForm.oldName = this.ruleForm.username this.$bus.$emit('getUrl') this.$message({ message: '修改成功~', type: 'success', }) this.ruleForm.username = '' this.ruleForm.age = '' this.ruleForm.gender = '' this.ruleForm.maritalStatus = '' }, error=>{ console.log(error.message) } ) } else { console.log('error submit!!'); return false; } }); }, resetForm(formName) { this.$refs[formName].resetFields(); } } } </script>
<template> <div> <el-upload class="avatar-uploader" action="http://localhost:5000/setAvatar" :data="{name:this.username}" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload"> <img v-if="imageUrl" :src="imageUrl" class="avatar" alt="丢失"> <i v-else class="el-icon-plus avatar-uploader-icon"></i> <div style="color: #3a3b3d">点击上传头像</div> </el-upload> <!-- <img :src="this.url" alt="">--> </div> </template> <script> export default { name: "setAvatar", data() { return { imageUrl: '', username:localStorage.getItem('username'), url:localStorage.getItem('url'), }; }, methods: { handleAvatarSuccess(res, file) { console.log(this.username) this.imageUrl = URL.createObjectURL(file.raw); console.log(res) localStorage.setItem('url',res) this.$bus.$emit('getUrl') console.log(this.imageUrl) }, beforeAvatarUpload(file) { const isJPG = file.type === 'image/jpeg'; const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG) { this.$message.error('上传头像图片只能是 JPG 格式!'); } if (!isLt2M) { this.$message.error('上传头像图片大小不能超过 2MB!'); } return isJPG && isLt2M; } } } </script>
采用element头像组件上传到服务器本地以及数据库
全部代码https://gitee.com/pegnet/bigevent.git
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。