html制作购买数量,09_03. 使用vuex实现购买数量

1. 项目分析

d75f92069bdb

2. 关键代码:

导入vuex以及使用

d75f92069bdb

配置

d75f92069bdb

d75f92069bdb

然后在单独创建出来的配置文件里面配置

import storage from '../js/storage.js';

export default {

// 定义状态, 相当于组件的data

// 区别是这里的数据共享, 那都可以通过$store.state.属性名的方式使用

state: {

// 从localStorage中取出购买数据进行共享

goodsBuyData: storage.get('goodsBuyData')

},

// 定义计算属性, 相当于组件的computed

getters: {

// 计算购买总数

getBuyTotal(state) {

// 先拿到每个商品数量, 然后使用reduce累加起来

return Object.values(state.goodsBuyData).reduce((sum, v) => sum + v, 0);

}

},

// 定义修改方法, 相当于组件的methods

mutations: {

// 修改单个商品的购买数量

upBuyData(state, params) {

// 拿到id与新数量, 修改goodsBuyData

// 然后把修改后的goodsBuyData存储到本地storage

state.goodsBuyData[params.id] = params.total;

storage.set('goodsBuyData', state.goodsBuyData);

}

}

};

在底部写活

d75f92069bdb

+ 在商品列表里面添加点击事件,使点击加入购物车按钮后底部的同时变化

d75f92069bdb

3. 完整代码块:

src/component/common/footer.vue

首页

商品购买

{{ $store.getters.getBuyTotal }}

购物车

设置

export default {};

i {

display: block;

height: 50px;

}

src/component/common/header.vue

export default {};

header i {

display: block;

height: 40px;

}

src/component/common/index.js

// 编写属于自己的公共Vue组件库

import HeaderComponent from './header.vue';

import FooterComponent from './footer.vue';

import NumboxComponent from './numbox.vue';

// Vue插件要求提供一个install方法, 这个方法会被注入Vue

// 需要我们调用Vue的filter component directive去扩展功能

export default {

install(Vue) {

Vue.component('app-header', HeaderComponent);

Vue.component('app-footer', FooterComponent);

Vue.component('app-numbox', NumboxComponent);

}

};

src/component/common/numbox.vue

-

+

export default {

props: ["initVal"],

data() {

return {

val: this.initVal || 0 // 优先使用父指定的默认值, 父不指定默认为0

};

},

methods: {

// 减减, 最小值为0

sub() {

this.val > 0 && this.val--;

},

// 加加, 最大值不限

add() {

this.val++;

}

},

watch: {

// 值发生变化就通过事件的方式传给父

val() {

this.$emit("change", this.val);

}

}

};

src/component/goods/son/intro.vue

{{ intro.title }}

export default {

props: ["id"],

data() {

return {

intro: {}

};

},

methods: {

// 获取图文介绍, 但是需要由父亲告诉我id

getIntro() {

this.axios

.get(this.api.goodsD + this.id)

.then(rsp => (this.intro = rsp.data.message[0]));

}

},

created() {

this.getIntro();

}

};

src/component/goods/goods_detail.vue

{{ goodsPrice.title }}

市场价:¥{{ goodsPrice.market_price }}

销售价:

¥{{ goodsPrice.sell_price }}

购买数量:

商品评论

图文介绍

内容1

import IntroComponent from "./son/intro.vue";

import storage from "../../js/storage.js";

export default {

data() {

return {

id: this.$route.params.id,

lunbos: [],

goodsPrice: {},

navbarSelector: "commont",

buyCount: (storage.get("goodsBuyData") || {})[this.$route.params.id]

};

},

methods: {

// 获取商品缩略图

getGoodsThumList() {

this.axios

.get(this.api.goodsT + this.id)

.then(rsp => (this.lunbos = rsp.data.message));

},

// 获取商品价格信息

getGoodsPrice() {

this.axios

.get(this.api.goodsP + this.id)

.then(rsp => (this.goodsPrice = rsp.data.message[0]));

},

// 获取最新的购买数量, 并存储起来

getTotal(total) {

this.buyCount = total;

},

// 加入购物车, 调用vuex中提供的修改方法即可

addShopcart() {

this.$store.commit("upBuyData", {

id: this.id,

total: this.buyCount

});

}

},

created() {

this.getGoodsThumList();

this.getGoodsPrice();

},

components: {

"app-intro": IntroComponent

}

};

