智慧商城项目(ing)

项目概述

在这里插入图片描述

1.创建项目

目标:基于VueCli自定义创建项目架子,将目录调整成符合企业规范的目录

在这里插入图片描述

  1. 删除多余的文件

  2. 修改路由配置 和App.vue

  3. 新增两个目录 api/utils

①api接口模块:发送ajax请求的接口模块

②utils工具模块:自己封装的一些工具方法模块

2.vant组件库

目标:认识第三方Vue组件库vant-ui
组件库:第三方封装好了很多的组件,整合到一起就是一个组件库
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/

Vue的组件库并不是唯一的,vant-ui也仅仅是组件库的一种
一般会按照不同的平台进行分类
① PC端:element-ui(element-plus) ,ant-design-vue
② 移动端:vant-ui ; Mint UI(饿了么) ; Cube UI(滴滴)

vant全部导入 和 按需导入

目标:明确 全部导入 和 按需导入 的区别

全部导入就相当于把整个工具箱全都放在了项目当中,好处是方便,就是我在任何页面中想要加一个密码框等效果,由于是导入整个,所以在那用都可以.缺点是会把所有组件都导入项目中,会使项目体积非常大
在这里插入图片描述

按需导入就相当于有vant这么一个工具箱,但是在项目当中要用的时候,并不是把整个工具箱丢到项目中,而是需要用到哪一个,就导入哪一个来使用
在这里插入图片描述

组件库的使用

1.全部导入

① 安装vant-ui

yarn add vant@latest-v2

②main.js中注册

import Vant from 'vant'
import 'vant/lib/index.css'
//把vant中所有组件都导入了
Vue.use(Vant)

③测试使用

<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
2.按需导入

①安装 vant-ui(已安装)

②安装插件

npm i babel-plugin-import -D

③babel.config.js中配置

