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>
相关推荐
腾讯TNTWeb前端团队3 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰6 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪6 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy7 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom8 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom8 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom8 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom8 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom8 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试