.goods-detail {

.mui-card-content {

.price {

color: rgb(51, 51, 51);

margin-bottom: 4px;

span {

margin-left: 6px;

}

em {

font-size: 18px;

color: red;

}

}

}

.mui-card-footer {

div {

width: 100%;

}

.mui-btn {

padding: 8px 0;

font-size: 16px;

}

}

.mint-tab-item {

&.is-selected {

margin-bottom: 0;

border-bottom: 3px solid #2ce094;

color: #2ce094;

}

}

.mint-tab-item-label {

font-size: 18px;

color: #2ce094;

}

}

// 给轮播图加个高度

.mint-swipe {

height: 260px;

background-color: white;

img {

display: block;

margin: 0 auto;

max-width: 100%;

height: 260px;

}

}

src/component/goods/goods_list.vue

  • ¥{{ item.sell_price }}

    ¥{{ item.market_price }}

    热卖中

    剩余{{ item.stock_quantity }}件

{{ lastPage? '已经是最后一页了': '加载更多' }}

export default {

data() {

return {

goodsList: [],

pageIndex: 1,

lastPage: false // 是否最后一页

};

},

methods: {

// 获取商品列表, 需要一个pageindex查询字符串, 用来指定页码

getGoodsList() {

this.axios

.get(`${this.api.goodsL}?pageindex=${this.pageIndex}`)

.then(rsp => {

this.goodsList.push(...rsp.data.message);

this.isLastPath(rsp.data.message);

});

},

// 加载下一页数据

loadMore() {

this.pageIndex++;

this.getGoodsList();

},

// 判断是不是最后一页数据, 是的话把lastPage设为ture

isLastPath(goodsList) {

if (goodsList.length == 0) {

this.lastPage = true;

}

}

},

created() {

this.getGoodsList();

}

};

.mui-card {

box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.3);

}

.mui-card-header {

padding: 8px;

/*height: 100px;*/

img {

width: 100%;

height: 100%;

}

}

.mui-card-content {

text-align: center;

.price {

margin-bottom: 4px;

color: #000;

span {

color: red;

}

}

.tip {

overflow: hidden;

padding: 0 8px;

font-size: 12px;

span:first-child {

float: left;

}

span:last-child {

float: right;

}

}

}

src/component/home/home.vue

  • 首页
  • 热点新闻
  • 图片分享
  • 搜索
  • 联系我们
  • 关于我们

export default {

data() {

return {

lunbos: []

};

},

methods: {

// 请求数据, 成功后把数据存储到lunbos中

getLunbo() {

this.axios

.get(this.api.getLunbo)

.then(rep => (this.lunbos = rep.data.message));

}

},

// 组件初始化完毕后, 自动调用接口获取数据渲染轮播图

created() {

this.getLunbo();

}

};

@height: 260px;

article {

height: @height;

img {

height: @height;

}

}

src/component/news/news_detail.vue

{{ newsDetail.title }}

创建时间: {{ newsDetail.add_time | date }}

点击数: {{ newsDetail.click }}

export default {

data() {

return {

newsDetail: {}

};

},

methods: {

// 获取新闻详情, 注意这里接口返回的是一个数组, 我们需要通过下标那新闻详情对象

getNewsDetail() {

this.axios

.get(this.api.getND + this.$route.params.id)

.then(rsp => (this.newsDetail = rsp.data.message[0]));

}

},

created() {

this.getNewsDetail();

}

};

article {

overflow: hidden;

}

.mui-card-header,

.mui-card-footer {

display: block;

}

src/component/news/news_list.vue

  • {{ item.title }}

    创建时间: {{ item.add_time | date }}

    点击数: {{ item.click }}

export default {

data() {

return {

newsList: []

};

},

methods: {

// 获取新闻列表数据

getNewsList() {

this.axios

.get(this.api.getNL)

.then(rsp => (this.newsList = rsp.data.message));

}

},

// 上来就调用接口初始化数据

created() {

this.getNewsList();

}

};

src/component/photo/photo_detail.vue

{{ photoDetail.title }}

创建时间: {{ photoDetail.add_time | date }}

点击数: {{ photoDetail.click }}

export default {

data() {

return {

id: this.$route.params.id,

photoDetail: {},

photoThumList: []

};

},

methods: {

// 获取图片详情

getPhotoDetail() {

this.axios

.get(this.api.photoD + this.id)

.then(rsp => (this.photoDetail = rsp.data.message[0]));

},

// 获取图片缩略图

getphotoThumList() {

this.axios

.get(this.api.photoT + this.id)

.then(rsp => (this.photoThumList = rsp.data.message));

}

},

created() {

this.getPhotoDetail();

this.getphotoThumList();

}

};

