前端部分:
Vue项目的入口文件main.js:
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
import router from './router/index'
import Vuex from 'vuex'
import store from './store'
//完整引入
//引入ElementUI组件库
import ElementUI from 'element-ui';
//引入ElementUI全部样式
import 'element-ui/lib/theme-chalk/index.css';
import './assets/css/reset.css'
import api from './api/index'
import * as echarts from 'echarts'
// 引入图标库
import './assets/icon/icon.css'
import './assets/fonts/iconfont.css'
//引入mavon-editor
import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
// //引入动画
// import animate from 'animate.css'
//代码高亮
// import '../src/plugins/hljs.js'
//引入Swiper
// import 'swiper/css/swiper.css'
import hljs from 'highlight.js';
Vue.directive('highlight', function (el) {
let blocks = el.querySelectorAll('pre code');
blocks.forEach((block) => {
hljs.highlightBlock(block)
})
})
import 'highlight.js/styles/atom-one-dark.css'
//引入音频插件
import APlayer from 'vue-aplayer';
Vue.use(APlayer, {
defaultCover: 'https://github.com/u3u.png',
productionTip: true,
});
import 'swiper/swiper-bundle.css'
// 引入插件
import plugins from '../src/plugins/plugins'
Vue.use(plugins)
//引入瀑布流插件
import waterfall from 'vue-waterfall2'
Vue.use(waterfall)
Vue.prototype.$echarts = echarts
// import { json } from 'express'
Vue.prototype.$api = api
Vue.use(ElementUI)
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.use(mavonEditor)
//关闭Vue的生产提示
Vue.config.productionTip = false
//登录持久化
//用户信息
let username = localStorage.getItem('username')
if (username) {
username = JSON.parse(username)
store.commit('loginModule/setUser', username)
}
//管理员信息
let adminname = localStorage.getItem('lwandzxl')
if (adminname){
adminname = JSON.parse(adminname)
store.commit('AdminLogin/setAdmin', adminname)
}
//管理员登录ip
let adminaddress = localStorage.getItem('lwandzxladdress')
if (adminaddress) {
adminaddress = JSON.parse(adminaddress)
store.commit('AdminLoginAddress/setAddress', adminaddress)
}
// let bgzxl = localStorage.getItem('bgzxl')
// if (bgzxl) {
// bgzxl = JSON.parse(bgzxl)
// store.commit('loginModule/setAdmin', bgzxl)
// }
//判断token是否失效
import axios from 'axios' // 引入axios
Vue.prototype.$axios = axios
import Storage from '@/assets/js/storage.js'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
/* 请求拦截器 */
axios.interceptors.request.use(function (config) { // 每次请求时会从localStorage中获取token
let token = Storage.localGet('token')
if (token) {
token = 'bearer' + ' ' + token.replace(/'|"/g, '') // 把token加入到默认请求参数中
config.headers.common['Authorization'] = token
}
return config
}, function (error) {
return Promise.reject(error)
})
/* 响应拦截器 */
axios.interceptors.response.use(function (response) { // ①10010 token过期(30天) ②10011 token无效
if (response.data.code === 10010 || response.data.code === 10011) {
Storage.localRemove('token') // 删除已经失效或过期的token(不删除也可以,因为登录后覆盖)
router.replace({
path: '/login' // 到登录页重新获取token
})
} else if (response.data.token) { // 判断token是否存在,如果存在说明需要更新token
Storage.localSet('token', response.data.token) // 覆盖原来的token(默认一天刷新一次)
}
return response
}, function (error) {
return Promise.reject(error)
})
new Vue({
el: '#app',
render: h => h(App),
router: router,
store,
beforeCreate() {
Vue.prototype.$bus = this
}
})
路由文件router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
/*
在原始的 Vue Router 中,当使用 this.$router.push(location) 导航到一个新路由时,如果目标路由不存在,则会抛出错误。
这个代码块可以解决这个问题
*/
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '',
//redirect: '/home',name: 'index', 把/home设置为首页
redirect: '/home',
name: 'index',
component: () => import('../views/Layout/index'),
children: [{
path: '/home',
name: 'home',
component: () => import('../views/components/Home/Home')
},
{
path: 'todos',
name: 'todos',
component: () => import('../components/Todos/Todos')
},
{
path: 'tools',
name: 'tools',
component: () => import('../components/Tools/index'),
children: [{
path: 'fanyi',
name: 'fanyi',
component: () => import('../components/Tools/Fanyi')
},
{
path: 'weather',
name: 'weather',
component: () => import('../components/Tools/Weather')
},
{
path: 'photos',
name: 'photos',
component: () => import('../components/Tools/Photo')
}, {
path: 'word',
name: 'word',
component: () => import('../components/Tools/Word')
},]
},
{
path: 'usercenter',
name: 'usercenter',
meta: { isLogin: true },
component: () => import('../../src/views/components/Usercenter/Usercenter'),
},
{
name: 'userinfoedit',
path: 'userinfoedit',
component: () => import('../components/Userinfoedit/Userinfoedit')
},
{
name: 'photo',
path: 'photo',
component: () => import('../views/components/Photo/Photo')
},
{
name: 'video',
path: 'video',
component: () => import('../views/components/Video/Video')
},
{
name: 'article',
path: 'article',
component: () => import('../../src/views/components/Article/Article')
},
{
name: 'articleinfo',
path: 'articleinfo',
component: () => import('../../src/components/ArticleInfo/ArticleInfo')
},
{
name: 'search',
path: 'search',
component: () => import('../../src/components/Search/Search')
},
{
name: 'messageinfo',
path: 'messageinfo',
component: () => import('../../src/components/Message/MessageInfo')
},
]
},
{
path: '/login',
name: 'login',
component: () => import('../views/components/Login/Login')
},
{
path: '/register',
name: 'register',
component: () => import('../views/components/Register/Register')
},
{
path: '/adminlogin',
name: 'adminlogin',
component: () => import('../Admin/AdminLogin/AdminLogin')
},
{
path: '/admin',
name: 'admin',
meta: { isAdminLogin: true },
component: () => import('../Admin/Layout/index'),
children: [{
path: 'userlist',
name: 'userlist',
component: () => import('../Admin/User/UserList')
},
{
path: 'adduser',
name: 'adduser',
component: () => import('../Admin/User/AddUser')
},
{
path: 'updateuser',
name: 'updateuser',
component: () => import('../Admin/User/UpdateUser')
},
{
path: 'addphoto',
name: 'addphoto',
component: () => import('../Admin/Photo/AddPhoto')
},
{
path: 'photolist',
name: 'photolist',
component: () => import('../Admin/Photo/PhotoList')
},
{
path: 'home',
name: '/admin/home',
component: () => import('../Admin/Home/Home')
},
{
path: 'articlelist',
name: 'articlelist',
component: () => import('../Admin/Article/ArticleList')
},
{
path: 'huishouzhan',
name: 'huishouzhan',
component: () => import('../Admin/Article/Huishouzhan')
},
{
path: 'addarticle',
name: 'addarticle',
component: () => import('../Admin/Article/Add')
},
{
path: 'editarticle',
name: 'editarticle',
component: () => import('../Admin/Article/Edit')
},
{
path: 'comment',
name: 'comment',
component: () => import('../Admin/Article/Comment')
},
{
path: 'system',
name: 'system',
component: () => import('../Admin/System/System')
},
{
path: 'videolist',
name: 'videolist',
component: () => import('../Admin/Video/VideoList')
},
{
path: 'addvideo',
name: 'addvideo',
component: () => import('../Admin/Video/AddVideo')
},
{
path: 'message',
name: 'message',
component: () => import('../Admin/Message/Message')
},
{
path: 'category',
name: 'category',
component: () => import('../Admin/Category/Category')
},
]
}
]
})
//获取vuex数据
import store from '../store/index'
//用户路由拦截
router.beforeEach((to, from, next) => {
// to and from are both route objects. must call `next`.
//需要登录
if (to.meta.isLogin) {
let token = store.state.loginModule.userinfo.token
if (token) {
next()
} else if (confirm('您还没有登录,确定登录吗?')) {
next('/login')
this.$router.go(-1)
}
} else {//不需要登录
next()
}
})
export default router
登录页面:
<template>
<div class="login">
<h3 class="title">登录界面</h3>
<el-form
:model="loginForm"
status-icon
:rules="rules"
ref="loginForm"
label-width="60px"
class="demo-loginForm"
>
<el-form-item label="账号" prop="username">
<el-input
type="text"
v-model="loginForm.username"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
type="password"
v-model="loginForm.password"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('loginForm')"
>登录</el-button>
<el-button @click="resetForm('loginForm')">重置</el-button>
<span class="zhuce" @click="register">没有账户?去注册</span>
<span class="youke" @click="zhuye">我是游客</span>
</el-form-item>
</el-form>
</div>
</template>
<script>
import jwt from "jwt-decode";
import { mapMutations } from "vuex";
export default {
name: "Login",
data() {
var validateLname = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入账户😊"));
} else {
callback();
}
};
var validatePass = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入密码😊"));
} else {
callback();
}
};
return {
bodyImg: "url(" + require("../../../assets/img/loginbg.jpg") + ")",
loginForm: {
username: "",
password: "",
},
rules: {
username: [{ validator: validateLname, trigger: "blur" }],
password: [{ validator: validatePass, trigger: "blur" }],
},
};
},
//设置背景图片
mounted() {
document.body.style.backgroundImage = this.bodyImg;
document.body.style.backgroundSize = "100%";
//注册成功后,跳转登录界面,通过读取localStorage里的数据,使得登录的账户,密码就是注册的账户,密码
let register = localStorage.getItem("register");
if (register) {
register = JSON.parse(register);
this.loginForm.username = register.username;
this.loginForm.password = register.password;
}
},
beforeMount() {
document.body.style.backgroundImage = "";
},
beforeDestroy() {
this.$bus.$off("zxl");
},
methods: {
//游客身份跳转主页
zhuye() {
this.$router.push("/");
},
register() {
this.$router.push("/register");
},
...mapMutations("loginModule", ["setUser"]),
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
console.log("校验通过", this.loginForm);
let { username, password } = this.loginForm;
// 请求登录接口
this.$api
.getLogin({
username,
password,
})
.then((res) => {
console.log("解析前", res.data);
if (res.data.status === 200) {
console.log("解析后", jwt(res.data.data));
//登录成功后:1. 存储登录信息 2. 跳转网页 3. 顶部区域显示用户信息 4. 持久化
let obj = {
username: jwt(res.data.data).username,
token: res.data.data,
avatar: res.data.avatar,
email: res.data.email,
};
console.log("obj", obj);
this.setUser(obj);
//存储本地
localStorage.setItem("loginStatus", true);
localStorage.setItem("username", JSON.stringify(obj));
//跳转
this.$router.push("/home");
this.$message({
message: "恭喜您,登录成功😊",
type: "success",
});
//清除注册成功的用户数据
localStorage.removeItem("register");
} else {
//账户或密码错误
this.$message({
message: "警告哦,账户或密码错误😊",
type: "warning",
});
}
});
} else {
console.log("error submit!!");
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
};
</script>
项目主页:
<template>
<div class="main">
<!-- 顶部区域 -->
<div class="header">
<!-- <div class="logo">
<div class="zxl"><img src="../../assets/img/logo3.png" alt="" /></div>
<div class="lw"><img src="../../assets/img/logo2.png" alt="" /></div>
</div> -->
<div class="search">
<el-input
placeholder="请输入内容"
v-model="val"
@keydown.native.enter="goSearch(val.trim())"
>
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
<button class="hhh" @click="goSearch(val.trim())">搜索</button>
</div>
<div class="login">
<i v-show="userinfo.username === 'admin'">欢迎超级管理员:</i>
<i v-show="userinfo.username !== 'admin'">欢迎:</i>
{{ userinfo.username || " 游客" }}
<span v-show="userinfo.username" @click="loginout"
><i class="el-icon-loading"></i> 退出登录</span
>
<span v-show="userinfo.username === ''" @click="login"
><i class="el-icon-loading"></i> 登录</span
>
<span v-show="userinfo.username === ''" @click="register"> 注册</span>
<!-- <span @click="loginout"> 退出登录</span> -->
</div>
<!-- <div class="light"><Light @changeBackground="changeBG"></Light></div> -->
</div>
<!-- 内容区域 -->
<div class="layout">
<Leftmenu class="leftmenu"></Leftmenu>
<Rightmenu class="rightmenu"></Rightmenu>
<Content class="content"></Content>
</div>
<transition
appear
name="animate__animated animate__bounce"
enter-active-class="animate__backInUp"
leave-active-class="animate__backOutDown"
>
<div class="gotop" v-if="istop" @click="gotop">
<img src="../../assets/img/gotop.png" alt="" />
</div>
</transition>
</div>
</template>
<script>
import "animate.css";
import { mapState } from "vuex";
import { mapMutations } from "vuex";
import Fengche from "../../components/Fengche/Fengche";
import Content from "./Content";
import Leftmenu from "./Leftmenu";
import Rightmenu from "./Rightmenu";
import Light from "../../components/Light/Light";
export default {
name: "index",
data() {
return {
istop: false,
val: "",
bodyImg: "url(" + require("../../assets/img/bg111.jpg") + ")",
bodyImg1: "url(" + require("../../assets/img/night.gif") + ")",
isclick: true,
};
},
components: {
Content,
Leftmenu,
Rightmenu,
Light,
Fengche,
},
computed: {
...mapState("loginModule", ["userinfo"]),
},
watch: {
isclick(val) {
console.log("变化", val);
if (val === true) {
document.body.style.backgroundImage = this.bodyImg;
document.body.style.backgroundSize = "100%";
document.body.style.backgroundAttachment = "fixed";
} else {
document.body.style.backgroundImage = this.bodyImg1;
document.body.style.backgroundSize = "100%";
document.body.style.backgroundAttachment = "fixed";
}
},
},
// watch: {
// val(val, oldval) {
// console.log("新", val);
// console.log("旧", oldval);
// if (val !== oldval) {
// this.val = val;
// }
// },
// },
methods: {
//搜索
goSearch(val) {
if (val == "") {
return this.$message.error("错了哦,输入不能为空");
}
this.$router.push(`/search?content=${val}`);
},
changeBG() {
this.isclick = !this.isclick;
// console.log(this.isclick);
// document.body.style.backgroundImage = `${
// this.click ? this.bodyImg : this.bodyImg1
// } `;
// document.body.style.backgroundSize = "100%";
// document.body.style.backgroundAttachment = "fixed";
},
...mapMutations("loginModule", ["clearUser"]),
//退出登录
loginout() {
//清空vuex数据
this.clearUser();
//清空本地数据
localStorage.removeItem("username");
localStorage.removeItem("loginStatus");
//返回登录
this.$router.push("/login");
},
login() {
this.$router.push("/login");
},
register() {
this.$router.push("/register");
},
handleScroll(e) {
let scrollTop =
document.body.scrollTop || document.documentElement.scrollTop;
if (scrollTop >= 480) {
this.istop = true;
}
if (scrollTop < 480) {
this.istop = false;
}
// if (e.target.scrollTop > 700) this.istop = true;
},
gotop() {
let top = document.documentElement.scrollTop || document.body.scrollTop;
const timeTop = setInterval(() => {
document.body.scrollTop =
document.documentElement.scrollTop =
top -=
50;
if (top < 0) {
clearInterval(timeTop);
}
}, 10);
},
},
mounted() {
document.body.style.backgroundImage = this.bodyImg;
document.body.style.backgroundSize = "100%";
document.body.style.backgroundAttachment = "fixed";
window.addEventListener("scroll", this.handleScroll);
},
beforeMount() {
document.body.style.backgroundImage = "";
},
beforeDestroy() {
window.removeEventListener("scroll", this.handleScroll);
},
};
</script>
left:
<template>
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
background-color="rgba(0,0,0,0.5)"
text-color="#fff"
active-text-color="#ffd04b"
@select="handleSelect"
>
<el-menu-item index="/home">
<i class="iconfont icon-shouye"></i>
<span slot="title">
<router-link to="/home"> 首页</router-link>
</span>
</el-menu-item>
<el-menu-item index="/article">
<i class="iconfont icon-16"></i>
<span slot="title">
<router-link to="/article"> 文章</router-link>
</span>
</el-menu-item>
<el-menu-item index="/todos">
<i class="iconfont icon-zuozhe2"></i>
<span slot="title">
<router-link to="/todos"> 记事本</router-link>
</span>
</el-menu-item>
<el-menu-item index="/photo">
<i class="iconfont icon-tupian"></i>
<span slot="title">
<router-link to="/photo"> 相册</router-link>
</span>
</el-menu-item>
<el-menu-item index="/video">
<i class="iconfont iconfont icon-shipin"></i>
<span slot="title">
<router-link to="/video"> 视频</router-link>
</span>
</el-menu-item>
<el-menu-item index="/messageinfo">
<i class="iconfont icon-liuyanban"></i>
<span slot="title">
<router-link to="/messageinfo"> 留言板</router-link>
</span>
</el-menu-item>
<el-menu-item index="/usercenter">
<i class="iconfont icon-yonghuxinxi-"></i>
<span slot="title">
<router-link to="/usercenter"> 个人中心</router-link>
</span>
</el-menu-item>
<el-menu-item index="/tools">
<i class="iconfont icon-gongjuxiang1"></i>
<span slot="title">
<router-link to="/tools"> 实用工具</router-link>
</span>
</el-menu-item>
<el-menu-item index="/admin/home" v-show="userinfo.username === 'admin'">
<i class="iconfont icon-yonghu"></i>
<span slot="title">
<router-link to="/admin/home"> 后台登录</router-link>
</span>
</el-menu-item>
</el-menu>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "Leftmenu",
computed: {
...mapState("loginModule", ["userinfo"]),
},
// mounted() {
// console.log(this.userinfo.username);
// },
methods: {
handleOpen(key, keyPath) {
console.log(key, keyPath);
},
handleClose(key, keyPath) {
console.log(key, keyPath);
},
//element-ui导航菜单使用刷新后高亮显示不一致完美解决办法
handleSelect(key, keyPath) {
this.$router.push(key);
},
},
};
</script>