快速搭建前端页面
1 Vue
前置:
需要先包含node环境
如果没有,可去node官网下载一个并配置
node官网地址:https://nodejs.org/zh-cn/
# 检测是否安装成功
node -v
1.1 vue脚手架
- 检测是否有node环境,如果没有则去node官网下载配置【进入cmd执行以下命令】
node -v
- 安装vue脚手架
npm install -g @vue/cli
# 检测是否安装成功
vue --version
- 创建项目
# 全局安装初始化命令
npm i -g @vue/cli-init
# 创建项目
vue init webpack 文件夹名称
如果当前文件夹已经存在会提示Target directory exists. Continue? 是否在当前目录下创建,输入yes表示同意
# 项目名
Project name -- testpro
# 项目描述
Project description -- A Vue.js project
# 作者
Author -- ziyi
# build选项
Vue build ("通过上下箭头选择")
- Runtime+Compiler 运行加编译【建议】
- Runtime-only 仅运行
# 是否安装路由【输入y或者n即可】
Install vue-router(Y/n)
# 是否使用ESLint(代码风格校验工具)
Use ESLint to lint your code?(Y/n)
# 是否设置单元测试
Set up unit tests (Y/n)
# 是否设置端到端测试
Setup e2e tests with Nightwatch (Y/n)
# 使用什么管理包
Should we run `npm install` for you after the project has been created?
- Yes, use NPM (使用npm包管理)
- Yes, use Yarn (使用yarn包管理)
- No, I will handle that myself (自己处理)
- 项目初始化并启动
切记一定要进入项目的文件夹,否则会报找不到
package.json
cd work(进入项目所在文件夹)
# 启动项目
npm run dev
5. 连接链接,访问
项目结构:
1.2 vue+elementUi实现注册登录
首先已经通过1.1的vue脚手架搭建好了vue基本框架,通是编写对应js
# 安装element-ui
npm i element-ui -S
vue项目结构介绍:
①新建vue文件
新建两个Vue文件,Login.vue和Register.vue
Login.vue:
<template>
<div class="loginbody">
<div class="logindata">
<div class="logintext">
<h2>Welcome</h2>
</div>
<div class="formdata">
<el-form ref="form" :model="form" :rules="rules">
<el-form-item prop="username">
<el-input
v-model="form.username"
clearable
placeholder="请输入账号"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="form.password"
clearable
placeholder="请输入密码"
show-password
></el-input>
</el-form-item>
</el-form>
</div>
<div class="tool">
<div>
<el-checkbox v-model="checked" @change="remenber"
>记住密码</el-checkbox
>
</div>
<div>
<span class="shou" @click="forgetpas">忘记密码?</span>
</div>
</div>
<div class="butt">
<el-button type="primary" @click.native.prevent="login('form')"
>登录</el-button
>
<el-button class="shou" @click="register">注册</el-button>
</div>
</div>
</div>
</template>
<script>
import { login } from "@/api/login";
export default {
name: "Login",
data() {
return {
form: {
password: "",
username: "",
},
checked: false,
rules: {
username: [
{ required: true, message: "请输入用户名", trigger: "blur" },
{ max: 10, message: "不能大于10个字符", trigger: "blur" },
],
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{ max: 10, message: "不能大于10个字符", trigger: "blur" },
],
},
};
},
mounted() {
if (localStorage.getItem("news")) {
this.form = JSON.parse(localStorage.getItem("news"));
this.checked = true;
}
},
methods: {
login(form) {
this.$refs[form].validate((valid) => {
if (valid) {
login(this.form)
.then((res) => {
if (res.code === 200) {
setToken(res.data.token);
localStorage.setItem("USERNAME", res.data.username);
this.$message({
message: "登录成功啦",
type: "success",
showClose: true,
});
this.$router.replace("/");
} else {
this.$message({
message: "账户名或密码错误",
type: "error",
showClose: true,
});
}
})
.catch((err) => {
this.$message({
message: "账户名或密码错误",
type: "error",
showClose: true,
});
});
} else {
return false;
}
});
},
remenber(data) {
this.checked = data;
if (this.checked) {
localStorage.setItem("news", JSON.stringify(this.form));
} else {
localStorage.removeItem("news");
}
},
forgetpas() {
this.$message({
type: "info",
message: "功能尚未开发额😥",
showClose: true,
});
},
register() {
//跳转到注册页面
this.$router.push({path:`register`})
},
},
};
</script>
<style scoped>
.loginbody {
width: 100%;
height: 100%;
min-width: 1000px;
background-image: url("../assets/login.png");
background-size: 100% 100%;
background-position: center center;
overflow: auto;
background-repeat: no-repeat;
position: fixed;
line-height: 100%;
padding-top: 150px;
}
.logintext {
margin-bottom: 20px;
line-height: 50px;
text-align: center;
font-size: 30px;
font-weight: bolder;
color: white;
text-shadow: 2px 2px 4px #000000;
}
.logindata {
width: 400px;
height: 300px;
transform: translate(-50%);
margin-left: 50%;
}
.tool {
display: flex;
justify-content: space-between;
color: #606266;
}
.butt {
margin-top: 10px;
text-align: center;
}
.shou {
cursor: pointer;
color: #606266;
}
</style>
Register.vue:
<template>
<div class="login clearfix">
<div class="login-wrap">
<el-row type="flex" justify="center">
<el-form ref="loginForm" :model="user" status-icon label-width="80px">
<h3>注册</h3>
<hr>
<el-form-item prop="username" label="用户名">
<el-input v-model="user.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item prop="email" label="邮箱">
<el-input v-model="user.email" placeholder="请输入邮箱"></el-input>
</el-form-item>
<el-form-item prop="password" label="设置密码">
<el-input v-model="user.password" show-password placeholder="请输入密码"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon @click="doRegister()">注册账号</el-button>
</el-form-item>
</el-form>
</el-row>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "login",
data() {
return {
user: {
username: "",
email: "",
password: ""
},
};
},
created() {
// console.log($);
// console.log("1111");
},
methods: {
doRegister() {
if (!this.user.username) {
this.$message.error("请输入用户名!");
return;
} else if (!this.user.email) {
this.$message.error("请输入邮箱!");
return;
} else if (this.user.email != null) {
var reg = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
if (!reg.test(this.user.email)) {
this.$message.error("请输入有效的邮箱!");
} else if (!this.user.password) {
this.$message.error("请输入密码!");
return;
} else {
// this.$router.push({ path: "/" }); //无需向后台提交数据,方便前台调试
axios
.post("/register/", {
name: this.user.username,
email: this.user.email,
password: this.user.password
})
.then(res => {
// console.log("输出response.data", res.data);
// console.log("输出response.data.status", res.data.status);
if (res.data.status === 200) {
this.$router.push({ path: "/" });
} else {
alert("您输入的用户名已存在!");
}
});
}
}
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.login {
width: 100%;
height: 740px;
background-size: cover;
overflow: hidden;
}
.login-wrap {
background-size: cover;
width: 400px;
height: 300px;
margin: 215px auto;
overflow: hidden;
padding-top: 10px;
line-height: 20px;
}
h3 {
color: #0babeab8;
font-size: 24px;
}
hr {
background-color: #444;
margin: 20px auto;
}
.el-button {
width: 80%;
margin-left: -50px;
}
</style>
②新建js文件
login.js:
登录请求是通过引入外部js实现,注册请求则是通过写入vue页面中的script实现,没有做复用【当然也可以封装到外部,写在login.js或register.js中】
import Axios from "axios";
import {get, post} from "./http";
// post 请求
export const login = (params) => post(`login`, params)
③添加路由配置
添加路由配置
router文件夹下的index.js
一定要加上mode: "history"
,同时要import组件
index.js:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Register from '@/components/Register'
import Login from '@/components/Login'
Vue.use(Router)
export default new Router({
routes: [
// {
// path: '/',
// name: 'HelloWorld',
// component: HelloWorld
// },
{
path:'/',
name: 'Login',
component: Login
},
{
path: '/register',
name: 'Register',
component: Register
}
],
mode: "history"
})
path:是指跳转路径,name是名称,component是组件名(对应.vue文件)
例如:/register表示如果路径是localhost:8080/register,就跳转到Register.vue文件夹下
④实例化vue对象,引入axios
实例化vue对象,引入需要使用的工具,如:axios、element-ui等
main.js:
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI)
//全局配置
Vue.prototype.$axios = axios
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
⑤效果
登录页面:
注册页面:
图片自取,页面图片,放在assets下,并命名为login.png
:
拓展:操作js(与后端交互)
①封装http请求
src/api/http.js
http.js:
// 封装通用请求
import axios from 'axios'
import router from '../router'
axios.defaults.timeout = 5000 // 超时时间:5s
axios.defaults.withCredentials = true// 允许跨域
// Content-Type 响应头
axios.defaults.headers.post['Content-Type'] = 'application/x-www.form-urlencoded;charset=UTF-8'
// 访问基础url
axios.defaults.baseURL = 'http://localhost:8090'
// 响应拦截器
axios.interceptors.response.use(
response => {
// 如果response里面的status是200,说明访问到接口了,否则失败
if (response.status === 200) {
// Promise:异步框架
return Promise.resolve(response)
} else {
return Promise.reject(response)
}
},
error => {
if (error.response.status) {
// 根据访问失败返回的状态码,分别做不同的处理
switch (error.response.status) {
case 401: // 未登录
router.replace({
path: '/',
query: {
redirect: router.currentRoute.fullPath // 存之前访问地址
}
})
break
case 404: // not found
break
}
return Promise.reject(error.response)
}
}
)
/**
* 封装get请求
*/
export function get (url, params = {}) {
return new Promise((resolve, reject) => {
axios.get(url, {params: params})
.then(response => {
resolve(response.data)
})
.catch(err => {
reject(err)
})
})
}
/**
* 封装post请求
*/
export function post (url, data = {}) {
return new Promise((resolve, reject) => {
axios.post(url, data)
.then(response => {
resolve(response.data)
})
.catch(err => {
reject(err)
})
})
}
②定义请求的js
例如文件为:index.js【文件放在src/api/index.js】
//查询所有歌手【普通的get请求,不带参数】
export const getAllSinger = () => get(`singer/selectAll`)
//根据歌手性别查询【带参数的get请求】
export const getSingerOfSex = (sex) => get(`singer/selectSingerOfSex?sex=${sex}`)
//返回当前的评论列表【根据传参发送不同请求】
export const getAllComment = (type, id) => {
if(type == 0){ //type: 0 歌曲
return get(`comment/commentOfSongId?songId=${id}`);
}else{ //歌单
return get(`comment/commentOfSongListId?songListId=${id}`);
}
}
//新增收藏【post请求】
export const setCollect = (params) => post(`collect/add`, params);
//下载音乐【异步请求】
export const download = (url) => Axios({
method: 'get',
url: url,
responseType: 'blob'
})
③在页面中引入并使用
在xxx.vue的<script>标签中引入对应的js
import {getAllSinger} from '../api/index';
在Singer.vue中的script标签中引入对应的js方法,一般我们都会多封装一层【getAllSinger被封装为getSingerList】
// export const getAllSinger = () => get(`singer/selectAll`)
<script>
import ContentList from "../components/ContentList";
import {getAllSinger} from '../api/index';
import {mixin} from "../mixins";
import {singerStyle} from '../assets/data/singer';
export default {
name: 'singer',
components:{
ContentList
},
data(){
return {
albumDatas: [], //歌手数据
pageSize: 10, //页面大小
currentPage: 1,
singerStyle:[], //歌手类型
activeName: '全部歌手', //当前风格【类型】
}
},
computed:{
//计算当前表格中的数据
data(){
return this.albumDatas.slice((this.currentPage - 1) * this.pageSize, this.currentPage*this.pageSize)
}
},
mounted() {
this.singerStyle = singerStyle;
this.getSingerList();
},
methods:{
//获取歌手列表
getSingerList() {
getAllSinger()
.then(res => {
this.currentPage = 1;
this.albumDatas = res;
})
}
}
}
</script>
2 Element-UI
2.1 提交表单
SongListPage.vue:
//html部分
<el-dialog title="添加歌单" :visible.sync="centerDialogVisible" width="400px" center>
<el-form :model="registerForm" ref="registerForm" label-width="80px">
<el-form-item prop="title" label="标题" size="mini">
<el-input v-model="registerForm.title" placeholder="标题"></el-input>
</el-form-item>
<el-form-item label="歌单风格" prop="style">
<el-select v-model="registerForm.style" placeholder="请选择歌曲风格">
<el-option label="流行" :value="'流行'"></el-option>
<el-option label="摇滚" :value="'摇滚'"></el-option>
<el-option label="古典" :value="'古典'"></el-option>
</el-select>
</el-form-item>
</el-form>
<span slot="footer">
<el-button size="mini" @click="centerDialogVisible = false">取消</el-button>
<el-button size="mini" @click="addSongList">确定</el-button>
</span>
</el-dialog>
其中,el-select的registerForm.style对应的是script中data的registerForm的style,el-form-item对应传给后端的属性名
//js 部分
<script>
import { getAllSongList, setSongList, updateSongList, delSongList} from '../api/index';
import {mixin} from '../mixins/index';
export default {
mixins:[mixin],
data: function(){
return{
registerForm:{ //添加框
title:'',
introduction:'',
style: ''
},
};
},
methods:{
//添加歌单
addSongList(){
let params = new URLSearchParams();
params.append('title', this.registerForm.title);
params.append('pic', '/img/songListPic/songList1.png');
params.append('introduction', this.registerForm.introduction);
params.append('style', this.registerForm.style);
setSongList(params)
.then(res => {
if(res.code == 1){
this.getData();
this.notify("添加成功", "success");
} else {
this.notify("添加失败", "error");
}
})
.catch(err => {
console.log(err);
});
//添加成功之后清除添加框
this.registerForm = {}
},
}
}
</script>