项目--2
1、回顾
2、调整数据请求
utils/index.js 记录请求的 地址
export default {
baseUrl: 'http://localhost:3000'
}
views/home/index.vue
import utils from '@/utils'
axios.get(utils.baseUrl + '/pro').then(res => {
console.log(res) // ----------- 跨域问题
this.prolist = res.data
})
3、解决跨域问题
3.1 使用cors解决跨域问题 --- node - day05 - app.js
3.2 使用反向代理解决跨域问题
项目根目录创建 vue.config.js
- 单个反向代理 ----- 一定要重启服务器
module.exports = {
devServer: { // 开发服务器
proxy: 'http://localhost:3000'
}
}
数据请求 /pro ------》 http://localhost:3000/pro
axios.get('/pro').then(res => {
console.log(res)
this.prolist = res.data.data
})
修改列表组件的相关字段 components/Prolist.vue
4、点击列表进入产品的详情页面
4.1 添加详情页面,注意结构 views/detail/index.vue
<template>
<div class="detail">
<div class="box">
<header class="header">详情头部</header>
<div class="content">详情内容</div>
</div>
<footer class="footer">详情底部</footer>
</div>
</template>
4.2 添加相关路由 router/index.js
{
path: '/detail',
name: 'detail',
component: () => import('@/views/detail/index.vue')
}
浏览器输入 /detail,发现问题(布局问题)
4.3 解决布局问题 --- 添加.detail
html, body, .container, .detail {
@include rect(100%, 100%); // width: 100%;height: 100%;
}
.container, .detail {}
页面有两个底部,不合理 ----- 命名视图(多视图路由)解决问题
5 命名视图(多视图路由)
5.1 抽离App.vue中的底部组件为单独的组件 components/Footer.vue
5.2 修改 router/index.js, 路由匹配多个组件
import Vue from 'vue'
import VueRouter from 'vue-router'
import Footer from '@/components/Footer'
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/home'
},
{ // 路由跟组件时映射关系
path: '/home',
name: 'home',
// 路由的懒加载
components: { // 一个路由 对象两个位置发生变化
default: () => import('@/views/home/index.vue'),
footer: Footer
}
},
{
path: '/kind',
name: 'kind',
// component: () => import('@/views/kind/index.vue')
components: { // 一个路由 对象两个位置发生变化
default: () => import('@/views/kind/index.vue'),
footer: Footer
}
},
{
path: '/cart',
name: 'cart',
// component: () => import('@/views/cart/index.vue')
components: { // 一个路由 对象两个位置发生变化
default: () => import('@/views/cart/index.vue'),
footer: Footer
}
},
{
path: '/user',
name: 'user',
// component: () => import('@/views/user/index.vue')
components: { // 一个路由 对象两个位置发生变化
default: () => import('@/views/user/index.vue'),
footer: Footer
}
},
{
path: '/detail',
name: 'detail',
// component: () => import('@/views/detail/index.vue')
components: { // 一个路由 对象两个位置发生变化
default: () => import('@/views/detail/index.vue')
}
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
5.3 App.vue 要添加 命名的视图
<template>
<div class="container">
<router-view></router-view>
<router-view name="footer"></router-view>
</div>
</template>
6、列表点击进入产品的详情
声明式跳转 + 编程式跳转(标签跳转 + js跳转(a + window.location.href=""))
6.1 声明式跳转
6.1.1 地址形如 /detail/123 ---- components/Prolist.1.vue
- 动态路由匹配传值 修改router/index.2.js
{
path: '/detail/:proid', // *************************************
name: 'detail',
// component: () => import('@/views/detail/index.vue')
components: { // 一个路由 对象两个位置发生变化
default: () => import('@/views/detail/index.vue')
}
}
- 列表组件通过 router-link 标签跳转 components/Prolist.vue
:to="'/detail/' + item.proid"
:to = "{ name: 'detail', params: { proid: item.proid }}"
https://router.vuejs.org/zh/guide/essentials/named-routes.html
<template>
<ul class="prolist">
<router-link tag="li" :to="'/detail/' + item.proid" class="proitem" v-for="item of prolist" :key="item.proid">
<div class="itemimg">
<img :src="item.proimg" alt="">
</div>
<div class="iteminfo">
<h2>{{ item.proname }}</h2>
<h3>{{ item.brand }}</h3>
<p>{{ '¥' + item.price }}</p>
</div>
</router-link>
</ul>
</template>
6.1.2 地址刑如 /detail?id=123 ---- components/Prolist.3.vue
此时路由文件得重新更改 ----- router/index.js
{
path: '/detail',
name: 'detail',
// component: () => import('@/views/detail/index.vue')
components: { // 一个路由 对象两个位置发生变化
default: () => import('@/views/detail/index.vue')
}
}
声明式跳转
<router-link tag="li" :to="'/detail?proid=' + item.proid" class="proitem" v-for="item of prolist" :key="item.proid" ></router-link>
6.2 编程式跳转
6.2.1 形如/detail/123 ----- components/Prolist.2.vue
- 列表添加点击事件
<li class="proitem" v-for="item of prolist" :key="item.proid" @click="toDetail(item.proid)"></li>
- 实现点击的函数
methods: {
toDetail (proid) {
console.log(this) // this.$router.push()
this.$router.push('/detail/' + proid)
}
}
拓展:路由是按照数组的形式来存储的 [{}, {}, {}]
this.$router.push() // 打开一个新的页面[{}, {}, {}, {}]
this.$router.replace() // 替换当前页面 [{}, {}, {}]
this.$router.back() // 返回上一页
this.$router.go(-1) // 返回上一页
6.2.2 形如/detail?proid=123 ---- components/Prolist.vue
- 列表添加点击事件
<li class="proitem" v-for="item of prolist" :key="item.proid" @click="toDetail(item.proid)"></li>
实现跳转
methods: {
toDetail (proid) {
this.$router.push('/detail?proid=' + proid)
}
}
7、详情页面获取数据 --- 渲染数据
7.1 形如 /detail/123 获取参数---- views/detail/index.1.vue
<template>
<div class="detail">
<div class="box">
<header class="header">详情头部</header>
<div class="content">
<img :src="proimg" alt="">
<h1>{{ proname }}</h1>
<h3>{{ note }}</h3>
<p>{{ price }}</p>
<ul>
<li v-for="item of commentlist" :key="item.commentid">
<h4>{{ item.username }} - {{ item.rating }}</h4>
<p>{{ item.note }}</p>
</li>
</ul>
</div>
</div>
<footer class="footer">详情底部</footer>
</div>
</template>
<script>
import axios from 'axios'
export default {
data () {
return {
proid: '',
proname: '',
proimg: '',
price: '',
note: '',
commentlist: []
}
},
created () {
console.log(this.$route.params)
const proid = this.$route.params.proid
axios.get('/pro/detail?proid=' + proid).then(res => {
console.log(res.data.data)
this.proid = res.data.data.proid
this.proname = res.data.data.proname
this.proimg = res.data.data.proimg
this.price = res.data.data.price
this.note = res.data.data.note
})
axios.get('/comment?proid=' + proid).then(res => {
console.log(res.data.data)
this.commentlist = res.data.data
})
}
}
</script>
7.2 形如 /detail?id=123 获取参数 ---- views/detail/index.vue
相比 7.1 来说,只需要修改 this.$route.params 为 this.$route.query
8、注册功能
- views/user/index.vue 中添加如下代码
<template>
<div class="box">
<header class="header">个人中心头部</header>
<div class="content">
<div v-if="flag">
<button>登陆</button><button>注册</button>
</div>
<div v-else>
欢迎您 **** <button>退出</button>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
flag: true
}
}
}
</script>
8.1 添加注册页面 views/register/index.vue
<template>
<div class="box">
<header class="header">注册</header>
<div class="content">
注册
</div>
</div>
</template>
8.2 添加注册路由
{
path: '/register',
name: 'register',
components: { // 一个路由 对象两个位置发生变化
default: () => import('@/views/register/index.vue')
}
}
8.3 添加注册表单
<template>
<div class="box">
<header class="header">注册</header>
<div class="content">
<input type="text" placeholder="请输入用户名" v-model="username">
<input type="text" placeholder="请输入电话号码" v-model="tel">
<input type="password" placeholder="请输入密码" v-model="password">
<button class="userBtn">注册</button>
</div>
</div>
</template>
<script>
export default {
data () {
return {
username: '吴大勋',
tel: '18813007814',
password: '123456'
}
}
}
</script>
<style lang="scss">
input {
outline: none;
border: 0;
display: block;
width: 96%;
margin: 5px 2%;
border-bottom: 1px solid #efefef;
line-height: 36px;
text-indent: 10px;
}
.userBtn {
outline: none;
border: 0;
display: block;
background-color:#f66;
width: 96%;
margin: 15px 2%;
line-height: 40px;
font-size: 18px;
color: #fff;
}
</style>
8.4 检验表单信息 ---- 计算属性
<template>
<div class="box">
<header class="header">注册</header>
<div class="content">
<input type="text" placeholder="请输入用户名" v-model="username">
<p class="tip">{{ usernametip }}</p>
<input type="text" placeholder="请输入电话号码" v-model="tel">
<p class="tip">{{ teltip }}</p>
<input type="password" placeholder="请输入密码" v-model="password">
<p class="tip">{{ passwordtip }}</p>
<button class="userBtn" @click="register">注册</button>
{{ tip }}
</div>
</div>
</template>
<script>
export default {
data () {
return {
username: '吴大勋',
tel: '18813007814',
password: '123456',
tip: ''
}
},
computed: {
usernametip () {
if (this.username === '') {
return ''
} else if (this.username.length < 3) {
return '用户名格式错误'
} else {
return ''
}
},
passwordtip () {
if (this.password === '') {
return ''
} else if (this.password.length < 6) {
return '密码格式错误'
} else {
return ''
}
},
teltip () {
if (this.tel === '') {
return ''
} else if (this.tel.length !== 11) {
return '手机号码格式错误'
} else {
return ''
}
}
},
methods: {
register () {
if (this.username === '' || this.usernametip !== '') {
// console.log('用户名输入不正确')
this.tip = '请输入正确的用户名'
return
}
if (this.tel === '' || this.teltip !== '') {
this.tip = '请输入正确的手机号码'
return
}
if (this.password === '' || this.passwordtip !== '') {
this.tip = '请输入正确的密码'
return
}
this.tip = ''
console.log('可以注册了')
}
}
}
</script>
<style lang="scss">
input {
outline: none;
border: 0;
display: block;
width: 96%;
margin: 5px 2%;
border-bottom: 1px solid #efefef;
line-height: 36px;
text-indent: 10px;
}
.tip {
color: #f66;
text-align: center;
height: 20px;
}
.userBtn {
outline: none;
border: 0;
display: block;
background-color:#f66;
width: 96%;
margin: 15px 2%;
line-height: 40px;
font-size: 18px;
color: #fff;
}
</style>
8.5 注册功能
methods: {
register () {
if (this.username === '' || this.usernametip !== '') {
// console.log('用户名输入不正确')
this.tip = '请输入正确的用户名'
return
}
if (this.tel === '' || this.teltip !== '') {
this.tip = '请输入正确的手机号码'
return
}
if (this.password === '' || this.passwordtip !== '') {
this.tip = '请输入正确的密码'
return
}
this.tip = ''
console.log('可以注册了')
axios.post('/users/register', {
username: this.username,
tel: this.tel,
password: this.password
}).then(res => {
console.log(res.data)
if (res.data.code === '10000') {
this.tip = '该用户已注册,请直接登陆'
} else {
this.tip = '注册成功'
}
})
}
}
长风破浪会有时,直挂云帆济沧海