项目首页
一、项目技术栈
- phone:
- vue-cli + vue-router + vuex +sass、less + es6^ +webpack
- server
- express +mysql + doc
二、接口
- 本地接口:http://localhost:8888 所有api的域名以这里的为主
- 本地接口需要在打开数据库,并创建一个新的数据库mall后,运行doc文件中的sql语句,得到表后,才能获得结果
- 远程接口:http://203.195.181.208:8888 或者 itfly.vip
三、项目引入vant
1.创建项目
- 在cmd中,进入到桌面(desktop),输入命令:vue create mall_phone
- 选择第二个vue-ui,因为需要用到babel、router、eslint
- 创建成功
- 通过VSCode打开创建的项目,并运行
- npm run serve 或
- npm start
2.vant
- yarn add vant
- 重启:npm run serve 每次修改文件后需要重启项目
三、项目常见结构
- public 静态资源
- src 源码
- main.js 入口
- App.vue 根组件
- assets common 图片、css
- components 存放普通的组件
- view 存放路由组件
- plugins 存放插件、ui
- api 存放接口
- utils 工具
- mixins 混入
- router 路由
- filters 过滤器
- directives 自定义过滤器
四、手动配置路由
1.yarn add vue-router
2.新建router文件夹,下面有一个index.js文件
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
// 还未写
]
})
// 1. 引入安装好的vue-router
// 2. 注册路由 Vue.use()
// 3. 实例化路由对象
// 4. 定义路由规则
// 设置 hash地址 和组件的对应关系
// 5. 挂载路由
// 6. 渲染路由
- 挂载路由
- 渲染路由
- 移动端中间部分都是渲染的路由
- 移动端中间部分都是渲染的路由
五、项目主要路由配置
- 配置路由时,需要把根元素写好,不然会报错:
<template>
<div class="home">
this is home
</div>
</template>
- 在views文件夹下创建四个主要组件(Home.vue、Friends.vue、Cart.vue、Search.vue),这四个组件是处于移动端页面底部的导航
1.Home组件
1.Home.vue
<template>
<div>home</div>
</template>
<script>
export default {
data: () => ({})
}
</script>
<style lang="less" scoped>
</style>
2.配置路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Friends from '../views/Friends.vue'
import Cart from '../views/Cart.vue'
import Search from '../views/Search.vue'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/friends',
component: Friends
},
{
path: '/cart',
component: Cart
},
{
path: '/search',
component: Search
}
]
})
export default router
3.渲染到页面上(App.vue)
- 需要用到的组件直接到Vant官网找到,并引入到项目的./plugins/vant.js(按需引入)(也可以全部引入)
import Vue from 'vue'
import { Button, Tabbar, TabbarItem, NavBar, Swipe, SwipeItem, Toast, Grid, GridItem } from 'vant'
Vue.use(Button)
Vue.use(Tabbar)
Vue.use(TabbarItem)
Vue.use(NavBar)
Vue.use(Swipe)
Vue.use(SwipeItem)
Vue.use(Grid)
Vue.use(GridItem)
Vue.prototype.$Toast = Toast
- (App.vue)
<template>
<div>
<van-nav-bar
:title="title"
left-text="返回"
left-arrow
/>
<router-view></router-view>
<van-tabbar v-model="active">
<van-tabbar-item
icon="home-o"
to="/home"
></van-tabbar-item>
<van-tabbar-item
icon="friends-o"
to="/friends"
></van-tabbar-item>
<van-tabbar-item
icon="cart-o"
to="/cart"
></van-tabbar-item>
<van-tabbar-item
icon="search"
to="/search"
></van-tabbar-item>
</van-tabbar>
</div>
</template>
<script>
export default {
data: () => ({
title: '首页',
active: 0
}),
methods: {}
}
</script>
<style lang="less" scoped>
</style>
六、返回上一页(App.vue)
<template>
<div>
<van-nav-bar
:title="title"
left-text="返回"
left-arrow
@click-left="onClickLeft"
/>
<router-view></router-view>
<van-tabbar v-model="active">
<van-tabbar-item
icon="home-o"
to="/home"
></van-tabbar-item>
<van-tabbar-item
icon="friends-o"
to="/friends"
></van-tabbar-item>
<van-tabbar-item
icon="cart-o"
to="/cart"
></van-tabbar-item>
<van-tabbar-item
icon="search"
to="/search"
></van-tabbar-item>
</van-tabbar>
</div>
</template>
<script>
export default {
data: () => ({
title: '首页',
active: 0
}),
methods: {
onClickLeft() {
this.$router.go(-1)
}
}
}
</script>
<style lang="less" scoped>
</style>
七、eslint报错处理
- 一. Too many blank lines at the end of file. Max of 0 allowed. (no-multiple-empty-lines)
问题分析:
解决方案
1.文件末尾的空行太多。最大值为0不允许有多个空行。
删除多余的空行。
2.安装 ESLint
我用的开发工具vscode,直接安装插件ESLint
-
二、space-before-function-paren 函数后面需要有空格
-
三、quotes 使用单引号
-
四、comma-dangle 对象中结尾不能有,
-
五、no-multiple-empty-lines 文件末尾的空行太多。最大值为0不允许有多个空行
-
六、vue/valid-template-root 定义的组件需要有div根组件
-
七、semi 语句结尾不能有分号;
-
八、key-spacing 对象中键值对 key后面不能有空格
八、托管项目到git
- 1.创建仓库
- 2.在终端查看状态
- 若之前做了修改,需要:
- git add .
- git commit -m 'xxx'
- git status
- git log(查看刚才所做的操作‘xxx’是否提交了)
- 3.连接远程仓库
git remote add origin http
- http是你的运程仓库地址 需要你在 github 或者 码云上创建
- 4.提交到主分支上去
git push -u origin master
- 5.回到gitee,刷新页面,可以看到代码
- 6.可以创建分支(home分支)
- git操作
git
git init 初始化git本地仓库
git status 查看监听的状态 (可选)
git add . 全部添加到可操作的状态 (当前状态应该为绿色)
git commit -m 'first' 把监听的文件添加到本地仓库
git log 查看修改日志 (可选)
git remote add origin http http是你的运程仓库地址 需要你在 github 或者 码云上创建
git push -u origin master 提交到主分支上
git checkout -b 分支的名称 创建并切换到新分支
git branch 查看分支
git checkout 分支名称 切换分支
git push -u origin 分支名称
git reset --hard commit_id 代码回退
合并
git merge 分支名称 要确保你是在master 分支上操作
删除
git branch -d 分支名称 删除本地分支
git branch -D 分支名称 强制删除本地分支
git push origin --delete 分支名称 删除远程分支
重命名
git branch -m 要改的本地分支名 修改后的分支名(修改本地分支)
git push origin :远程修改前的分支名(删除远程分支)
git push origin 修改后的分支名:修改后的分支名(push 到远程分支)
git branch --set-upstream 修改后的分支名 origin/修改后的分支名(绑定远程分支)
代码拉取
git pull origin master
强制删除远程分支
git push origin --delete 分支名称
出现这个问题是因为gitee中的文件不在本地代码目录中,可以通过如下命令进行代码合并,之后在提交
代码合并
git merge 分支名称 本地合并
git push 提交到远程
git pull --rebase origin master 同步远程和本地的代码
AppID(小程序ID) wx2739bc5dddb04a70
每次操作完后记得查看状态 在git add . 和git commit -m ' ' 后再进行下一步操作
九、home组件-轮播图请求渲染
1.引入轮播组件
2.需要用到axios(下载)
cnpm i axios -S
3.写请求
1.轮播函数(函数需要在created()函数中调用)
写完请求后,先打印一下结果,看是否获取到了数据
- 查看接口(可以在此了解数据的组成,有哪些内容、属性)
2.在轮播组件上循环
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
<van-swipe-item v-for="item in lunbolist" :key="item.id">
<img :src="item.img" alt="" />
</van-swipe-item>
</van-swipe>
- 可以为轮播图设置样式
<style lang="less" >
// lang 设置使用哪个预编译语言
// scoped 限制当前样式之能在当前组件使用
.home {
.my-swipe {
height: 200px;
// background-color: red;
img {
width: 100%;
height: 100%;
}
}
.van-grid-item__icon {
font-size: 60px;
}
}
</style>
Home.vue完整代码
<template>
<div class="home">
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
<van-swipe-item v-for="item in lunbolist" :key="item.id">
<img :src="item.img" alt="" />
</van-swipe-item>
</van-swipe>
<van-grid :column-num="3">
<van-grid-item
v-for="grid in grids"
:key="grid.id"
:icon="grid.src"
:text="grid.title"
/>
</van-grid>
</div>
</template>
<script>
export default {
data: () => ({
lunbolist: [],
grids: []
}),
methods: {
async getLunbo() {
const { data: { message } } = await this.$http.getLunbo()
this.lunbolist = message
},
async getGrids() {
const { data: { message } } = await this.$http.getGrids()
this.grids = message
}
},
created() {
this.getLunbo()
this.getGrids()
}
}
</script>
<style lang="less" >
// lang 设置使用哪个预编译语言
// scoped 限制当前样式之能在当前组件使用
.home {
.my-swipe {
height: 200px;
// background-color: red;
img {
width: 100%;
height: 100%;
}
}
.van-grid-item__icon {
font-size: 60px;
}
}
</style>
- 如果遇到如下问题:
- 解决: 删除node_modules包,再在终端执行:npm i
- 引入axios
import axios from 'axios'
若没有引入则会报如下错误:
十、home组件-grid请求渲染
1.设置好接口
<script>
export default {
data: () => ({
lunbolist: [],
grids: []
}),
methods: {
async getLunbo() {
const { data: { message } } = await this.$http.getLunbo()
this.lunbolist = message
},
async getGrids() {
const { data: { message } } = await this.$http.getGrids()
this.grids = message
}
},
created() {
this.getLunbo()
this.getGrids()
}
}
</script>
2.引入组件
<van-grid :column-num="3">
<van-grid-item
v-for="grid in grids"
:key="grid.id"
:icon="grid.src"
:text="grid.title"
/>
</van-grid>
- 引入组件
import Vue from 'vue'
import { Button, Tabbar, TabbarItem, NavBar, Swipe, SwipeItem, Toast, Grid, GridItem } from 'vant'
Vue.use(Button)
Vue.use(Tabbar)
Vue.use(TabbarItem)
Vue.use(NavBar)
Vue.use(Swipe)
Vue.use(SwipeItem)
Vue.use(Grid)
Vue.use(GridItem)
Vue.prototype.$Toast = Toast
十一、优化axios请求 单独抽离接口模块
- ./api/index.js
// import Vue from 'vue'
// import axios from 'axios'
// axios.defaults.baseURL = 'http://itfly.vip:8888'
// Vue.prototype.$http = axios
import Vue from 'vue'
import axios from 'axios'
import { Toast } from 'vant'
// axios.defaults.baseURL = 'http://itfly.vip:8888'
const http = axios.create({
baseURL: 'http://itfly.vip:8888'
})
// 响应拦截
http.interceptors.response.use(response => {
// Toast('succss')
return response
}, err => {
Toast(err)
})
//
Vue.prototype.$http = {
async getLunbo() {
return await http.get('/api/getlunbo')
},
async getGrids() {
return await http.get('/api/grids')
}
}
十二、newlist组件-路由跳转-页面请求渲染
- newlist.vue
<template>
<div class="news-list">
<van-card
v-for="item in newslist"
:key="item.id"
:title="item.title"
:thumb="item.img_url"
@click="goDatil(item.id)"
>
<template #price>
<div>
{{ item.add_time }}
</div>
</template>
<template #num>
<div>点击{{ item.click }}次</div>
</template>
</van-card>
</div>
</template>
<script>
export default {
data: () => ({
newslist: []
}),
methods: {
async getNewsList() {
const { data: { message } } = await this.$http.getNewsList()
this.newslist = message
console.log(this.newslist)
},
goDatil(id) {
// 跳转的同时传递id
this.$router.push('/home/newsinfo/' + id)
}
},
created() {
this.getNewsList()
}
}
</script>
<style lang="less">
.news-list {
.van-card__thumb {
height: 55px;
}
.van-card__content {
min-height: 55px;
}
.van-card__price {
color: red;
}
.van-card__title {
// 用于省略
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
}
</style>
- router/index.js
import Newslist from '../views/Home/news/Newslist'
{
path: '/home/newslist',
component: Newslist
}
-
在vant.js中引入需要的组件
-
api/index.js
async getNewsList() {
return await http.get('/api/getnewslist')
}
十三、newsinfo组件
- 在newslist.vue点击跳转到newsinfo
van-card
v-for="item in newslist"
:key="item.id"
:title="item.title"
:thumb="item.img_url"
@click="goDatil(item.id)"//跳转到newsinfo
>
。。。。
methods: {
async getNewsList() {
const { data: { message } } = await this.$http.getNewsList()
this.newslist = message
console.log(this.newslist)
},
goDatil(id) {
// 跳转的同时传递id
this.$router.push('/home/newsinfo/' + id)
}
}
1.newsinfo头部请求渲染
- api/index.js
async getNewsInfo(id) {
return await http.get('/api/getnew/' + id)
}
- router/index.js
import Newsinfo from '../views/Home/news/Newsinfo'
{
path: '/home/newsinfo/:id',
component: Newsinfo,
props: true
}
- newsinfo.vue
<template>
<div class="news-info">
<van-panel
:title="newsinfo.zhaiyao"
:status="'阅读' + newsinfo.click + '次'"
>
<div class="content">{{ newsinfo.content }}</div>
</van-panel>
<Comment :id="id"></Comment>
</div>
</template>
<script>
// @相当于src
import Comment from '@/components/Comment.vue'
// import Comment from '../../../../components/Comment.vue'
export default {
components: { Comment },
props: ['id'],
data: () => ({
newsinfo: {}
}),
methods: {
async getNewsInfo() {
const res = await this.$http.getNewsInfo(this.id)
this.newsinfo = res.data.message
}
},
created() {
// 接受id
// this.id = this.$route.params.id
this.getNewsInfo()
},
conponents: {
Comment
}
}
</script>
<style lang="less" scoped>
.news-info {
.van-panel__header {
font-size: 12px;
.van-cell__title {
flex: 2;
font-size: 16px;
font-weight: 600;
color: red;
}
}
.content {
text-align: center;
text-indent: 2em;
}
}
</style>
2.comment组件-页面布局
- 评论以下的归为一个组件
- 新建一个文件夹:conponents,下面有一个Comments.js组件
- 在newsinfo.vue中引入Comments.js,并注册、渲染
// 引入Comments.js
import Comment from '@/components/Comment.js'
// 注册
conponents: {
Comment
}
// 渲染
<Comment></Comment>
- api/index.js
/**
*
* @param {number} id 评论id
* @param {number} pageNo 页码
* @param {number} pageSize 每页的总条数
* @returns Promise
*/
async getComments({ id, pageNo, pageSize }) {
return await http.get(`/api/getComments/${id}?pageindex=${pageNo}&limit=${pageSize}`)
}
- comments.js
<template>
<div class="comment">
<van-field
class="my-filed"
v-model="message"
rows="2"
label="留言"
type="textarea"
maxlength="50"
:placeholder="placeholder"
show-word-limit
/>
<van-button class="my-post" type="primary" block @click="postComments"
>发表评论</van-button
>
<van-card
v-for="(comment, index) in comments"
:key="index"
:desc="comment.content"
>
<template #thumb>
<div class="box">
<van-icon
name="https://b.yzcdn.cn/vant/icon-demo-1126.png"
class="name"
/>
<div>{{ comment.user_name }}</div>
</div>
</template>
<template #title>
<van-rate v-model="comment.rate" readonly />
</template>
<template #price> {{ comment.add_time | datefmt }} </template>
</van-card>
<van-button type="warning" block @click="goMore">{{ moreText }}</van-button>
</div>
</template>
<script>
export default {
props: ['id'],
data: () => ({
message: '',
placeholder: '请输入bb的内容',
pageNo: 2,
pageSize: 4,
comments: [],
hasMore: false
}),
methods: {
async getComments() {
// 节流
if (this.hasMore !== false) return
++this.pageNo
const res = await this.$http.getComments({ id: this.id, pageNo: this.pageNo, pageSize: this.pageSize })
// 上一页拼接下一页的数据
this.comments = this.comments.concat(res.data.message)
// 计算是否已经加载完毕
this.hasMore = this.pageNo * this.pageSize > res.data.count
},
async postComments() {
await this.$http.postComments({ id: this.id, content: this.message })
this.comments.unshift({
add_time: new Date().getTime(),
content: this.message,
rate: 5,
user_name: 'Svip'
})
this.message = ''
},
goMore() {
this.getComments()
}
},
created() {
this.getComments()
},
computed: {
moreText() {
return this.hasMore ? '被掏空了' : '加载更多'
}
}
}
</script>
<style lang="less" scoped>
.comment {
margin-top: 10px;
.my-field {
border: 1px solid #ddd;
}
.my-post {
margin-top: 10px;
}
.box {
.name {
font-size: 40px;
}
}
.van-card__thumb {
height: 55px;
}
.van-card__content {
min-height: 55px;
}
}
</style>
- 在newsinfo.vue传递id
<Comment :id="id"></Comment>
- 提交评论
- api/index.js
/** * * @param {number} id 评论id * @param {String} content 评论内容 * @returns Promise */ async postComments({ id, content }) { return await http.post(`/api/postcomment/${id}`, { content }) } - 格式化时间
- 1.创建文件夹filters,下有datefmt.js
- 2.yarn add moment
- 3.在main.js引入该文件:import from './filters/datefmt'
- 4.对想要格式化的时间进行格式化:{{comment.add_time|datefmt}}
- datefmt.js
import Vue from 'vue' import moment from 'moment' Vue.filter('datefmt', arg => { return moment(arg).format('YYYY-MM-DD HH:mm:ss') })- main.js
import Vue from 'vue' import App from './App.vue' import router from './router' import './plugins/vant.js' import './api' import './filter/datefmt' Vue.config.productionTip = false new Vue({ router, render: h => h(App) }).$mount('#app') - 设置评分为只读
<template #title>
<van-rate v-model="comment.rate" readonly />
</template>