Vue.js结合element-ui(电商项目&&考试问卷)
一、项目结构二、基础配置(引入js框架\样式\vuex\router\axios等):和上一个博客中的一样,不同的是需要多一个element-ui配置:(1)、安装npm install element-ui -S(2)、main.js里面引入:import ElementUI from 'element-ui';import 'element-ui/lib/theme-chal...
·
一、项目结构
二、基础配置(引入js框架\样式\vuex\router\axios等):
和上一个博客中的一样,不同的是需要多一个element-ui配置:
(1)、安装npm install element-ui -S
(2)、main.js里面引入:
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
三、各个文件:
(1)、src\vuex
<1>:modules.js
import Vue from 'vue';
import Vuex from 'Vuex';
Vue.use(Vuex);
import shopStore from './shopStore/index.js';
import questionStore from './questionStore/index.js';
import createPersistedState from "vuex-persistedstate"
// vuex-persistedstate --save
export default new Vuex.Store({
modules:{
shopStore:shopStore,
questionStore:questionStore
},
plugins: [createPersistedState({
storage: window.sessionStorage
})]
})
<2>、src\vuex\questionStore\index.js:
//声明数据
const state={
questionList:[
{id:1,question:'你的性别是?',modelVal:'sex',type:'radio',answer:['男','女','保密']},
{id:2,question:'你的爱好是?',modelVal:'hobby',type:'checkbox',answer:['游泳','篮球','足球']},
{id:3,question:'请填写自我介绍!',modelVal:'remarks',type:'textarea',answer:''}
],
currentPage:1
};
const mutations={
setCurrentPage(state,data){
state.currentPage+=data;
}
};
export default {
state,
mutations
}
<3>、src\vuex\shopStore\index.js:
import productData from '../../../static/product.js';
//声明取出重复方法
function getFilterArray(array) {
const res= [];
const json = {};
array.forEach((item,index)=>{
const _self=item;
if (!json[_self]) {
res.push(_self);
json[_self]=1
}
})
return res;
};
//声明数据
const state={
productList:[],
carList:[]
};
//声明数据赋值方法
const mutations={
setAllProduct(state,data){
state.productList=data;
},
addCar(state,data){
let val=state.carList.find((item)=>item.id==data);
if (val!=undefined){
val.count++;
}else {
state.carList.push({
id:data,
count:1
})
}
},
restCar(state,data){
data.forEach((item)=>{
let index=state.carList.findIndex((val)=>item.id==val.id);
state.carList.splice(index,1);
});
}
};
//声明数据逻辑方法
const actions={
getAllProduct(concent){
setTimeout(()=>{
concent.commit('setAllProduct',productData);
},50);
}
};
//声明视图
const getters={
colorAll:(state)=>{
let color=state.productList.map((item)=>item.color);
return getFilterArray(color);
},
brandAll:(state)=>{
let brand=state.productList.map((item)=>item.brand);
return getFilterArray(brand);
}
};
export default {
state,
mutations,
actions,
getters
}
(2)、src\router
<1>:src\router\index.js:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
//配置路由路径
const RoutersPath=[
{
path: '*',
meta:{
title:'总列表'
},
redirect: '/productList'
},
{
path: '/productList',
name:'productList',
meta:{
title:'商品列表',
show:true
},
component:(resolve)=>require(['./view/productList.vue'],resolve)
},{
path: '/carList',
name:'carList',
meta:{
title:'购物车',
show:true
},
component:(resolve)=>require(['./view/carList.vue'],resolve)
},{
path: '/productInfo',
name:'productInfo',
meta:{
title:'商品详情'
},
component:(resolve)=>require(['./view/productInfo.vue'],resolve)
},{
path: '/question',
name:'question',
meta:{
title:'问卷调查',
show:true
},
component:(resolve)=>require(['./view/question.vue'],resolve)
}
]
//配置路由属性
const routerConfig={
routes:RoutersPath,
mode:'history',
base:__dirname
}
//实例化一个router对象
const router=new Router(routerConfig);
//声明钩子函数
router.beforeEach((to,from,next)=>{
window.document.title=to.meta.title;
next();
});
router.afterEach((to,from,next)=>{
window.scroll(0,0);
});
export default router;
<2>:src\router\view\carList.vue:
<template>
<div class="carList-main">
<el-row type="flex" justify="start">
<el-col :span="24">
<div class="carList-main-containerCls">
<el-select clearable filterable size="small" v-model="nowSelect" placeholder="请选择">
<el-option v-for="item in dictList" :key="item.id" :label="item.name" :value="item.name"></el-option>
</el-select>
<el-button type="primary" @click="search" size="small" icon="el-icon-search">搜索</el-button>
</div>
</el-col>
</el-row>
<el-row type="flex" justify="center">
<el-col :span="24">
<template v-if="carlist.length!=0">
<el-table :data="dictList" style="width: 100%" stripe border @selection-change="handleSelectionChange" show-summary sum-text="合计">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column type="index"></el-table-column>
<el-table-column align="center" label="商品" prop="name">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top">
<p>名称: {{ scope.row.name }}</p>
<p>价格: {{ scope.row.sales }}</p>
<div slot="reference" class="name-wrapper">
<el-tag size="medium">{{ scope.row.name }}</el-tag>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column align="center" label="品牌" prop="brand"></el-table-column>
<el-table-column align="center" label="价格" prop="sales" sortable></el-table-column>
<el-table-column align="center" label="数量" prop="count" sortable>
<template slot-scope="scope">
<div slot="reference" class="name-wrapper">
<el-input type="number" clearable max=100 min=0 v-model.lazy.number="scope.row.count" placeholder="请输入数量"></el-input>
</div>
</template>
</el-table-column>
<el-table-column align="center" label="总价" prop="total" sortable></el-table-column>
</el-table>
</template>
<template v-else>
<el-alert title="购物车暂时没有记录!"type="warning"></el-alert>
</template>
</el-col>
</el-row>
<el-row type="flex" :gutter="20" justify="start" style="margin: 15px">
<el-col :span="6">
<el-input v-model="input" size="small" placeholder="请输入优惠码"></el-input>
</el-col>
<el-col :span="4">
<el-button type="primary" size="small" @click="handleYouhui">使用优惠码</el-button>
</el-col>
<el-col :span="3">
<el-button type="primary" size="small" @click="shop">结算</el-button>
<el-dialog title="提示" :visible.sync="dialogVisible" width="30%">
<span>您购买的商品是:</span>
<div v-for="multipitem in nowMultipleSelection">{{multipitem.name}}</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
</el-col>
<el-col :span="9"></el-col>
<el-col :span="6" >
<div class="carList-total">
<div style="color: #e38d13">当前商品原价:{{total}}</div>
<div style="color: #e38d13">当前优惠价:{{youhui}}</div>
<div style="color: #e38d13">优惠后价格:{{total-youhui}}</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "carList",
data(){
return{
carlist:this.$store.state.shopStore.carList,
productList:this.$store.state.shopStore.productList,
multipleSelection: [],
nowMultipleSelection: [],
input:'',
youhui:0,
dialogVisible:false,
maxNum:5,
minNum:0,
nowSelect:'',
fiflerSelect:''
}
},
computed:{
dictList(){
var _this=this;
var dict={};
this.productList.forEach((item)=>{
dict[item.id]=item;
});
var dictCar=this.carlist.map((item)=>{
item.name=dict[item.id].name;
item.brand=dict[item.id].brand;
item.image=dict[item.id].image;
item.sales=dict[item.id].sales;
item.color=dict[item.id].color;
item.total=dict[item.id].sales*item.count;
return item;
});
if (_this.fiflerSelect!=''){
dictCar=dictCar.filter((item)=>item.name==_this.fiflerSelect);
}
return dictCar;
},
total(){
var total=0;
this.multipleSelection.filter((item)=>{
total+=item.sales*item.count
});
return total;
}
},
methods:{
handleSelectionChange(val){
this.multipleSelection = val;
},
shop(){
var _this=this;
this.$confirm('此操作将购买选中的商品, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
_this.$store.commit('restCar',this.multipleSelection);
_this.dialogVisible=true;
_this.nowMultipleSelection=_this.multipleSelection;
_this.multipleSelection=[];
}).catch(() => {
this.$message({
type: 'info',
message: '已取消购买'
});
});
},
handleYouhui(){
if (this.total<=0){
this.$message({
message: '请先购买商品!',
type: 'warning',
duration:1000
});
return false;
}
if (this.input!='vue.js'){
this.$message({
message: '优惠码错误',
type: 'warning',
duration:500
});
this.input='';
}else{
if (this.youhui>0){
this.$message({
message: '已经优惠过了,不能再使用!',
type: 'warning',
duration:500
});
return false;
}
this.youhui=500;
}
},
search(){
this.fiflerSelect=this.nowSelect;
}
},
watch:{
dictList:{
handler(val){
var _this=this;;
val.forEach((item)=>{
if (item.count>_this.maxNum){
item.count=_this.maxNum
this.$message({
message: '超出范围,最大值为5!',
type: 'warning',
duration:1000
});
};
if (item.count<_this.minNum){
item.count=_this.minNum
this.$message({
message: '超出范围,最小值为0!',
type: 'warning',
duration:1000
});
};
});
},deep:true
}
}
}
</script>
<style scoped>
.carList-total{
margin: 10px auto;
text-align: right;
vertical-align: middle;
}
.carList-span{
margin: 10px;
text-align: left;
}
.carList-main-containerCls{
margin: 10px auto;
}
</style>
<3>:src\router\view\productInfo.vue:
<template>
<div class="productInfo-main">
<el-row type="flex" justify="center" align="middle">
<el-col :span="3"></el-col>
<el-col :span="18">
<el-container>
<el-header>
<el-alert title="商品详情" center type="success" :closable="false"></el-alert>
</el-header>
<el-main>
<el-container>
<el-aside>
<img :src="require('@/assets/img/'+productInfoData.image)"></img>
</el-aside>
<el-main>
<div class="productInfo-main-item"><span>名称:{{productInfoData.name}}</span></div>
<div class="productInfo-main-item"><span>品牌{{productInfoData.brand}}</span></div>
<div class="productInfo-main-item"><span>价格:{{productInfoData.sales}}</span></div>
<div class="productInfo-main-item"><el-button @click="addCar(productInfoData.id)" type="warning" round>go</el-button></div>
</el-main>
</el-container>
</el-main>
</el-container>
</el-col>
<el-col :span="3"></el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "productInfo",
data(){
return{
productData:this.$store.state.shopStore.productList,
id:this.$route.params.id
}
},
computed:{
productInfoData(){
var _this=this;
return this.productData.find((item)=>item.id==_this.id);
}
},
methods:{
addCar(id){
this.$store.commit('addCar',id);
this.$message({
message: '购买成功!',
type: 'success',
duration:500
});
}
}
}
</script>
<style scoped>
.productInfo-main-item{
text-align: center;
margin: 5px auto;
}
</style>
<4>:src\router\view\productList.vue:
<template>
<div class="productList-main">
<div class="shopList-conterll-item">
<div class="shopList-item">
<el-tag>排序:</el-tag>
<el-tag type="info" :class="[{itemClsAct:nowSort.indexOf('sales')>-1},'itemCls']" @click="handleSort('sales')">价格<span v-if="nowSort.indexOf('ASC')>-1">↓</span><span v-else>↑</span></el-tag>
<el-tag type="info" :class="[{itemClsAct:nowSort.indexOf('cost')>-1},'itemCls']" @click="handleSort('cost')">销量<span v-if="nowSort.indexOf('ASC')>-1">↓</span><span v-else>↑</span></el-tag>
</div>
<div class="shopList-item">
<el-tag>颜色:</el-tag>
<el-tag :class="[{itemClsAct:nowColor.indexOf(item)>-1},'itemCls']" type="info" @click="handleClick(item)" name="item" :key="item" v-for="item in colorAll">{{item}}</el-tag>
</div>
<div class="shopList-item">
<el-tag>品牌:</el-tag>
<el-tag :class="[{itemClsAct:nowBrand==item},'itemCls']" type="info" @click="handleBrandClick(item)" name="item" :key="item" v-for="item in brandAll">{{item}}</el-tag>
</div>
</div>
<div class="shopList-product">
<product v-for="item in fiferList" :key="item.id" :item="item"></product>
</div>
</div>
</template>
<script>
import product from '../../components/product.vue';
export default {
name: "productList",
components:{
product
},
data(){
return{
nowColor:[],
nowBrand:'',
nowSort:''
}
},
computed:{
productList(){
return this.$store.state.shopStore.productList
},
colorAll(){
return this.$store.getters.colorAll;
},
brandAll(){
return this.$store.getters.brandAll;
},
fiferList(){
let list=[...this.productList];
//按颜色筛选
if(this.nowColor.length!=0){
list=list.filter((item)=>{
var obj=this.nowColor.find((value)=>value==item.color);
if (obj!=undefined) return item;
})
}
//按品牌筛选
if (this.nowBrand!=''){
list=list.filter((item)=>item.brand==this.nowBrand);
}
//排序
if (this.nowSort!=''){
//按价格排序
if (this.nowSort.indexOf('sales')>-1){
list=list.sort((a,b)=>{
return this.nowSort.indexOf("ASC")>-1?a.sales-b.sales:b.sales-a.sales;
});
}else {
//按销量排序
list=list.sort((a,b)=>{
return this.nowSort.indexOf("ASC")>-1?a.cost-b.cost:b.cost-a.cost;
});
}
}
return list;
}
},
methods:{
getAllProduct(){
this.$store.dispatch('getAllProduct');
},
handleClick(itemVal){
var findIndex=this.nowColor.findIndex((item)=>item==itemVal);
if (findIndex>-1){
this.nowColor.splice(findIndex,1);
}else{
this.nowColor.push(itemVal);
}
},
handleBrandClick(itemVal){
this.nowBrand=this.nowBrand==itemVal?'':itemVal;
},
handleSort(type){
//如果是按价格排序
if (type=='sales') {
this.nowSort=this.nowSort==''?'salesASC':(this.nowSort=='salesASC'?'salesDESC':'salesASC');
}
//如果是按销量排序
if (type=='cost') {
this.nowSort=this.nowSort==''?'costASC':(this.nowSort=='costASC'?'costDESC':'costASC');
}
}
},
created(){
this.getAllProduct();
}
}
</script>
<style scoped>
.itemCls{
cursor: pointer;
margin: 0px 2px;
}
.itemClsAct{
background-color: #ffbd6c;
}
.shopList-item{
margin: 5px auto;
display: flex;
flex-flow:row wrap;
justify-content: start;
align-items:center;
}
.shopList-product{
display: flex;
flex-flow:row wrap;
justify-content: start;
align-items:center;
}
</style>
<5>:src\router\view\question.vue:
<template>
<div class="question-main">
<el-container>
<el-header>
<span>调查问卷</span>
</el-header>
<el-main>
<div class="question-main-slot">
<questionComp>
<template slot="questionSlot" slot-scope="props">
<div class="question-main-slot-quesnaire" v-show="props.questionItem.id==currentPage">
<div class="question-main-slot-quesnaire-title">
<span>{{props.questionItem.id}}.{{props.questionItem.question}}</span>
</div>
<div class="question-main-slot-quesnaire-answer" v-if="props.questionItem.modelVal=='sex'">
<el-radio-group v-model="sex">
<el-radio v-for="answer in props.questionItem.answer" :key="answer" :label="answer" >{{answer}}</el-radio>
</el-radio-group>
</div>
<div class="question-main-slot-quesnaire-answer" v-if="props.questionItem.modelVal=='hobby'">
<el-checkbox-group v-model="hobby">
<el-checkbox v-for="answer in props.questionItem.answer" :key="answer" :label="answer" >{{answer}}</el-checkbox>
</el-checkbox-group>
</div>
<div class="question-main-slot-quesnaire-answer" v-if="props.questionItem.modelVal=='remarks'">
<el-input type="textarea" autosize placeholder="请输入内容" v-model="remarks"></el-input>
</div>
</div>
</template>
</questionComp>
</div>
</el-main>
<el-footer></el-footer>
</el-container>
</div>
</template>
<script>
import questionComp from '../../components/questionComp.vue';
export default {
name: "question",
components:{
questionComp
},
data(){
return{
sex:'',
hobby:[],
remarks:''
}
},
computed:{
currentPage(){
return this.$store.state.questionStore.currentPage
}
}
}
</script>
<style scoped>
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.question-main-slot-quesnaire-answer{
margin: 5px auto;
}
</style>
(3)、src\components
<1>:product.vue:
<template>
<div class="product-main" @click.stop="routerInfo(item.id)">
<div class="product-main-item" :style="{backgroundColor:colors[item.color]}">
<el-row type="flex" justify="center" align="middle">
<el-col :span="24">
<div class="product-main-img">
<img :src="require('@/assets/img/'+item.image)">
</div>
<div class="product-main-name">
{{item.name}}
</div>
<div class="product-main-sales">
¥{{item.sales}}
</div>
<div class="product-main-cost">
销量:{{item.cost}}
</div>
</el-col>
</el-row>
<div class="product-add" @click.stop="addProduct(item.id)">加入购物车</div>
</div>
</div>
<2>:questionComp.vue:
<template>
<div class="questionComp-main">
<div class="questionComp-main-problem">
<el-row type="flex" justify="center" align="center">
<slot name="questionSlot" v-for="item in question" :questionItem="item"></slot>
</el-row>
</div>
<div class="questionComp-main-btn">
<el-row type="flex" justify="center" :gutter="20">
<el-button @click="handlerClick('up')" size="small" type="primary">上一题</el-button>
<el-button @click="handlerClick('next')" size="small" type="primary">下一题</el-button>
<el-button @click="handlerClick('rest')" size="small" type="primary">重置</el-button>
<el-button @click="handlerClick('submit')" size="small" type="primary">提交</el-button>
</el-row>
</div>
</div>
</template>
<script>
export default {
name: "questionComp",
data(){
return{
question:this.$store.state.questionStore.questionList
}
},
methods:{
handlerClick(type){
if (type=='up'){
this.$store.commit('setCurrentPage',-1)
}
if (type=='next') {
this.$store.commit('setCurrentPage',1)
}
}
}
}
</script>
<style scoped>
</style>
(4)、main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import $ from 'jquery';
import '../static/bootstrap-3.3.7-dist/css/bootstrap.css';
import '../static/bootstrap-3.3.7-dist/js/bootstrap.min';
import '../static/css/main.css';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 引入axiosUtil.js
import axiosUtil from './util/axiosUtil.js';
//引入Vuex总仓库
import store from './vuex/modules.js';
// 设置全局变量$axios
Vue.prototype.$axios = axiosUtil;
Vue.use(ElementUI);
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
(5)、App.vue
<template>
<div id="app">
<el-row type="flex" justify="center">
<el-col :span="6"></el-col>
<el-col :span="12">
<el-menu :default-active="this.$route.path" mode="horizontal"
background-color="#6F6D6D" text-color="#F3F2F2" active-text-color="#EFEB7F" @select="handleSelect">
<el-menu-item v-for="(item,i) in navList" :key="i" :index="item.name">
<template v-if="item.meta.show">
<i class="el-icon-location"></i>
{{item.meta.title}}
<template v-if="carTotal!=0 && item.meta.title=='购物车'">
<span>({{carTotal}})</span>
</template>
</template>
</el-menu-item>
</el-menu>
<router-view/>
</el-col>
<el-col :span="6"></el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'App',
computed:{
navList(){
return this.$router.options.routes.filter((item)=>{
if (item.meta.show!= undefined){
return item;
}
});
},
carTotal(){
var carList=this.$store.state.shopStore.carList;
var num=0;
carList.forEach((item)=>{
num+=item.count
});
return num;
}
},
methods:{
handleSelect(key,keyPath){
this.$router.push({name:key});
}
}
}
</script>
<style>
</style>
四、总结:
这个项目和上一篇博客页面上基本一样,只是为了练习一下element-ui就重做了一遍。
新的心得是:
1、更加理解了slot:slot说白了,就是为了父组件向子组件发送dom,比如要显示3个子组件,
子组件分为顶部\中部\底部分,其中顶部和底部样式都一样,中间部分不一样。
这个时候就要用到slot,在父组件任何位置发送dom,插入到子组件里面声明了slot的地方,从而起到复用的作用。
2、加深了计算属性和data的区别,
动态的数据要用计算属性,data数据初始化后,如果不手动改变,数据不会改变
3、熟悉了element-ui
4、加深了es6语法中this关键字的理解,=>里面的this是定义时的对象。
更多推荐
已为社区贡献3条内容
所有评论(0)