module.exports = {presets: [@vue/cli-plugin-babel/preset'],plugins: [['import', {libraryName: 'vant',libraryDirectory: 'es',style: true}, 'vant']}
}

④main.js按需导入注册

import Vue from 'vue';
import { Button } from 'vant';Vue.use(Button);

⑤测试使用

<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>

为了将来不在main.js中到人很多,然后写的很长👇
在这里插入图片描述
可以在utils文件中建一个vant-ui.js,把要的东西放在这里面👇
在这里插入图片描述
然后再在main.js中导入这个文件👇
在这里插入图片描述

3.项目中的vw适配

目标:基于postcss插件,实现项目vw适配

Vant 默认使用 px 作为样式单位,如果需要使用 viewport 单位 (vw, vh, vmin, vmax),推荐使用 postcss-px-to-viewport 进行转换。
postcss-px-to-viewport 是一款 PostCSS 插件,用于将 px 单位转化为 vw/vh 单位。

视图中的长度是px,那么就需要把px转换成vw(vant2进阶用法)
①安装插件

yarn add postcss-px-to-viewport@1.1.1 -D

②根据目录新建postcss.comfig.js

// postcss.config.js
module.exports = {plugins: {'postcss-px-to-viewport': {viewportWidth: 375,},},
};

4.路由设计配置

①目标分析项目页面,设计路由,配置一级路由

但凡是单个页面,独立展示的,都是一级路由

准备好views中的各个页面,然后开始在index.js中配置路由
在这里插入图片描述
配对应的路由规则,路由规则就是数组里面包对象

import Login from '@/views/login'
import Layout from '@/views/layout'
import Search from '@/views/search'
import SearchList from '@/views/search/list'
import ProDetail from '@/views/prodetail'
import Pay from '@/views/pay'
import MyOrder from '@/views/myorder'
import Home from '@/views/layout/home'
import Category from '@/views/layout/category'
import Cart from '@/views/layout/cart'
import User from '@/views/layout/user'
import store from '@/store'
Vue.use(VueRouter)
const router = new VueRouter({routes: [{ path: '/login', component: Login },{path: '/',component: Layout,// 首页redirect: '/home',children: [{ path: '/home', component: Home },{ path: '/category', component: Category },{ path: '/cart', component: Cart },{ path: '/user', component: User }]},{ path: '/search', component: Search },{ path: '/searchlist', component: SearchList },// 动态路由传参,确认将来是哪个商品,路由参数中携带 id  这个是商品详情,所以得知道是哪一个商品,这就需要用到动态路由传参{ path: '/prodetail/:id', component: ProDetail },{ path: '/pay', component: Pay },{ path: '/myorder', component: MyOrder }]
})

基础用法

Icon 的 name 属性支持传入图标名称或图片链接,所有可用的图标名称见右侧示例。

<van-icon name="chat-o" />
<van-icon name="https://b.yzcdn.cn/vant/icon-demo-1126.png" />

徽标提示

设置 dot 属性后,会在图标右上角展示一个小红点;设置 badge 属性后,会在图标右上角展示相应的徽标。

<van-icon name="chat-o" dot />
<van-icon name="chat-o" badge="9" />
<van-icon name="chat-o" badge="99+" />

图标颜色

Icon 的 color 属性用来设置图标的颜色。

<van-icon name="cart-o" color="#1989fa" />
<van-icon name="fire-o" color="#ee0a24" />

图标大小

Icon 的 size 属性用来设置图标的尺寸大小,默认单位为 px。

<van-icon name="chat-o" size="40" /> <van-icon name="chat-o" size="3rem" />

自定义颜色

<van-tabbar v-model="active" active-color="#ee0a24" inactive-color="#000"><van-tabbar-item icon="home-o">标签</van-tabbar-item><van-tabbar-item icon="search">标签</van-tabbar-item><van-tabbar-item icon="friends-o">标签</van-tabbar-item><van-tabbar-item icon="setting-o">标签</van-tabbar-item>
</van-tabbar>
//active-color是激活时的颜色,inactive是未激活

在这里插入图片描述

②基于底部导航,完成二级路由配置

在这里插入图片描述
在这里插入图片描述

5.登录静态页面布局

1.准备工作

(1)新建‘styles/common.less’重置默认样式

// 重置默认样式
* {margin: 0;padding: 0;box-sizing: border-box;
}// 文字溢出省略号
.text-ellipsis-2 {overflow: hidden;-webkit-line-clamp: 2;text-overflow: ellipsis;display: -webkit-box;-webkit-box-orient: vertical;
}

(2) main.js 导入common.less

import '@/styles/common.less'

(3)图片素材拷贝到assets目录【备用】

2.登录页静态布局编写

(1)头部组件说明(NavBar)

<van-nav-bar title="会员登录" left-arrow @click-left="$router.go(-1)" />

在这里插入图片描述
在这里插入图片描述
这里的这个小箭头是蓝色的,希望以后所有的都是蓝色的,就可以把他写到通用样式里👇
先在检查里找到它的名字👇
在这里插入图片描述
然后在👇里面写

在这里插入图片描述

// 添加导航的通用样式
.van-nav-bar {.van-nav-bar__arrow {color: #333;}
}

(2)通用样式覆盖

(3)其他静态结构编写

6.request模块 - axios封装

目标:将axios请求方法,封装到request模块

使用axios来请求后端接口,一般都会对axios进行 一些配置(比如:配置基础地址,请求响应拦截器等)
所以项目开发中,都会对axios进行基本的二次封装,单独封装到一个request模块中,便于维护使用
在这里插入图片描述

  1. 安装 axios
npm i axios
  1. 新建 utils/request.js 封装 axios 模块

    利用 axios.create 创建一个自定义的 axios 来使用

    http://www.axios-js.com/zh-cn/docs/#axios-create-config

/* 封装axios用于发送请求 */
import axios from 'axios'// 创建一个新的axios实例,将来对创建出来的实例,进行自定义配置
// 好处:不会污染原始的axios实例
const request = axios.create({baseURL: 'http://cba.itlike.com/public/index.php?s=/api/',timeout: 5000
})// 添加请求拦截器
request.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config
}, function (error) {// 对请求错误做些什么return Promise.reject(error)
})// 添加响应拦截器
request.interceptors.response.use(function (response) {// 对响应数据做点什么return response.data
}, function (error) {// 对响应错误做点什么return Promise.reject(error)
})export default request
  1. 获取图形验证码,请求测试
import request from '@/utils/request'
export default {name: 'LoginPage',async created () {const res = await request.get('/captcha/image')console.log(res)}
}

在这里插入图片描述

7.图形验证码功能完成

目标:基于请求回来的base64图片,实现图形验证码功能

说明:

  1. 图形验证码,本质就是一个从后台请求回来的一个图片
  2. 用户将来输入图形验证码,用于强制人机交互,可以抵御机器自动化攻击(例如:避免批量请求获取短信)

需求:

  1. 动态将请求回来的base64图片,解析渲染出来
  2. 点击验证码图片盒子,要刷新验证码
  1. 准备数据,获取图形验证码后存储图片路径,存储图片唯一标识
<img v-if="picUrl" :src="picUrl" @click="getPicCode" alt="">
<script>
import { codeLogin, getMsgCode, getPicCode } from '@/api/login'
// import { Toast } from 'vant'export default {name: 'LoginPage',data () {return {picKey: '', // 将来请求传递的图形验证码唯一标识picUrl: '', // 存储请求渲染的图片地址picCode: '', // 用户输入的图形验证码}},async created () {this.getPicCode()},methods: {// 获取图形验证码async getPicCode () {const { data: { base64, key } } = await getPicCode()this.picUrl = base64 // 存储地址this.picKey = key // 存储唯一标识},}
  1. 动态渲染图形验证码,并且点击时要重新刷新验证码
<img v-if="picUrl" :src="picUrl" @click="getPicCode">

8.api接口模块 - 封装图片验证码接口

目标:将请求封装成方法,统一存放到aip模块,与页面分离

在这里插入图片描述

//😄login/index.vue
import { getPicCode } from '@/api/login'methods: {// 获取图形验证码async getPicCode () {const { data: { base64, key } } = await getPicCode()this.picUrl = base64 // 存储地址this.picKey = key // 存储唯一标识// Toast('获取图形验证码成功')// this.$toast('获取成功')// this.$toast.success('成功文案')},//😄api/login.js
// 此处用于存放所有登录相关的接口请求
import request from '@/utils/request'// 1. 获取图形验证码
export const getPicCode = () => {return request.get('/captcha/image')
}

9.Toast轻提示

在这里插入图片描述

①导入调用

引入

import Vue from 'vue';
import { Toast } from 'vant';Vue.use(Toast);

文字提示

Toast('提示内容');

②通过this直接调用

全局方法

引入 Toast 组件后,会自动在 Vue 的 prototype 上挂载 $toast 方法,便于在组件内调用。

export default {mounted() {this.$toast('提示文案');},
};

😘方法①
在login/index.vue中导入调用

import { Toast } from 'vant'

那么在合适的位置就可以调用了(在图形验证码的地方)

Toast('获取图形验证码成功')

这种方式组件内或非组件内均可使用(在main.js中也可以)

😘方法②(this)

//在组件内直接使用
this.$toast('获取成功')

10.短信验证倒计时

步骤分析

  1. 点击按钮,实现倒计时效果

  2. 倒计时之前的校验处理(手机号,验证码)

  3. 封装短信验证请求接口,发送请求添加提示

1.login/index.vue

export default {name: 'LoginPage',data () {return {totalSecond: 60, // 总秒数second: 60, // 当前秒数,开定时器对 second--timer: null, // 定时器 id}},
}

2.准备好了就可以注册点击事件了

<button @click="getCode">{{ second === totalSecond ? '获取验证码' : second + '秒后重新发送'}}
</button>

3.getCode还要写一个methods

 async getCode () {if (!this.validFn()) {// 如果没通过校验,没必要往下走了return}😊 // 当前目前没有定时器开着,且 totalSecond 和 second 一致 (秒数归位) 才可以倒计时if (!this.timer && this.second === this.totalSecond) {// 发送请求// 预期:希望如果响应的status非200,最好抛出一个promise错误,await只会等待成功的promiseawait getMsgCode(this.picCode, this.picKey, this.mobile)this.$toast('短信发送成功,注意查收')// 开启倒计时this.timer = setInterval(() => {this.second--if (this.second <= 0) {clearInterval(this.timer)this.timer = null // 重置定时器 idthis.second = this.totalSecond // 归位}}, 1000)}},

4.应为这些代码,离开页面时还在计时,所以要写一个离开页面清除定时器

  // 离开页面清除定时器destroyed () {clearInterval(this.timer)}

在这里插入图片描述


5.还要校验手机号和图形验证码是否合法

先在data中提供两个变量

export default {name: 'LoginPage',data () {return {mobile: '', // 手机号picCode: '', // 用户输入的图形验证码}},
}

6.然后给手机号和验证码进行绑定

<div class="form-item"><input v-model="mobile" class="inp" maxlength="11" placeholder="请输入手机号码" type="text"></div>
<div class="form-item"><input v-model="picCode" class="inp" maxlength="5" placeholder="请输入图形验证码" type="text"><img v-if="picUrl" :src="picUrl" @click="getPicCode" alt="">
</div>

7.校验 手机号 和 图形验证码 是否合法

 // 校验 手机号 和 图形验证码 是否合法// 通过校验,返回true// 不通过校验,返回falsevalidFn () {if (!/^1[3-9]\d{9}$/.test(this.mobile)) {this.$toast('请输入正确的手机号')return false}if (!/^\w{4}$/.test(this.picCode)) {this.$toast('请输入正确的图形验证码')return false}return true},

8.还需要再在上面的倒计时前面加个判断👇

在这里插入图片描述

  1. 输入框 v-model 绑定变量
data () {return {mobile: '', // 手机号picCode: '' // 图形验证码}
},<input v-model="mobile" class="inp" maxlength="11" placeholder="请>输入手机号码" type="text">
<input v-model="picCode" class="inp" maxlength="5" placeholder="请输入图形验证码" type="text">
  1. methods中封装校验方法
// 校验输入框内容
validFn () {if (!/^1[3-9]\d{9}$/.test(this.mobile)) {this.$toast('请输入正确的手机号')return false}if (!/^\w{4}$/.test(this.picCode)) {this.$toast('请输入正确的图形验证码')return false}return true
},
  1. 请求倒计时前进行校验
// 获取短信验证码
async getCode () {if (!this.validFn()) {return}...
}

9.封装短信验证请求接口,发送请求添加提示

api/login.js

// 2. 获取短信验证码
export const getMsgCode = (captchaCode, captchaKey, mobile) => {return request.post('/captcha/sendSmsCaptcha', {form: {captchaCode,captchaKey,mobile}})
}

加这个👇
在这里插入图片描述

11.封装api登录接口,实现登录功能

1.阅读文档,封装登录接口

2.登录前的校验

3.调用方法,发送请求,成功添加提示并跳转

api/login.js

// 3. 登录接口
export const codeLogin = (mobile, smsCode) => {return request.post('/passport/login', {form: {isParty: false,partyData: {},mobile,smsCode}})
}

登录界面的校验,校验通过后,调用上面的接口
login/index.vue

 // 登录async login () {if (!this.validFn()) {return}if (!/^\d{6}$/.test(this.msgCode)) {this.$toast('请输入正确的手机验证码')return}console.log('发送登录请求')const res = await codeLogin(this.mobile, this.msgCode)this.$store.commit('user/setUserInfo', res.data)this.$toast('登录成功')// 进行判断,看地址栏有无回跳地址// 1. 如果有   => 说明是其他页面,拦截到登录来的,需要回跳// 2. 如果没有 => 正常去首页const url = this.$route.query.backUrl || '/'this.$router.replace(url)}
在data中添加
msgCode: '' // 短信验证码

12.响应拦截器统一处理错误提示

问题:每次请求,都会有可能会有错误,就都需要错误提示

说明:响应拦截器是咱们拿到数据的 第一个 数据流转站,可以在里面统一处理错误
在这里插入图片描述
utils/request.js

import { Toast } from 'vant'...// 添加响应拦截器
request.interceptors.response.use(function (response) {const res = response.dataif (res.status !== 200) {Toast(res.message)return Promise.reject(res.message)}// 对响应数据做点什么return res
}, function (error) {// 对响应错误做点什么return Promise.reject(error)
})

13.登录权证信息存储

目标:vuex构建user模块存储登录权证(token & userld)
补充说明:
1.token存入vuex的好处,易获取,响应式
2.vuex需要分模块 => user模块
构建user模块挂载到vuex提供mutations页面中commit调用

1. 新建 vuex user 模块 store/modules/user.js

export default {namespaced: true,state () {return {userInfo: {token: '',userId: ''},}},mutations: {},actions: {}
}

2. 挂载到 vuex 上

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'Vue.use(Vuex)export default new Vuex.Store({modules: {user,}
})

3. 提供 mutations

mutations: {setUserInfo (state, obj) {state.userInfo = obj},
},

4. 页面中 commit 调用

// 登录按钮(校验 & 提交)
async login () {if (!this.validFn()) {return}...const res = await codeLogin(this.mobile, this.msgCode)this.$store.commit('user/setUserInfo', res.data)this.$router.push('/')this.$toast('登录成功')
}

14.storage存储模块 - vuex持久化处理

目标:封装storage存储模块,利用本地储存,进行buex持久化处理

问题1:vuex刷新多久会丢失,怎么办?

//将token存入本地
localStorage.setItem('hm_shopping_info',JSON.stringify(xxx))

问题2么此存取操作太长怎么办?

把它封装成专门的存储模块storage,把一个一个存储方法封装成函数,到时候页面中只要调用对应的函数就可以了

  1. 新建 utils/storage.js 封装方法
const INFO_KEY = 'hm_shopping_info'// 获取个人信息
export const getInfo = () => {const result = localStorage.getItem(INFO_KEY)return result ? JSON.parse(result) : {token: '',userId: ''}
}// 设置个人信息
export const setInfo = (info) => {localStorage.setItem(INFO_KEY, JSON.stringify(info))
}// 移除个人信息
export const removeInfo = () => {localStorage.removeItem(INFO_KEY)
}
  1. vuex user 模块持久化处理
import { getInfo, setInfo } from '@/utils/storage'
export default {namespaced: true,state () {return {userInfo: getInfo()}},mutations: {setUserInfo (state, obj) {state.userInfo = objsetInfo(obj)}},actions: {}
}

15.添加请求loading效果

背景:有时候因为网络原因,一次请求的结果可能需要一段时间后才能回来,

此时,需要给用户 添加loading效果

实操步骤:

  1. 请求拦截器中,每次请求,打开loading
  2. 响应拦截器中,每次响应,关闭loading

1. 请求时,打开 loading

// 添加请求拦截器
request.interceptors.request.use(function (config) {// 在发送请求之前做些什么Toast.loading({message: '请求中...',forbidClick: true,loadingType: 'spinner',duration: 0})return config
}, function (error) {// 对请求错误做些什么return Promise.reject(error)
})

2. 响应时,关闭 loading

// 添加响应拦截器
request.interceptors.response.use(function (response) {const res = response.dataif (res.status !== 200) {Toast(res.message)return Promise.reject(res.message)} else {// 清除 loading 中的效果Toast.clear()}// 对响应数据做点什么return res
}, function (error) {// 对响应错误做点什么return Promise.reject(error)
})

16. 登录访问拦截 - 路由前置守卫

目标:基于全局前置守卫,进行页面访问拦截处理

说明:智慧商城项目,大部分页面,游客都可以直接访问, 如遇到需要登录才能进行的操作,提示并跳转到登录

但是:对于支付页,订单页等,必须是登录的用户才能访问的,游客不能进入该页面,需要做拦截处理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

路由导航守卫 - 全局前置守卫

1.所有的路由一旦被匹配到,都会先经过全局前置守卫

2.只有全局前置守卫放行,才会真正解析渲染组件,才能看到页面内容

router.beforeEach((to, from, next) => {// 1. to   往哪里去, 到哪去的路由信息对象  // 2. from 从哪里来, 从哪来的路由信息对象// 3. next() 是否放行//    如果next()调用,就是放行//    next(路径) 拦截到某个路径页面
})

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

const authUrl = ['/pay', '/myorder']
router.beforeEach((to, from, next) => {const token = store.getters.tokenif (!authUrl.includes(to.path)) {next()return}if (token) {next()} else {next('/login')}
})

17. 首页 - 静态结构准备

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 静态结构和样式 layout/home.vue
<template><div class="home"><!-- 导航条 --><van-nav-bar title="智慧商城" fixed /><!-- 搜索框 --><van-searchreadonlyshape="round"background="#f1f1f2"placeholder="请在此输入搜索关键词"@click="$router.push('/search')"/><!-- 轮播图 --><van-swipe class="my-swipe" :autoplay="3000" indicator-color="white"><van-swipe-item><img src="@/assets/banner1.jpg" alt=""></van-swipe-item><van-swipe-item><img src="@/assets/banner2.jpg" alt=""></van-swipe-item><van-swipe-item><img src="@/assets/banner3.jpg" alt=""></van-swipe-item></van-swipe><!-- 导航 --><van-grid column-num="5" icon-size="40"><van-grid-itemv-for="item in 10" :key="item"icon="http://cba.itlike.com/public/uploads/10001/20230320/58a7c1f62df4cb1eb47fe83ff0e566e6.png"text="新品首发"@click="$router.push('/category')"/></van-grid><!-- 主会场 --><div class="main"><img src="@/assets/main.png" alt=""></div><!-- 猜你喜欢 --><div class="guess"><p class="guess-title">—— 猜你喜欢 ——</p><div class="goods-list"><GoodsItem v-for="item in 10" :key="item"></GoodsItem></div></div></div>
</template><script>
import GoodsItem from '@/components/GoodsItem.vue'
export default {name: 'HomePage',components: {GoodsItem}
}
</script><style lang="less" scoped>
// 主题 padding
.home {padding-top: 100px;padding-bottom: 50px;
}// 导航条样式定制
.van-nav-bar {z-index: 999;background-color: #c21401;::v-deep .van-nav-bar__title {color: #fff;}
}// 搜索框样式定制
.van-search {position: fixed;width: 100%;top: 46px;z-index: 999;
}// 分类导航部分
.my-swipe .van-swipe-item {height: 185px;color: #fff;font-size: 20px;text-align: center;background-color: #39a9ed;
}
.my-swipe .van-swipe-item img {width: 100%;height: 185px;
}// 主会场
.main img {display: block;width: 100%;
}// 猜你喜欢
.guess .guess-title {height: 40px;line-height: 40px;text-align: center;
}// 商品样式
.goods-list {background-color: #f6f6f6;
}
</style>
  1. 新建components/GoodsItem.vue
<template><div class="goods-item" @click="$router.push('/prodetail')"><div class="left"><img src="@/assets/product.jpg" alt="" /></div><div class="right"><p class="tit text-ellipsis-2">三星手机 SAMSUNG Galaxy S23 8GB+256GB 超视觉夜拍系统 超清夜景 悠雾紫5G手机 游戏拍照旗舰机s23</p><p class="count">已售104件</p><p class="price"><span class="new">¥3999.00</span><span class="old">¥6699.00</span></p></div></div>
</template><script>
export default {}
</script><style lang="less" scoped>
.goods-item {height: 148px;margin-bottom: 6px;padding: 10px;background-color: #fff;display: flex;.left {width: 127px;img {display: block;width: 100%;}}.right {flex: 1;font-size: 14px;line-height: 1.3;padding: 10px;display: flex;flex-direction: column;justify-content: space-evenly;.count {color: #999;font-size: 12px;}.price {color: #999;font-size: 16px;.new {color: #f03c3c;margin-right: 10px;}.old {text-decoration: line-through;font-size: 12px;}}}
}
</style>
  1. 组件按需引入
import { Search, Swipe, SwipeItem, Grid, GridItem } from 'vant'Vue.use(GridItem)
Vue.use(Search)
Vue.use(Swipe)
Vue.use(SwipeItem)
Vue.use(Grid)

18. 首页 - 动态渲染

  1. 封装准备接口 api/home.js
import request from '@/utils/request'// 获取首页数据
export const getHomeData = () => {return request.get('/page/detail', {params: {pageId: 0}})
}
  1. 页面中请求调用
import GoodsItem from '@/components/GoodsItem.vue'
import { getHomeData } from '@/api/home'
export default {name: 'HomePage',components: {GoodsItem},data () {return {bannerList: [],navList: [],proList: []}},async created () {const { data: { pageData } } = await getHomeData()this.bannerList = pageData.items[1].datathis.navList = pageData.items[3].datathis.proList = pageData.items[6].data}
}
  1. 轮播图、导航、猜你喜欢渲染
<!-- 轮播图 -->
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white"><van-swipe-item v-for="item in bannerList" :key="item.imgUrl"><img :src="item.imgUrl" alt=""></van-swipe-item>
</van-swipe><!-- 导航 -->
<van-grid column-num="5" icon-size="40"><van-grid-itemv-for="item in navList" :key="item.imgUrl":icon="item.imgUrl":text="item.text"@click="$router.push('/category')"/>
</van-grid><!-- 猜你喜欢 -->
<div class="guess"><p class="guess-title">—— 猜你喜欢 ——</p><div class="goods-list"><GoodsItem v-for="item in proList"  :item="item" :key="item.goods_id"></GoodsItem></div>
</div>
  1. 商品组件内,动态渲染
<template><div v-if="item.goods_name" class="goods-item" @click="$router.push(`/prodetail/${item.goods_id}`)"><div class="left"><img :src="item.goods_image" alt="" /></div><div class="right"><p class="tit text-ellipsis-2">{{ item.goods_name }}</p><p class="count">已售 {{ item.goods_sales }}件</p><p class="price"><span class="new">¥{{ item.goods_price_min }}</span><span class="old">¥{{ item.goods_price_max }}</span></p></div></div>
</template><script>
export default {props: {item: {type: Object,default: () => {return {}}}}
}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/663817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flutter组件 StatefulWidget、StatelessWidget 可继承写法

前言 学过Java的同学&#xff0c;应该都知道面向对象语言的三大特征&#xff0c;封装、继承、多态&#xff1b; Dart也是面向对象的语言&#xff0c;但是在Flutter中的很多组件都被下划线 _ 标记为私有&#xff0c;导致无法继承&#xff0c;本文将介绍一种非私有的创建组件写…

VUE开发记录

1、VUE模板传递参数到JS方法 <select-language :value"item.language" change"selectLanguage($event, key)"></select-language>selectLanguage(value, key){console.log(value, key) }, 2、Element框架el-form-item自定义label和内容 <…

追觅科技发布全折叠高速吹风机Pocket

2月2日&#xff0c;追觅科技召开2024新品发布会&#xff0c;一系列年度新品亮相。现场&#xff0c;追觅科技发布了个护重磅新品——追觅Pocket折叠高速吹风机&#xff0c;这也是行业首个全折叠高速吹风机。 创新柔性折叠技术&#xff0c;直卷吹一机全能 追觅Pocket折叠高速吹风…

07 SB3之@HttpExchange(TBD)

HttpExchange是SpringBoot3的新特性. Spring Boot3 提供了新的 HTTP 的访问能力&#xff0c;封装了Http底层细节. 通过接口简化 HTTP远程访问&#xff0c;类似 Feign 功能。 SpringBoot 中定义接口提供 HTTP 服务 --> 框架生成的代理对象实现此接口 --> 框架生成的代理…

java处理ppt方案详解

需求 需要系统中展示的ppt案例有一个动态展示的效果&#xff0c;也就是要有动画的交互&#xff0c;要求支持浏览器直接打开预览 背景 目前已经实现了前端上传pptx文件&#xff0c;后端解析为png的图片&#xff0c;前端掉接口返回对应的图片&#xff0c;模拟播放ppt的效果 各种尝…

树莓派Pico W无线开发板蓝牙通信MicroPython编程

内容提要:2023年6月14日,树莓派官方发布了对树莓派Pico W无线开发板(简称Pico W)MicroPython蓝牙功能的支持。本文首先介绍Pico W接口信号及蓝牙通信协议,然后通过Pico W接口信号扩展连接一只LED和一个按键,并给出Pico W蓝牙通信MicroPython编程示例。 一、Pico W接口信号…

华为数通方向HCIP-DataCom H12-821题库(单选题:401-420)

第401题 R1的配置如图所示,此时在R1查看FIB表时,关于目的网段192.168.1.0/24的下跳是以下哪一项? A、10.0.23.3 B、10.0.12.2 C、10.0.23.2 D、10.0.12.1 【答案】A 【答案解析】 该题目考查的是路由的递归查询和 RIB 以及 FIB 的关系。在 RIB 中,静态路由写的是什么,下…

时间序列预测 —— TCN模型

时间序列预测 —— TCN模型 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;在图像处理等领域取得了显著的成就&#xff0c;一般认为在处理时序数据上不如RNN模型&#xff0c;而TCN&#xff08;Temporal Convolutional Network&#xff09;…

十分钟上手vue!

Vue 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0c;Vue 都可以胜任。 一 vue.js的导入及使用 vue安装…

PostgreSQL从小白到高手教程 - 第44讲:pg流复制部署

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第44讲&#…

AJAX-入门

定义 概念&#xff1a;AJAX是浏览器与服务器进行数据通信的技术 使用 1.先使用axios库&#xff0c;与服务器进行数据通信 1&#xff09;基于XMLHttpRequest封装、代码简单、月下载量在14亿次 2&#xff09;Vue、React项目中都会用到axios 2.再学习XMLHttpRequest对象的使用…

高宇辰:打造“π”型人才 | 提升之路系列(七)

导读 为了发挥清华大学多学科优势&#xff0c;搭建跨学科交叉融合平台&#xff0c;创新跨学科交叉培养模式&#xff0c;培养具有大数据思维和应用创新的“π”型人才&#xff0c;由清华大学研究生院、清华大学大数据研究中心及相关院系共同设计组织的“清华大学大数据能力提升项…

ElementUI Form:Upload 上传

ElementUI安装与使用指南 Upload 上传 点击下载learnelementuispringboot项目源码 效果图 el-upload.vue&#xff08;Upload上传&#xff09;页面效果图 el-upload.vue代码 import Vue from vue import VueRouter from vue-router import HomeView from ../views/HomeV…

【论文阅读笔记】Taming Transformers for High-Resolution Image Synthesis

Taming Transformers for High-Resolution Image Synthesis 记录前置知识AbstractIntroductionRelated WorkMethodLearning an Effective Codebook of Image Constituents for Use in TransformersLearning the Composition of Images with Transformers条件合成合成高分辨率图…

Interpolator:在Android中方便使用一些常见的CubicBezier贝塞尔曲线动画效果

说明 方便在Android中使用Interpolator一些常见的CubicBezier贝塞尔曲线动画效果。 示意图如下 import android.view.animation.Interpolator import androidx.core.view.animation.PathInterpolatorCompat/*** 参考* android https://yisibl.github.io/cubic-bezier* 实现常…

个人网站如何让搜索引擎收录

当我们花费功夫搭建好个人网站&#xff0c;如何能让搜索引擎搜索到个人网站呢&#xff1f;比如百度&#xff0c;根本百度不到自己网站的内容。这时候就要使用到搜索引擎提供的站点收录功能了&#xff0c;但是点开百度的搜索资源平台&#xff0c;添加自己的站点时&#xff0c;就…

Java/Python/Go不同开发语言基础数据结构和相关操作总结-Map篇

Java/Python/Go不同开发语言基础数据结构和相关操作总结 1. Java1.1 基础操作1.1.1 数据结构和定义方式1.1.2 增加1.1.3 修改1.1.4 查询1.1.5 删除1.1.6 获取总长度1.1.7 按key排序1.1.8 按value排序1.1.9 遍历 1.2 常用其他方法1.2.1 几种数据结构的对比 2. Go2.1基础操作2.1.…

山东淄博刑侦大队利用无人机抓获盗窃团伙

山东淄博刑侦大队利用无人机抓获盗窃团伙 近期&#xff0c;山东淄博临淄区发生多起盗窃案件。通过视频追踪和调查访问&#xff0c;推断临淄区某村可能为嫌疑人藏匿地点。刑侦大队无人机应急小组迅速到达现场&#xff0c;经无人机高空侦查&#xff0c;发现并锁定了嫌疑人的藏匿…

格式化内存卡后,如何找回丢失的监控视频?

随着摄像头的应用越来越广泛&#xff0c;很多监控摄像头采用了内存卡作为存储介质&#xff0c;方便用户存储和查看摄像头拍摄的视频文件。然而&#xff0c;由于各种原因&#xff0c;监控摄像头的内存卡有时会被意外格式化导致重要数据的丢失&#xff0c;给用户带来诸多困扰。 那…

SpringMVC请求和响应

文章目录 1、请求映射路径2、请求参数3、五种类型参数传递3.1、普通参数3.2、POJO类型参数3.3、嵌套POJO类型参数3.4、数组类型参数3.5、集合类型参数 4、json数据传递4.1、传递json对象4.2、传递json对象数组 5、日期类型参数传递6、响应6.1、响应页面6.2、文本数据6.3、json数…