.mui-card-header,

.mui-card-footer {

display: block;

}

.mui-card h4 {

font-size: 14px;

}

src/component/photo/photo_list.vue

  • 全部

  • {{ item.title }}

{{ item.title }}

{{ item.zhaiyao }}

export default {

data() {

return {

photoCategoryList: [],

photoList: []

};

},

methods: {

// 获取图片分类列表

getPhotoCategoryList() {

this.axios

.get(this.api.photoC)

.then(rsp => (this.photoCategoryList = rsp.data.message));

},

// 获取图片列表, 需要使用分类ID来获取指定的图片列表

getPhotoList() {

this.axios

.get(this.api.photoL + this.$route.params.id)

.then(rsp => (this.photoList = rsp.data.message));

}

},

// 在组件初始化完毕后执行一次

created() {

this.getPhotoCategoryList();

this.getPhotoList();

},

watch: {

// 监听url的变化, 变化后拿到新的id,

// 调用接口发送请求修改photoList数据

$route() {

this.getPhotoList();

}

}

};

.mui-table-view {

overflow: hidden;

li {

float: left;

color: deepskyblue;

}

}

.mui-card-header,

.mui-card-header {

display: block;

}

src/component/shopcart/shopcart.vue

{{ item.title }}
  • ¥{{ item.sell_price }}
  • 删除

  • 总计(不含运费)
  • 已勾选商品{{ buyTotal }}件,总价:¥品{{ buyPriceTotal }}元

付 款{{ $store.state.buyTotal }}

import storage from "../../js/storage.js";

export default {

data() {

return {

goodsBuyData: storage.get("goodsBuyData"),

buyGoodsList: []

};

},

methods: {

// 获取购物车列表数据

getBuyGoodsList() {

let ids = Object.keys(storage.get("goodsBuyData")).join(",");

this.axios.get(this.api.shopcL + ids).then(rsp => {

// 给每个商品补充一个isSelected属性, 默认值为true

rsp.data.message.forEach(goods => (goods.isSelected = true));

this.buyGoodsList = rsp.data.message;

});

},

// 修改购买数据

modifyBuyData(id, val) {

this.goodsBuyData[id] = val;

console.log(this.goodsBuyData);

},

// 删除商品

delGoods(id) {

this.$delete(this.goodsBuyData, id);

this.buyGoodsList = this.buyGoodsList.filter(v => v.id != id);

}

},

created() {

this.getBuyGoodsList();

},

computed: {

// 总数

buyTotal() {

return this.buyGoodsList.reduce((sum, goods) => {

// reduce方法每次把上一次的sum结果传递进来, 供我们继续累加,

// 如果商品为选中状态我们就累加, 否则原物传递到下一次计算

return goods.isSelected ? sum + this.goodsBuyData[goods.id] : sum;

}, 0);

},

// 总价

buyPriceTotal() {

return this.buyGoodsList.reduce((sum, goods) => {

// reduce方法每次把上一次的sum结果传递进来, 供我们继续累加,

// 如果商品为选中状态我们就累加, 否则原物传递到下一次计算

return goods.isSelected

? sum + this.goodsBuyData[goods.id] * goods.sell_price

: sum;

}, 0);

}

},

watch: {

// 监听商品数量的变化, 实时存储的本地storage

goodsBuyData: {

handler() {

storage.set("goodsBuyData", this.goodsBuyData);

},

// 深度监听对象的变化, 这样vue每次会比较子属性的值

deep: true

}

}

};

.shopcart-list {

.goods {

border-bottom: 1px solid rgba(0, 0, 0, 0.3);

height: 102px;

display: flex;

padding: 5px;

.switch {

flex: 0 0 52px;

}

.img {

margin-left: 5px;

height: 75px;

width: 75px;

flex: 0 0 85px;

}

.inforight {

margin-left: 5px;

flex: 1;

}

.inforight ul {

padding-left: 0px;

}

.inforight li {

list-style: none;

display: inline-block;

}

.inforight h4 {

color: #0094ff;

font-size: 14px;

}

.bottom li:first-child {

color: red;

margin-right: 5px;

}

.bottom li:last-child {

margin-left: 5px;

}

}

.total {

height: 84px;

background-color: rgba(0, 0, 0, 0.1);

display: flex;

padding: 5px 14px;

ul {

padding-left: 0px;

margin: 14px 0;

li {

list-style: none;

font-size: 14px;

}

}

&_val {

flex: 1;

}

&_btn {

flex: 0 0 60px;

margin: 18px 0 0 0;

}

}

}

