美文网首页
007 一个在线商店例子

007 一个在线商店例子

作者: 逸章 | 来源:发表于2019-09-30 11:43 被阅读0次

一、第一阶段

效果图: image.png

1. 图片讲解

image.png
image.png
image.png
image.png
image.png

2. 代码如下

store---index.js

import Vue from "vue";
import Vuex from "vuex";

//Vuex is distributed as a Vue.js plugin. Plugins must be enabled using the Vue.use method
Vue.use(Vuex);

const testData = [];
for (let i = 1; i <= 10; i++) {
    testData.push({
        id: i, name: `Product #${i}`, category: `Category ${i % 3}`,
        description: `This is Product #${i}`, price: i * 50
    })
}

//create a Vuex.Store object, passing a configuration object
/*
vuex的state和vue的data有很多相似之处,都是用于存储一些数据,或者说状态值.这些值都将被挂载.
state在使用的时候一般被挂载到子组件的computed计算属性上,这样有利于state的值发生改变的时候及时响应给子组件

*/
export default new Vuex.Store({
    //strict mode is a useful feature that generates a warning if you forget to use 
    //a mutation and modify data values directly
    strict: true,
    state: {
        products: testData,
        //用于分页
        productsTotal: testData.length,
        currentPage: 1,
        pageSize: 4,
        currentCategory: "All"
    },
    getters: {
        /*processedProducts: state => {
            let index = (state.currentPage -1) * state.pageSize;
            return state.products.slice(index, index + state.pageSize);
        },
        pageCount: state => Math.ceil(state.productsTotal / state.pageSize)*/

        productsFilteredByCategory: state => state.products.filter(p => state.currentCategory == "All"
            || p.category == state.currentCategory),
        processedProducts: (state, getters) => {
            let currentPageStartIndex = (state.currentPage -1) * state.pageSize;
            return getters.productsFilteredByCategory
            .slice(currentPageStartIndex, currentPageStartIndex + state.pageSize);
            },
        pageCount: (state, getters) =>
            Math.ceil(getters.productsFilteredByCategory.length / state.pageSize),
        categories: state => ["All",
            /*which has the effect of removing any duplicates.The Set is spread into an array,
which is sorted, resulting in an array of distinct categories, sorted by name
            */
            ...new Set(state.products.map(p => p.category).sort())]
    },
    /*
    The separation between standard data properties and computed properties is a theme that runs
through Vue.js development because it allows for efficient change-detection. When a data property changes,
Vue.js is able to determine the effect on the computed property and doesn’t have to recalculate values
when the underlying data has not changed

    */
    mutations: {
/* both the getters and the mutations are defined as functions that receive a state object as their first parameter.  */
        setCurrentPage(state, page) {
            state.currentPage = page;
        },
        setPageSize(state, size) {
            state.pageSize = size;
            state.currentPage = 1;
        },
        setCurrentCategory(state, category) {
            state.currentCategory = category;
            state.currentPage = 1;
        }
    }
})

components---CategoryControls.vue

<template>
    <div class="container-fluid">
        <div class="row my-2" v-for="c in categories" v-bind:key="c">
            <button class="btn btn-block"
                v-on:click="setCurrentCategory(c)"
                v-bind:class="c == setCurrentCategory ? 'btn-primary' : 'btn-secondary'">
                {{ c }}
            </button>
        </div>
    </div>
</template>

<script>
    //import { mapState, mapGetters, mapMutations} from "vuex";
    import { mapGetters, mapMutations} from "vuex";
    export default {
        computed: {
            //...mapState(["currentCategory"]),
            ...mapGetters(["categories"])
        },
        methods: {
            ...mapMutations(["setCurrentCategory"])
        }
    }
</script>

components---PageControls.vue

<template>
    <div class="row mt-2">
        <div class="col form-group">
<!--If you just specify the name of a method when using the v-on directive, the methods will receive an event object, which can be used to access details of the element that triggered the event.-->
            <select class="form-control" v-on:change="changePageSize">
                <option value="4">4 per page</option>
                <option value="8">8 per page</option>
                <option value="12">12 per page</option>
            </select>
        </div>
        <div class="text-right col">
            <div class="btn-group mx-2">
                <button v-for="i in pageNumbers" v-bind:key="i"
                        class="btn btn-secpmdary"
                        v-bind:class="{ 'btn-primary': i == currentPage }"
                        v-on:click="setCurrentPage(i)">

                {{ i }}
                </button>
            </div>
        </div>
    </div>
</template>
<script>    
    import { mapState, mapGetters, mapMutations } from "vuex";
    export default {
        computed: {
            /*
This component uses the mapState and mapGetters helper functions to provide access to the data store currentPage and pageCount properties
            */
            ...mapState(["currentPage"]),
            ...mapGetters(["pageCount"]),
            pageNumbers() {
                //keys()用于从数组中创建一个可迭代的对象,该对象包含数组的键(即位置下标0 1 2...)
                return [...Array(this.pageCount + 1).keys()].slice(1);
            }
        },
        methods: {
            /*The setCurrentPage is mapped to the data store mutation of the same name*/
            //...mapMutations(["setCurrentPage"]),
            ...mapMutations(["setCurrentPage", "setPageSize"]),
            changePageSize($event) {
                this.setPageSize(Number($event.target.value));
            }
        }
    }
    
