一、初始化项目结构
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>