src/component/App.vue

export default {};

src/component/filter/date.js

export default function(time) {

let date = new Date(time);

return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;

};

src/component/filter/index.js

import DateFilter from './date.js';

export default {

install(Vue) {

Vue.filter('date', DateFilter);

}

};

src/component/js/api_config.js

const domain = 'http://vue.studyit.io/api';

export default {

// 获取轮播图的接口

getLunbo: `${domain}/getlunbo`,

// 新闻相关接口

getNL: `${domain}/getnewslist`,

getND: `${domain}/getnew/`, // 该接口后面需要一个id

// 图片相关接口

photoC: `${domain}/getimgcategory/`,

photoL: `${domain}/getimages/`, // 该接口后面需要一个分类id: /getimages/:id

photoD: `${domain}/getimageinfo/`, // 该接口后面需要一个图片id: /getimageinfo/:id

photoT: `${domain}/getthumimages/`, // 该接口后面需要一个图片id: /getthumimages/:id

// 商品相关接口

goodsL: `${domain}/getgoods/`, // 该接口后面需要一个页码: /getgoods/?pageindex=number

goodsD: `${domain}/goods/getdesc/`, // 该接口后面需要一个商品id: /getdesc/:id

goodsT: `${domain}/getthumimages/`, // 该接口后面需要一个商品id: /getthumimages/:id

goodsP: `${domain}/goods/getinfo/`, // 该接口后面需要一个商品id: /getinfo/:id

// 购物车相关接口

shopcL: `${domain}/goods/getshopcarlist/`, // 该接口后面需要一串id: /getshopcarlist/:ids

// 评论相关接口

commentL: `${domain}/getcomments/`, // 该接口后面需要一个id: /getcomments/:id

commentS: `${domain}/postcomment/`, // 该接口后面需要一个id: /postcomment/:id, 该需要content内容

};

src/component/js/main.js

// from后面的路径, 如果含有./ ../那么就相对于当前文件找文件

// 如果没有, 那么就会去node_modules里面找对应的包

// 1.1 导入第三方包

import Vue from 'vue';

import MintUi from 'mint-ui';

import 'mint-ui/lib/style.css';

import Common from '../component/common'; // 自动找到index.js引入

import 'mui/dist/css/mui.css';

import 'mui/examples/hello-mui/css/icons-extra.css';

import axios from 'axios';

import VueRouter from 'vue-router';

import Filter from '../filter' // 自动找到index.js引入

import '../less/index.less';

import VuePP from 'vue-picture-preview';

import Vuex from 'vuex';

// 1.2 启用vue插件

Vue.use(MintUi);

Vue.use(Common);

Vue.use(VueRouter);

Vue.use(Filter);

Vue.use(VuePP);

Vue.use(Vuex);

// 2.1 导入配置

import routerConfig from '../router' // 自动找到index.js引入

import apiConfig from './api_config.js'

// 2.2 扩展实例成员

Vue.prototype.axios = axios; // 把axios库放置到原型, 将来其他组件直接可以拿到axios对象

Vue.prototype.api = apiConfig;

// 2.3 导入根组件

import AppComponent from '../component/App.vue';

// 2.4 渲染根组件, 启动项目

new Vue({

el: '#app',

render(createNode) {

return createNode(AppComponent);

},

router: new VueRouter(routerConfig),

store: new Vuex.Store({

// 这个配置项就相当于咱们的data选项

state: {

buyTotal: 100

},

// 这个配置想就相当于咱们的methods选项, 用于定义一些方法,

// 但是这里的方法作用很明确就是为了修改状态值

mutations: {

// 这个方法的第一个参数为vuex自动传入, 即state状态

// 这个方法的第二个参数为用户传入

// 注意: 千万不要通过$store.state.buyTotal = newTotal这样方式修改值

// 因为这样vuex无法捕获到数据的变化, 同时这个数据是共享的, 可能在任意地方被修改不利于维护

// 如果调用这里方法修改数据, 不会混乱, 因为修改规则我都提取设计好了

setBuyTotal(state, newTotal) {

state.buyTotal = newTotal;

}

}

})

});

src/component/less/index.less

export default {

// localStorage.setItem的封装

set(key, val) {

localStorage.setItem(key, JSON.stringify(val));

},

// localStorage.getItem的封装

get(key) {

let val = localStorage.getItem(key);

// 先尝试着解析数据, 成功了就返回解析后的值, 不成功就原物返回

try {

val = JSON.parse(val);

} finally {

return val;

}

},

// 清除所有本地存储的数据

clear() {

localStorage.clear();

}

};