</script>

components---ProductList.vue

<template>
    <div>
        <div v-for="p in products" v-bind:key="p.id" class="card m-1 p-1 bg-light">
            <h4>
                <!--data binding-->
                {{p.name}}
                <span class="badge badge-pill badge-primary float-right">
                    {{ p.price | currency }}<!--currency是一个自定义的filter,used to format data values-->
                </span>
            </h4>
            <div class="card-text bg-white p-1">{{ p.description }}</div>
        </div>
        <page-controls />
    </div>
</template>

<script>    
    import { mapGetters} from "vuex";
    import PageControls from "./PageControls";

    export default {
        components: { PageControls },
        //the computed property, which is providing access to the data in the data store
        computed: {
            /*
1. mapState function is used with the spread operator because it can map multiple data store properties in a single operation, even though only the products state property is mapped in this example.

2. The result of using the mapState function is that I can use the products property with the v-for directive to access the data in the data store
            */
            //...mapState(["products"])//Using the Spread Operator
            /*
注意下面这个写法
allows me to specify that the processedProducts getter will be mapped using the name products
            */
            ...mapGetters({ products: "processedProducts" })
        },
        //the filters property, which is used to define filters
        filters: {
            currency(value) {
                /*
new Intl.NumberFormat([locales[, options]])
options 可以包含下面的属性:
    1. style
        The formatting style to use. Possible values are "decimal" for plain number formatting, "currency" for currency formatting, and "percent" for percent formatting; the default is "decimal".
    2. currency
        The currency to use in currency formatting. Possible values are the ISO 4217 currency codes, such as "USD" for the US dollar, "EUR" for the euro, or "CNY" for the Chinese RMB — see the Current currency & funds code list. There is no default value; if the style is "currency", the currency property must be provided.
                */

            return new Intl.NumberFormat("zh-CN",{ style: "currency", currency: "CNY" })
                .format(value);
            /*return new Intl.NumberFormat("en-US",{ style: "currency", currency: "USD" })
                .format(value);*/
            }
        }
    }
</script>

components---Store.vue

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col bg-dark text-white">
                <a class="navbar-brand">SPORTS STORE</a>
            </div>
        </div>
        <div class="row">
            <div class="col-3 bg-info p-2">
                <CategoryControls />
            </div>

            <div class="col-9 p-2 ">
                <!--I could also have used ProductList or productList-->
                <!--形成了父子关系,组件ProductList是子,Store是父-->
                <product-list />
                <!--ProductList />
                <productList /-->
            </div>
        </div>
    </div>
</template>

<script>
    import ProductList from "./ProductList";
    import CategoryControls from "./CategoryControls";
    export default {
        components: { ProductList, CategoryControls }
    }
</script>

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

import "bootstrap/dist/css/bootstrap.min.css";
import "font-awesome/css/font-awesome.min.css";

import store from "./store";

new Vue({
  render: h => h(App),
  //Adding the store property to the configuration property used to create the Vue object
  //ensures that the data store features can be used throughout the application
  store
}).$mount('#app')

App.view

<template>
  <store />
</template>

<script>
  import Store from "./components/Store";
  export default {
    name: 'app',
    components: { Store }
  }
</script>

3. 改用RESTful web service提供数据

上面给的例子都是写死的,我们这里改用resuful web service方式提供数据。 修改的地方和说明如下:


image.png
image.png
image.png

index.js中要修改的地方:


image.png
image.png
image.png
App.vue需要修改的地方:
image.png

二、第二阶段

增加 购物车结账 功能

image.png
image.png
image.png
image.png

完整代码见https://github.com/yinbodotcc/provuejs2.git

1. 看各个component之间层次或者路由关系

image.png

2. 函数是怎么实现共享出去的

2.1各个函数从module里面导出的原理

image.png

2.2 modules property生产访问限定符用法的说明

image.png

3. 怎么路由

image.png

三、第三阶段

1. 当数据量很大时候的PageControl

这是Amazon常用的分页控制方法

image.png image.png image.png image.png image.png

实现关键点解释:


image.png
image.png

2. 增加商品过滤功能

image.png
核心代码解释如下: image.png

3. 管理员界面

3.1 必须登录才可以进入管理员界面(禁止直接访问/admin,即Admin.vue提供的功能需要认证)

image.png
image.png
image.png

第四阶段 Docker部署

使用https://github.com/yinbodotcc/provuejs2.git里面sportsstore可以用docker部署.7z

注意:我的UBUNTU上只有docker,没有npm等vue需要的环境

image.png

Dockerfile文件为:

FROM node:8.11.2

RUN mkdir -p /usr/src/sportsstore

COPY dist /usr/src/sportsstore/dist

COPY authMiddleware.js /usr/src/sportsstore/

COPY data.json /usr/src/sportsstore/

COPY server.js /usr/src/sportsstore/server.js

COPY deploy-package.json /usr/src/sportsstore/package.json

WORKDIR /usr/src/sportsstore

RUN npm config set registry https://registry.npm.taobao.org

RUN npm install

EXPOSE 80

CMD ["node", "server.js"]
image.png
image.png
image.png
image.png
image.png

相关文章

网友评论

      本文标题:007 一个在线商店例子

      本文链接:https://www.haomeiwen.com/subject/clvcpctx.html