vue选项式写法项目案例(购物车)

一、初始化项目结构

1.初始化vite项目

javascript 复制代码
npm create vite
cd vite-project
npm install

2.清理项目结构

清空App.vue

删除components目录下的HelloWorld.vue组件

3.为组件的样式启用sacc或less组件

javascript 复制代码
npm i sass

4.初始化index.css全局样式

css 复制代码
:root{
  font-size:12px
}

二、封装es-header组件

1.允许用户自定义title标题内容

2.允许用户自定义color文字颜色

3.允许用户自定义bgcolor背景颜色

4.允许用户自定义fsize字体大小

5.组件必须固定到页面顶部位置,高度45px,文字居中,z-index为999

javascript 复制代码
<template>
    <div class="head-container" :style="{backgroundColor:bgcolor,color:color,fontSize:fsize+'px'}">{{ title }}</div>
</template>
<script>
import bus from '../../ulits/eventBus'
export default {
    props:{
        title:{
            type:String,
            default:'es-header'
        },
        bgcolor:{
            type:String,
            default:'#007bff'
        },
        color:{
            type:String,
            default:'#fff'
        },
        fsize:{
            type:Number,
            default:'12'
        },
    }
}
</script>
<style scoped lang="scss">
.head-container{
    position: fixed;
    top:0;
    left: 0;
    width: 100%;
    height: 45px;
    text-align: center;
    line-height: 45px;
    z-index:999;
}
</style>

三、基于axios请求商品列表数据

1.安装axios

javascript 复制代码
npm i axios -S

2.在main.js入口文件中导入并配置axios

import axios from 'axios'

//配置请求的根路径

axios.defaults.baseURL='http://localhost:3000'

//将axios挂载为全局的$http自定义属性

app.config.globalProperties.$http=axios

javascript 复制代码
// 从vue中按需导入createApp函数,用于创建vue的实例对象
import { createApp } from 'vue'
// 导入待渲染的App组件
import App from './App.vue'
import './style.css'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import router from "./routers/index.js";
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import axios from 'axios'
// 创建vue的实例对象
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
  }
axios.defaults.baseURL='http://localhost:3000'
app.config.globalProperties.$http=axios
app.use(ElementPlus)
app.use(router)
// 指定vue控制的Dom区域,把App组件的模板结构渲染到指定的el区域中
app.mount('#app')

3.请求商品列表数据

四、封装es-footer组件

1.组件必须固定到页面底部位置,高度50px,内容两端贴边对其,z-index为999

2.允许用户自定义amount总价格,渲染时保留两位小数

3.允许用户自定义totalt总数量,并渲染到结算按钮中,如果要结算的商品数量为0,则禁用结算按钮

4.允许用户自定义isfull全选按钮的选中状态

5.允许用户通过自定义事件的形式,监听全选按钮选中状态的变化,并获取到最新的选中状态

javascript 复制代码
<template>
  <div class="footer-container">
    <div>
        <input type="checkbox" id="custom-control-label" :checked="isfull" @change="onCheckboxChange" /><label for="custom-control-label">全选</label>
    </div>
    <div>
        <span>合计:</span>
        <span class="amount">¥{{ amount.toFixed(2)}}</span>
    </div>
    <el-button class="btn-settle" :disabled="total===0">结算({{total}})</el-button>
  </div>
</template>
<script>
export default {
    props:{
        amount:{
            type:Number,
            dafault:0
        },
        total:{
            type:Number,
            dafault:0
        },
        isfull:{
            type:Boolean,
            default: false
        }
    },
    emits:['fullChange'],
    methods:{
        onCheckboxChange(e){
            this.$emit('fullChange',e.target.checked)
        }
    }
}
</script>

<style scoped lang="scss">
.footer-container{
    width: 100%;
    height: 50px;
    border-top:1px solid #ededed;
    background-color: #fff;
    position: fixed;
    bottom:0;
    left:0;
    padding:0 10px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    input{
       vertical-align: middle; 
    }
    .amount{
        color:red;
    }
    .btn-settle{
        min-width: 100px;
        height: 38px;
        border-radius: 19px;
    }
}
</style>

五、封装es-goods组件

1.实现edgoods组件的基本布局

2.封装组建的6的自定义属性id,thumb,title,price,count,checked

3.封装组件的自定义事件stateChange,允许外界 监听组件选中状态的变化

javascript 复制代码
<template>
  <div class="goods-container">
    <div class="left">
        <input type="checkbox" :id="id" :checked="checked" @change="onCheckboxChange" />
        <label :for="id"><img :src="thumb" alt=""></label>
    </div>
    <div class="right">
        <div class="top">{{ title }}</div>
        <div class="bottom">
            <div class="price">¥{{ price.toFixed(2) }}</div>
            <EsCounter :count="count" :min="1" @numberChange="numberChange"></EsCounter>
        </div>
    </div>
  </div>