src/component/less/index.less

.mui-card-footer img {

width: 100%;

}

.mui-card img {

width: 100%;

height: auto;

}

src/component/router/index.js

// 这里对外导出一个路由配置对象

import HomeComponent from '../component/home/home.vue';

import NewsListComponent from '../component/news/news_list.vue';

import NewsDetailComponent from '../component/news/news_detail.vue';

import PhotoListComponent from '../component/photo/photo_list.vue';

import PhotoDetailComponent from '../component/photo/photo_detail.vue';

import GoodsListComponent from '../component/goods/goods_list.vue';

import GoodsDetailComponent from '../component/goods/goods_detail.vue';

import ShopcartComponent from '../component/shopcart/shopcart.vue';

export default {

linkActiveClass: 'mui-active',

routes: [

// 首页路由配置

{ path: "/", redirect: "/index" },

{ name: "i", path: "/index", component: HomeComponent },

// 新闻路由配置

{ name: "nl", path: "/news/list", component: NewsListComponent },

{ name: "nd", path: "/news/detail/:id", component: NewsDetailComponent },

// 图片分享相关路由

{ name: "pl", path: '/photo/list/:id', component: PhotoListComponent },

{ name: "pd", path: '/photo/details/:id', component: PhotoDetailComponent },

// 商品相关路由

{ name: "gl", path: '/goods/list/', component: GoodsListComponent },

{ name: "gd", path: '/goods/detail/:id', component: GoodsDetailComponent },

// 购物车相关路由

{ name: "sc", path: '/shopcart', component: ShopcartComponent },

]

};

src/component/vuex/index.js

import storage from '../js/storage.js';

export default {

// 定义状态, 相当于组件的data

// 区别是这里的数据共享, 那都可以通过$store.state.属性名的方式使用

state: {

// 从localStorage中取出购买数据进行共享

goodsBuyData: storage.get('goodsBuyData')

},

// 定义计算属性, 相当于组件的computed

getters: {

// 计算购买总数

getBuyTotal(state) {

// 先拿到每个商品数量, 然后使用reduce累加起来

return Object.values(state.goodsBuyData).reduce((sum, v) => sum + v, 0);

}

},

// 定义修改方法, 相当于组件的methods

mutations: {

// 修改单个商品的购买数量

upBuyData(state, params) {

// 拿到id与新数量, 修改goodsBuyData

// 然后把修改后的goodsBuyData存储到本地storage

state.goodsBuyData[params.id] = params.total;

storage.set('goodsBuyData', state.goodsBuyData);

}

}

};

src/index.html

Document

.babelrc

{

"presets": [ "env" ],

"plugins":["transform-runtime"]

}

gitignore

# 忽略第三方包, 他们已经记录在package.json文件中了

/node_modules

# 忽略打包后的文件, 因为我们的项目核心是源代码

/dist

# 忽略隐藏文件

.*

# 不忽略git配置文件和babel配置文件

!.gitignore

!.babelrc

webpack.config.js

const path = require('path');

const HtmlWP = require('html-webpack-plugin');

const CleanWP = require('clean-webpack-plugin');

module.exports = {

// 打包的入口文件

entry: path.resolve(__dirname, './src/js/main.js'),

// 输出

output: {

path: path.resolve(__dirname, './dist'),

filename: 'bundle.js'

},

// 插件配置

plugins: [

new HtmlWP({

template: './src/index.html',

filename: 'index.html',

inject: 'body'

}),

new CleanWP(['dist'])

],

// 模块配置

module: {

// 配置loader规则

rules: [

// css

{

test: /\.css$/,

use: ['style-loader', 'css-loader']

},

// less

{

test: /\.less$/,

use: ['style-loader', 'css-loader', 'less-loader']

},

// html

{

test: /\.(html|tpl)$/,

use: ['html-loader']

},

// 静态资源引用

{

test: /\.(png|jpeg|gif|jpg|svg|mp3|ttf)$/,

use: [

{ loader: 'url-loader', options: { limit: 10240 } } // 小于10KB的打包

]

},

// js

{

test: /\.js$/,

use: ['babel-loader'],

exclude: path.resolve(__dirname, '../node_modules')

},

// vue

{

test: /\.vue$/,

use: ['vue-loader']

}

]

}

};

4. 执行webpack-dev-server:

webpack-dev-server

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值