</template>
<script>
import EsCounter from './esCounter.vue'
export default {
    components:{
        EsCounter
    },
    props:{
        id:{
            type:String,
            default:'',
            require:true
        },
        thumb:{
            type:String,
            default:'',
            require:true
        },
        title:{
            type:String,
            default:'',
            require:true
        },
        price:{
            type:Number,
            default:0,
            require:true
        },
        count:{
            type:Number,
            default:0,
            require:true
        },
        checked:{
            type:Boolean,
            default:false,
            require:true
        }
    },
    emits:['stateChange','countChange'],
    methods:{
        onCheckboxChange(e){
            this.$emit('stateChange',e.target.id,e.target.checked)
        },
        numberChange(num){
            this.$emit('countChange',this.id,num)
        }
    }
}
</script>

<style scoped lang="scss">
.goods-container{
    width: 100%;
    padding:10px;
    display: flex;
    border:1px solid #ededed;
    .left{
        width: 110px;
        height: 100px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        img{
            width: 100px;
            height: 100px;
        }
        input{
           vertical-align: middle; 
        }
    }
    .right{
        padding: 10px;
        flex:1;
        display: flex;
        flex-direction: column;
        justify-content: space-around;
        .bottom{
            display: flex;
            justify-content: space-between;
            align-items: center;
            .price{
                color:red;
            }
        }
    }
}
</style>

六、实现合计、结算数量、全选功能

javascript 复制代码
<template>
    <div class="son">
      <EsHeader title="案例"></EsHeader>  
      <EsGoods v-for="goods in goodsList" :key="goods.id" :id="goods.id" :thumb="goods.thumb" :title="goods.title" :price="goods.price" :count="goods.count" :checked="goods.state" @stateChange="stateChange" @countChange='countChange'></EsGoods>
      <EsFooter :isfull="isfull" :amount="amount" :total="total" @fullChange="fullChange"></EsFooter>
    </div>
</template>
<script>
import EsHeader from './EsHeader.vue'
import EsFooter from './esFooter.vue'
import EsGoods from './EsGoods.vue'
export default {
    components:{EsHeader,EsGoods,EsFooter},
    data(){
        return{
            goodsList:[],
            amount:0,
            total:0
        }
    },
    methods:{
        async getGoodsList(){
           const {data:res}= await this.$http.get('/goodsList')
           this.goodsList = res
        },
        fullChange(flag){
            this.goodsList.forEach(item=>item.state =flag)
        },
        stateChange(id,checked){
            const findRes = this.goodsList.find((item)=>{
                return item.id==id;
            })
            if(findRes){
                findRes.state =checked;
            }
            this.isfull = this.goodsList.every(item=>item.state);
        },
        countChange(id,count){
            const findRes = this.goodsList.find((item)=>{
                return item.id==id;
            })
            if(findRes){
                findRes.count =count;
            }
        }
    },
    created(){
        this.getGoodsList()
    },
    computed:{
        total(){
            let a=0;
            this.goodsList.filter(item=>item.state).forEach(item=>a+=item.count)
            return a
        },
        amount(){
            let sum=0;
            this.goodsList.filter(item=>item.state).forEach(item=>sum+=item.count*item.price)
            return sum
        },
    }
}
</script>
  
<style scoped lang="scss">
.son{
    padding: 45px 0 50px 0;
}
</style>

七、封装es-counter组件能

1.渲染组件的基础布局

2.实现数量值得加减操作

3.处理min值

4.使用watc处理文本框输入的结果

5.封装numChange自定义事件

javascript 复制代码
<template>
  <div class="counter-contain">
    <button class="btn" :disabled="num===1" @click="onSubClick"> -</button>
    <input type="number" class="counter-input" v-model.number.lazy="num" >
    <button class="btn"  @click="onAddClick">+</button>
  </div>
</template>
<script>
export default {
    props:{
        count:{
            type:Number,
            default:0,
            require:true
        },
        min:{
            type:Number,
            default:0,
            require:NaN
        }
    },
    emits:['numberChange'],
    data(){
        return{
           num: this.count
        }
    },
    methods:{
        onSubClick(){
            if(isNaN(this.min)&& this.num-1<this.min)return;
            this.num--
        },
        onAddClick(){
            this.num++
        },
    },
    watch:{
        num:{
            handler(newVal,oldVal){
                const parseRes = parseInt(newVal);
                //不是数字或者小于1,强制等于1
                if(isNaN(parseRes) ||parseRes<1){
                    this.num=1
                    return
                }
                // //如果新值是小数,把转换的结果赋给num
                if(String(parseRes).includes('.')==-1){
                    this.num=parseRes
                    return
                }
                this.$emit('numberChange',this.num)
            }
        }
    }
}
</script>
<style scoped lang="scss">
.counter-contain{
 .btn{
    width: 34px;
}
.counter-input{
    width: 36px;
}   
}

</style>
相关推荐
四喜花露水几秒前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
前端Hardy10 分钟前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
web Rookie40 分钟前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust1 小时前
css:基础
前端·css
帅帅哥的兜兜1 小时前
css基础:底部固定,导航栏浮动在顶部
前端·css·css3
工业甲酰苯胺1 小时前
C# 单例模式的多种实现
javascript·单例模式·c#
yi碗汤园1 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
就是个名称1 小时前
购物车-多元素组合动画css
前端·css
编程一生1 小时前
回调数据丢了?
运维·服务器·前端
丶21362 小时前
【鉴权】深入了解 Cookie:Web 开发中的客户端存储小数据
前端·安全·web