赞
踩
项目源代码:https://download.csdn.net/download/qq_44757034/86440913
npm init vite@latest my-vue-app -- --template vue
创建成功
用WebStorm打开项目
自动下载好对应的依赖
启动运行项目
npm run dev
http://127.0.0.1:5173/
提示要安装插件
<template> <h1>{{msg}}</h1> <div> <a href="https://vitejs.dev" target="_blank"> <img src="/vite.svg" class="logo" alt="Vite logo" /> </a> <a href="https://vuejs.org/" target="_blank"> <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /> </a> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> // This starter template is using Vue 3 <script setup> SFCs // Check out https://vuejs.org/api/sfc-script-setup.html#script-setup import HelloWorld from './components/HelloWorld.vue' let msg = "itbluebox" </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
翻转字符串函数
<template> <h1>{{msg}}</h1> <h1>{{reMsg(msg)}}</h1> <div> <a href="https://vitejs.dev" target="_blank"> <img src="/vite.svg" class="logo" alt="Vite logo" /> </a> <a href="https://vuejs.org/" target="_blank"> <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /> </a> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> // This starter template is using Vue 3 <script setup> SFCs // Check out https://vuejs.org/api/sfc-script-setup.html#script-setup import HelloWorld from './components/HelloWorld.vue' let msg = "itbluebox" function reMsg(val){ return val.split('').reverse().join('') } </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
<template> <h1>{{msg}}</h1> <h1>{{reMsg(msg)}}</h1> <button @click="setMsg">点击修改</button> <div> <a href="https://vitejs.dev" target="_blank"> <img src="/vite.svg" class="logo" alt="Vite logo" /> </a> <a href="https://vuejs.org/" target="_blank"> <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /> </a> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> // This starter template is using Vue 3 <script setup> SFCs // Check out https://vuejs.org/api/sfc-script-setup.html#script-setup import HelloWorld from './components/HelloWorld.vue' let msg = "itbluebox" function reMsg(val){ return val.split('').reverse().join('') } function setMsg(){ msg = "red!!!" } </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
点击,发现没有反应
数据以及发生变化,但是没有响应式
设置响应式
//通过 ref 创建响应式对象
<template> <h1>{{msg}}</h1> <h1>{{reMsg(msg)}}</h1> <button @click="setMsg">点击修改</button> <div> <a href="https://vitejs.dev" target="_blank"> <img src="/vite.svg" class="logo" alt="Vite logo" /> </a> <a href="https://vuejs.org/" target="_blank"> <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /> </a> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> // This starter template is using Vue 3 <script setup> SFCs // Check out https://vuejs.org/api/sfc-script-setup.html#script-setup import HelloWorld from './components/HelloWorld.vue' import {ref} from 'vue' //通过 ref 创建响应式对象 let msg = ref("itbluebox") function reMsg(val){ return val.split('').reverse().join('') } function setMsg(){ msg.value = "red!!!" } </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
分析HelloWorld.vue
设置一个对象并显示
<template> <div> <h1>用户名:{{user.username}}</h1> <h1>年龄:{{user.age}}</h1> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> import HelloWorld from './components/HelloWorld.vue' import {ref} from 'vue' var user = ref({ username: 'itbluebox', age:18 }) </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
显示成功
显示成功
但是在修改的时候(设置点击事件设置点击修改年龄)
刷新页面
点击修改年龄,值已经被修改,但是页面没有变化
值设置为value的方式
点击修改
页面值发生变化
设置响应式的转换
<template> <div> <h1>用户名:{{user.username}}</h1> <h1>年龄:{{user.age}}</h1> <button @click="setUserAge">点击修改年龄</button> <h1>老师:{{ teacher.username }}</h1> <button @click="setTeacherName">修改名字</button> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> import HelloWorld from './components/HelloWorld.vue' import {ref,reactive} from 'vue' var user = ref({ username: 'itbluebox', age:18 }) const teacher = reactive({ username: 'itbluebox teacher', sex:"男", age:18 }) function setUserAge(){ //user.age = 14 user.value.age = 14 console.log(user) } function setTeacherName(){ teacher.username = "redbox" } </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
<template> <div> <h1>用户名:{{user.username}}</h1> <h1>年龄:{{user.age}}</h1> <button @click="setUserAge">点击修改年龄</button> <h1>老师:{{ teacher.username }}</h1> <button @click="setTeacherName">修改名字</button> <h1>用户名:{{user2.username}}</h1> <h1>年龄:{{user2.age}}</h1> <button @click="setUser2Age">点击修改年龄</button> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> import HelloWorld from './components/HelloWorld.vue' import {ref,reactive} from 'vue' var user = ref({ username: 'itbluebox', age:18 }) const teacher = reactive({ username: 'itbluebox teacher', sex:"男", age:18 }) function setUserAge(){ //user.age = 14 user.value.age = 14 console.log(user) } function setTeacherName(){ teacher.username = "redbox" } let user2 = reactive(user.value) function setUser2Age(){ user2.age = 100; } </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
修改成功
事件对象
<template> <div> <h1>用户名:{{user.username}}</h1> <h1>年龄:{{user.age}}</h1> <button @click="setUserAge">点击修改年龄</button> <h1>老师:{{ teacher.username }}</h1> <button @click="setTeacherName">修改名字</button> <h1>用户名:{{user2.username}}</h1> <h1>年龄:{{user2.age}}</h1> <button @click="setUser2Age">点击修改年龄</button> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> import HelloWorld from './components/HelloWorld.vue' import {ref,reactive} from 'vue' var user = ref({ username: 'itbluebox', age:18 }) const teacher = reactive({ username: 'itbluebox teacher', sex:"男", age:18 }) function setUserAge(event){ //user.age = 14 user.value.age = 14 console.log(user) console.log(event) } function setTeacherName(){ teacher.username = "redbox" } let user2 = reactive(user.value) function setUser2Age(){ user2.age = 100; } </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
传递参数
<button @click="setUserAge(10000)">点击修改年龄</button>
function setUserAge(value){
//user.age = 14
user.value.age = value
console.log(user)
}
<button @click="setUserAge(10000,$event)">点击修改年龄</button>
传递参数的同时传递事件对象
function setUserAge(value,event){
//user.age = 14
user.value.age = value
console.log(user)
console.log(event)
}
computed
将计算的结果进行缓存,防止多次调用损失性能<template> <div> <h1>{{msg}}</h1> <h1>{{ reMsg }}</h1> <h1>{{ reMsg }}</h1> <h1>{{ reMsg }}</h1> <button @click="setMsg('itred',$event)"></button> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> import HelloWorld from './components/HelloWorld.vue' import {ref,computed,reactive} from 'vue' let msg = reactive("itbluebox") function setMsg(value,event){ msg.value = value console.log(value) console.log(event) } const reMsg = computed( function (){ console.log(123) return msg.split("").reverse().join("") } ) </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
我们可以发现虽然调用了三遍,但是函数只执行了一次
<template> <div> <h1>{{msg}}</h1> <h1>{{ reMsg }}</h1> <h1>{{ reMsg }}</h1> <h1>{{ reMsg }}</h1> <button @click="setMsg('itred',$event)">修改内容</button> <button @click="setReMsg">修改计算属性reMsg</button> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> import HelloWorld from './components/HelloWorld.vue' import {ref,computed} from 'vue' let msg = ref("itbluebox") function setMsg(value,event){ msg.value = value console.log(value) console.log(event) } const reMsg = computed( { get:()=>{ console.log(123) return msg.value.split("").reverse().join("") }, set:(value)=>{ msg.value = value.split("").reverse().join("") } }) function setReMsg(){ reMsg.value = "zhngsan"; } </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
上述reMsg
默认加载的时候调用了get
方法
点击修改计算属性reMsg
的时候调用set方法后又调用get方法
watch(msg,(newValue,oldValue)=>{
console.log("newValue",newValue)
console.log("oldValue",oldValue)
})
监听对象
<h1>{{user.name}}</h1>
<button @click="user.name = '赵六'">修改名字</button>
watch(
()=> user.name,
(newValue,oldValue)=>{
console.log("newValue",newValue)
console.log("oldValue",oldValue)
})
同时监听多个数据
watch(
[msg,()=> user.name],
(newValue,oldValue)=>{
console.log("newValue",newValue)
console.log("oldValue",oldValue)
})
<template> <div> <h1>{{msg}}</h1> <h1 v-bind:class="classname"></h1> <h1 :class="classname"></h1> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> import HelloWorld from './components/HelloWorld.vue' import {ref, computed, watch, reactive} from 'vue' let msg = ref("itbluebox") let classname = ref('box bgRed') </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } .box{ width: 200px; height: 200px; background-color: pink; } .bgRed{ background-color: red; } </style>
<template> <div> <h1>{{msg}}</h1> <h1 v-bind:class="classname"></h1> <h1 :class="classname"></h1> <h1 :id="box"></h1> </div> <HelloWorld msg="Vite + Vue" /> </template> <script setup> import HelloWorld from './components/HelloWorld.vue' import {ref, computed, watch, reactive} from 'vue' let msg = ref("itbluebox") let classname = ref('box bgRed') let box = ref('box') </script> <style scoped> .box{ width: 200px; height: 200px; background-color: pink; } .bgRed{ background-color: red; } #box{ width: 200px; height: 200px; background-color: pink; } </style>
<h1 :title="desc" :class="classname"></h1>
let desc = ref("这是一个box")
<div class="content">{{html}}</div>
let html = ref("<h1>这是HTML的内容</h1>")
我们发现显示的是文本
设置富文本显示
<div class="content" v-html="html"></div>
监听点击事件
全显示
<div v-on:click="count++">{{count}}</div>
let count = ref(0)
简写
<h1 :class="{box:true,bgRed:toggle}" @click="toggle = !toggle" ></h1>
let toggle = ref(true)
:style
<h1 :style="h1Style"></h1>
let h1Style = reactive({
background:"skyblue",
width:"200px",
height:"200px"
})
有横杠的属性使用驼峰命名法
borderStyle:"solid",
清空一下App.vue的数据
<template>
<div>
</div>
</template>
<script setup>
</script>
<style scoped>
</style>
新建自定义组件
<template> <h1> <span> {{num}} </span> ------------ <span> {{title}} </span> </h1> </template> <script setup> let num = 1; let title = "今天是个好日子" </script> <style scoped> </style>
在App.vue当中
<template>
<div>
<ListItem></ListItem>
</div>
</template>
<script setup>
import ListItem from "./components/ListItem.vue"
</script>
<style scoped>
</style>
<template> <div> <ListItem :num="article.num" :title="article.title"></ListItem> <!-- prop = property --> </div> </template> <script setup> import ListItem from "./components/ListItem.vue" import {reactive} from "vue"; let article = reactive({ num:10, title:"定义propss" }) </script> <style scoped> </style>
<template> <h1> <span> {{props.num}} </span> --------- <span> {{props.title}} </span> </h1> </template> <script setup> import { defineProps } from 'vue' const props = defineProps({ num:Number, title:String }) </script> <style scoped> </style>
刷新页面
<template> <div> <ListItem :num="article.num" :title="article.title"></ListItem> <!-- prop = property --> <h1>列表循环</h1> <ListItem :num="item.num" :title="item.title" v-for="item in articleList"></ListItem> </div> </template> <script setup> import ListItem from "./components/ListItem.vue" import {reactive} from "vue"; let article = reactive({ num:10, title:"定义propss" }) let articleList = reactive([ { num:10, title:"定义propss1" }, { num:11, title:"定义propss2" } ]) </script> <style scoped> </style>
刷新页面
<ListItem :num="article.num" :title="article.title" :article="article"></ListItem>
<!-- prop = property -->
<h1>列表循环</h1>
<ListItem
:num="item.num"
:title="item.title"
:article="item"
v-for="item in articleList">
</ListItem>
<template> <h1> <span> {{props.num}} </span> --------- <span> {{props.title}} </span> <br> article <span> {{props.article.num}} </span> --------- <span> {{props.article.title}} </span> </h1> </template> <script setup> import { defineProps } from 'vue' const props = defineProps({ num:Number, title:String, article:Object }) </script> <style scoped> </style>
刷新页面
子组件当中定义事件
<template> <h1 @click="sendRead"> <span> {{props.num}} </span> --------- <span> {{props.title}} </span> <br> article <span> {{props.article.num}} </span> --------- <span> {{props.article.title}} </span> </h1> </template> <script setup> import { defineProps,defineEmits } from 'vue' const props = defineProps({ num:Number, title:String, article:Object }) const emit = defineEmits(['finishRead','reading']) function sendRead(){ emit('finishRead') } </script> <style scoped> </style>
<template> <div> <ListItem :num="article.num" :title="article.title" :article="article"></ListItem> <!-- prop = property --> <h1>列表循环</h1> <ListItem :num="item.num" :title="item.title" :article="item" v-for="item in articleList" @finishRead="changeTitle(item)" > </ListItem> </div> </template> <script setup> import ListItem from "./components/ListItem.vue" import {reactive} from "vue"; let article = reactive({ num:10, title:"定义propss" }) let articleList = reactive([ { num:10, title:"定义propss1" }, { num:11, title:"定义propss2" } ]) function changeTitle(item){ item.title += '【已读】' } </script> <style scoped> </style>
点击
emit('finishRead','【已经阅读】')
<template> <div> <ListItem :num="article.num" :title="article.title" :article="article"></ListItem> <!-- prop = property --> <h1>列表循环</h1> <ListItem :num="item.num" :title="item.title" :article="item" v-for="item in articleList" @finishRead="changeTitle(item,$event)" > </ListItem> </div> </template> <script setup> import ListItem from "./components/ListItem.vue" import {reactive} from "vue"; let article = reactive({ num:10, title:"定义propss" }) let articleList = reactive([ { num:10, title:"定义propss1" }, { num:11, title:"定义propss2" } ]) function changeTitle(item,$event){ item.title += $event } </script> <style scoped> </style>
点击
点击几次就增加几次
设置点击事件,设置点击内容内容不增加
function changeTitle(item,$event){
if(item.title.indexOf($event) == -1){
item.title += $event
}
}
点击多次内容不累加
采用下角标的方式实现
<template> <div> <ListItem :num="article.num" :title="article.title" :article="article"></ListItem> <!-- prop = property --> <h1>列表循环</h1> <ListItem :num="item.num" :title="item.title" :article="item" v-for="(item,index) in articleList" @finishRead="changeTitle(index,$event)" > </ListItem> </div> </template> <script setup> import ListItem from "./components/ListItem.vue" import {reactive} from "vue"; let article = reactive({ num:10, title:"定义propss" }) let articleList = reactive([ { num:10, title:"定义propss1" }, { num:11, title:"定义propss2" } ]) function changeTitle(index,$event){ if(articleList[index].title.indexOf($event) == -1){ articleList[index].title += $event } } </script> <style scoped> </style>
实现效果是一样的
创建一些其他的页面
<template>
<h1>Home</h1>
</template>
<script>
export default {
name: "Home"
}
</script>
<style scoped>
</style>
<template>
<h1>About</h1>
</template>
<script>
export default {
name: "About"
}
</script>
<style scoped>
</style>
<template>
<h1>Buycart</h1>
</template>
<script>
export default {
name: "Buycart"
}
</script>
<style scoped>
</style>
设置路由
安装vue-router
npm install vue-router@4
完善index.js
import { createRouter, createWebHistory, createWebHashHistory } from "vue-router" import Home from "../views/Home.vue"; import About from "../views/About.vue"; import Buycart from "../views/Buycart.vue"; //2、定义一些路由 //每个路由都需要映射到一个组件 //我们后面再讨论嵌套路由 const routes = [ {path:"/",component:Home,name:"home"}, {path:"/about",component:About,name:"About"}, {path:"/buycart",component:Buycart,name:"Buycart"}, ]; //3、创建路由实例并传递‘routes’配置 //你可以在这里输入更多的配置,但是我们在这里 const router = createRouter({ //4、内部提供了 history 模式的实现。为了简单起见,我们在这里使用hash模式 history:createWebHashHistory(), routes, //routes:routes 的缩写 }) export default router
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index.js'
let app = createApp(App)
app.use(router)
app.mount('#app')
修改App.vue引入路由,完善页面
<template>
<div>
<router-view></router-view>
</div>
</template>
<script setup>
</script>
<style scoped>
</style>
清除样式所带来的干扰
<template> <div> <router-link to="/">跳转至首页</router-link> | <router-link to="/about">跳转至about</router-link> | <button @click="router.push('/')">跳转至首页</button> <button @click="goAbout">跳转至abou</button> </div> <router-view></router-view> </template> <script setup> import { useRoute, useRouter } from "vue-router"; // 获取路由信息 let route = useRoute(); // 执行路由的跳转 let router = useRouter(); function goAbout() { console.log(route); router.push("/about"); } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
{
path:"/product/:id",
component:()=>{
return import('../views/Product.vue')
},
name:"product"
},
访问:http://127.0.0.1:5173/#/product/123
停止运行项目
npm install vuex@next --save
import { createStore } from 'vuex' // 创建一个新的 store 实例 const store = createStore({ state () { return { count: 0 } }, mutations: { increment (state) { state.count++ } } }) export default store;
在main.js当中使用
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index.js'
import store from "./store/index.js";
let app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')
完善store下的index.js
import { createStore } from 'vuex' // 创建一个新的 store 实例 const store = createStore({ state () { return { count: 0 } }, mutations: { increment (state,payload) { state.count+=payload } }, getters:{ totalPrice(state){ return state.count * 98.8; } }, actions:{ asyncAdd(store,payload){ setTimeout(()=>{ store.commit('increment',10) },1000) } } }) export default store;
<template> <h1>Buycart</h1> <h1>商品数量:{{ store.state.count }}</h1> <button @click="addProd">添加商品数量+2</button> </template> <script setup> import {useStore} from 'vuex' let store = useStore() function addProd(){ store.commit('increment',2) } </script> <style scoped> </style>
访问
http://127.0.0.1:5173/#/buycart
点击以后每次加2
<template> <h1>Buycart</h1> <h1>商品数量:{{ store.state.count }}</h1> <button @click="addProd">添加商品数量+2</button> <button @click="asyncAddProd">异步添加商品数量+10</button> </template> <script setup> import {useStore} from 'vuex' let store = useStore() function addProd(){ store.commit('increment',2) } function asyncAddProd(){ store.dispatch('asyncAdd'); } </script> <style scoped> </style>
点击以后有延迟的在新增数据
使用参数传递新增数据
store.commit('increment',payload)
store.dispatch('asyncAdd',5);
<template> <h1>Buycart</h1> <h1>商品数量:{{ store.state.count }}</h1> <button @click="addProd">添加商品数量+2</button> <button @click="asyncAddProd(5)">异步添加商品数量+5</button> </template> <script setup> import {useStore} from 'vuex' let store = useStore() function addProd(){ store.commit('increment',2) } function asyncAddProd(val){ store.dispatch('asyncAdd',val); } </script> <style scoped> </style>
获取商品总价
<h1>商品总价{{ store.getters.totalPrice }}</h1>
官网:https://www.antdv.com/docs/vue/migration-v3-cn
安装命令
npm i --save ant-design-vue
安装成功
局部引入组件
安装
npm i unplugin-vue-components -D
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import Components from "unplugin-vue-components/vite";
import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [AntDesignVueResolver()],
}),
],
});
<template>
<h1>About</h1>
<a-button type="primary">Primary Button</a-button>
</template>
<script setup>
</script>
<style scoped>
</style>
安装axios
npm install axios
import axios from "axios"; const request = axios.create({ // 配置接口请求的基准路径 baseURL: "http://localhost:8080/metashop/api", }); // 响应拦截器 request.interceptors.response.use( (response) => { if (response.status == 200) { return response.data; } else { return response; } }, function (error) { return Promise.reject(error); } ); export const getHomepage = (params) => { return request({ method: "GET", url: "/homepage", // params选项用来配置QUERY的参数 ?query=xxxx params, }); }; export const getProducts = () => { return request({ method: "GET", url: "/products", }); };
重新启动项目后访问:http://127.0.0.1:5173/#/
成功
获取数据
在子组件当中定义发起请求,在父组件当中显示数据
自定义异步组件
<template> <h1>首页</h1> <ul> <li v-for="(item,i) in hero"> <h3>{{item.category}}</h3> </li> </ul> </template> <script setup> import * as api from '../api/index'; import {reactive} from "vue"; let result = await api.getHomepage(); const hero = reactive(result.hero); </script> <style scoped> </style>
完善Home
<template> <suspense> <template #fallback> <div class="loading"> <h1>Loading</h1> </div> </template> <template #default> <h1><HomeAsync></HomeAsync></h1> </template> </suspense> </template> <script setup> import { onMounted, defineAsyncComponent } from "vue"; const HomeAsync = defineAsyncComponent(() => import("../components/HomeCom.vue") ); onMounted(async () => { // let result = await api.getHomepage(); // console.log(result); }); </script> <style lang="less" scoped></style>
运行查看
会先显示Loading后显示首页
设置加载页面的优化,我们下载一些加载动画
https://loading.io/spinner/
选择一个
复制这段代码
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style=" margin: auto; background: #fff; display: block; position: fixed; left: 50%; top: 50%; margin-left: -100px; margin-top: -100px; " width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" > <defs> <clipPath id="ldio-ssf8off6o8l-cp" x="0" y="0" width="100" height="100"> <rect x="0" y="5" width="100" height="46"></rect> </clipPath> </defs> <path d="M70 75.2H34.1l-4.1-18.4l-0.7-3l-1-4.7c0 0 0 0 0-0.1c0-0.1 0-0.1-0.1-0.2c0 0 0-0.1-0.1-0.1c0 0 0-0.1-0.1-0.1 c0 0-0.1-0.1-0.1-0.1c0 0-0.1-0.1-0.1-0.1c0 0-0.1-0.1-0.1-0.1c0 0 0 0-0.1-0.1L22.3 44c0-0.1 0-0.2 0-0.3c0-1.9-1.6-3.5-3.5-3.5 s-3.5 1.6-3.5 3.5c0 1.9 1.6 3.5 3.5 3.5c0.7 0 1.4-0.2 2-0.6l4.8 3.7L31.5 77c0 0 0 0 0 0l-5.6 7.7c-0.3 0.5-0.4 1.1-0.1 1.6 c0.3 0.5 0.8 0.8 1.3 0.8h4c-0.8 0.8-1.3 1.9-1.3 3.2c0 2.6 2.1 4.7 4.7 4.7c2.6 0 4.7-2.1 4.7-4.7c0-1.2-0.5-2.3-1.3-3.2h29 c-0.8 0.8-1.3 1.9-1.3 3.2c0 2.6 2.1 4.7 4.7 4.7c2.6 0 4.7-2.1 4.7-4.7c0-1.2-0.5-2.3-1.3-3.2H77c0.8 0 1.5-0.7 1.5-1.5 s-0.7-1.5-1.5-1.5H30l4.3-6h36.8c0.7 0 1.3-0.5 1.4-1.1l7.5-27.3c0.2-0.8-0.2-1.6-1-1.8c-0.8-0.2-1.6 0.2-1.8 1l-1.3 4.7l-0.8 3" fill="#dddddd" ></path> <polygon points="31.3 53.1 35.7 73.2 68.5 73.2 74 53.1" fill="#dddddd" ></polygon> <g clip-path="url(#ldio-ssf8off6o8l-cp)"> <g> <g transform="translate(50 41)"> <path d="M6.5-6.7C6.1-6.9 5.7-7.2 5.3-7.4C5-7.5 4.6-7.7 4.3-7.8C3.1-2.2-4-3.7-2.9-9.3c-0.4 0-0.7 0-1.1 0 c-0.5 0-1 0.1-1.4 0.2c-1.8 0.3-3.6 0.9-5.3 1.8l1.1 4.2l3.1-0.8L-8.7 6.9L3.2 9.3L5.4-1.5l2.5 2l2.7-3.4C9.5-4.4 8.1-5.7 6.5-6.7z" fill="#e15b64" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(35 17)"> <path d="M3.4-5.3L2.5-5l0.8-2.3L1.1-6.3l-1.2-2.2l-1.6 4.6l-4.6-1.6l0.9 2.3l-2.2 1.2l2.3 0.8L-6-0.9 c-0.6 0.3-0.8 0.9-0.5 1.5l1 2.1C-5.2 3.4-4.6 3.6-4 3.3l0.1-0.1l2.1 4.5C-1.4 8.4-0.7 8.7 0 8.3l1.7-0.8l1.7-0.8L5 5.9l1.7-0.8 C7.4 4.8 7.7 4 7.4 3.3L5.2-1.1l0.1-0.1c0.6-0.3 0.8-0.9 0.5-1.5l-1-2.1C4.6-5.4 3.9-5.6 3.4-5.3z" fill="#f47e60" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(66 26)"> <path d="M-4.5-3.7L1.9-6l0.5-0.2L2-7.2l-6.9 2.5C-5.7-4.4-6.1-3.5-6-2.7c0 0.1 0 0.2 0.1 0.3l3 8.2 C-2.5 6.9-1.3 7.4-0.2 7l5.6-2C5.9 4.8 6.2 4.2 6 3.7L3.2-3.9l-0.4-1L2.4-4.7L1.9-4.5l-3.2 1.2l-2.7 1c-0.3 0.1-0.6 0-0.8-0.2 c-0.1-0.1-0.1-0.1-0.1-0.2C-5.1-3.1-4.9-3.6-4.5-3.7z" fill="#f8b26a" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(55 6)"> <polygon points="0 -4.9 1.6 -1.7 5.1 -1.1 2.6 1.3 3.2 4.9 0 3.2 -3.2 4.9 -2.6 1.3 -5.1 -1.1 -1.6 -1.7" fill="#abbd81" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </polygon> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> </g> <g clip-path="url(#ldio-ssf8off6o8l-cp)"> <g transform="translate(0 -75)"> <g> <g transform="translate(50 41)"> <path d="M6.5-6.7C6.1-6.9 5.7-7.2 5.3-7.4C5-7.5 4.6-7.7 4.3-7.8C3.1-2.2-4-3.7-2.9-9.3c-0.4 0-0.7 0-1.1 0 c-0.5 0-1 0.1-1.4 0.2c-1.8 0.3-3.6 0.9-5.3 1.8l1.1 4.2l3.1-0.8L-8.7 6.9L3.2 9.3L5.4-1.5l2.5 2l2.7-3.4C9.5-4.4 8.1-5.7 6.5-6.7z" fill="#e15b64" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(35 17)"> <path d="M3.4-5.3L2.5-5l0.8-2.3L1.1-6.3l-1.2-2.2l-1.6 4.6l-4.6-1.6l0.9 2.3l-2.2 1.2l2.3 0.8L-6-0.9 c-0.6 0.3-0.8 0.9-0.5 1.5l1 2.1C-5.2 3.4-4.6 3.6-4 3.3l0.1-0.1l2.1 4.5C-1.4 8.4-0.7 8.7 0 8.3l1.7-0.8l1.7-0.8L5 5.9l1.7-0.8 C7.4 4.8 7.7 4 7.4 3.3L5.2-1.1l0.1-0.1c0.6-0.3 0.8-0.9 0.5-1.5l-1-2.1C4.6-5.4 3.9-5.6 3.4-5.3z" fill="#f47e60" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(66 26)"> <path d="M-4.5-3.7L1.9-6l0.5-0.2L2-7.2l-6.9 2.5C-5.7-4.4-6.1-3.5-6-2.7c0 0.1 0 0.2 0.1 0.3l3 8.2 C-2.5 6.9-1.3 7.4-0.2 7l5.6-2C5.9 4.8 6.2 4.2 6 3.7L3.2-3.9l-0.4-1L2.4-4.7L1.9-4.5l-3.2 1.2l-2.7 1c-0.3 0.1-0.6 0-0.8-0.2 c-0.1-0.1-0.1-0.1-0.1-0.2C-5.1-3.1-4.9-3.6-4.5-3.7z" fill="#f8b26a" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(55 6)"> <polygon points="0 -4.9 1.6 -1.7 5.1 -1.1 2.6 1.3 3.2 4.9 0 3.2 -3.2 4.9 -2.6 1.3 -5.1 -1.1 -1.6 -1.7" fill="#abbd81" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </polygon> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> </g> </g> </svg>
将刚刚的代码复制到这里
添加加载内容和百分比
<template> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style=" margin: auto; background: #fff; display: block; position: fixed; left: 50%; top: 50%; margin-left: -100px; margin-top: -100px; " width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" > <defs> <clipPath id="ldio-ssf8off6o8l-cp" x="0" y="0" width="100" height="100"> <rect x="0" y="5" width="100" height="46"></rect> </clipPath> </defs> <path d="M70 75.2H34.1l-4.1-18.4l-0.7-3l-1-4.7c0 0 0 0 0-0.1c0-0.1 0-0.1-0.1-0.2c0 0 0-0.1-0.1-0.1c0 0 0-0.1-0.1-0.1 c0 0-0.1-0.1-0.1-0.1c0 0-0.1-0.1-0.1-0.1c0 0-0.1-0.1-0.1-0.1c0 0 0 0-0.1-0.1L22.3 44c0-0.1 0-0.2 0-0.3c0-1.9-1.6-3.5-3.5-3.5 s-3.5 1.6-3.5 3.5c0 1.9 1.6 3.5 3.5 3.5c0.7 0 1.4-0.2 2-0.6l4.8 3.7L31.5 77c0 0 0 0 0 0l-5.6 7.7c-0.3 0.5-0.4 1.1-0.1 1.6 c0.3 0.5 0.8 0.8 1.3 0.8h4c-0.8 0.8-1.3 1.9-1.3 3.2c0 2.6 2.1 4.7 4.7 4.7c2.6 0 4.7-2.1 4.7-4.7c0-1.2-0.5-2.3-1.3-3.2h29 c-0.8 0.8-1.3 1.9-1.3 3.2c0 2.6 2.1 4.7 4.7 4.7c2.6 0 4.7-2.1 4.7-4.7c0-1.2-0.5-2.3-1.3-3.2H77c0.8 0 1.5-0.7 1.5-1.5 s-0.7-1.5-1.5-1.5H30l4.3-6h36.8c0.7 0 1.3-0.5 1.4-1.1l7.5-27.3c0.2-0.8-0.2-1.6-1-1.8c-0.8-0.2-1.6 0.2-1.8 1l-1.3 4.7l-0.8 3" fill="#dddddd" ></path> <polygon points="31.3 53.1 35.7 73.2 68.5 73.2 74 53.1" fill="#dddddd" ></polygon> <g clip-path="url(#ldio-ssf8off6o8l-cp)"> <g> <g transform="translate(50 41)"> <path d="M6.5-6.7C6.1-6.9 5.7-7.2 5.3-7.4C5-7.5 4.6-7.7 4.3-7.8C3.1-2.2-4-3.7-2.9-9.3c-0.4 0-0.7 0-1.1 0 c-0.5 0-1 0.1-1.4 0.2c-1.8 0.3-3.6 0.9-5.3 1.8l1.1 4.2l3.1-0.8L-8.7 6.9L3.2 9.3L5.4-1.5l2.5 2l2.7-3.4C9.5-4.4 8.1-5.7 6.5-6.7z" fill="#e15b64" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(35 17)"> <path d="M3.4-5.3L2.5-5l0.8-2.3L1.1-6.3l-1.2-2.2l-1.6 4.6l-4.6-1.6l0.9 2.3l-2.2 1.2l2.3 0.8L-6-0.9 c-0.6 0.3-0.8 0.9-0.5 1.5l1 2.1C-5.2 3.4-4.6 3.6-4 3.3l0.1-0.1l2.1 4.5C-1.4 8.4-0.7 8.7 0 8.3l1.7-0.8l1.7-0.8L5 5.9l1.7-0.8 C7.4 4.8 7.7 4 7.4 3.3L5.2-1.1l0.1-0.1c0.6-0.3 0.8-0.9 0.5-1.5l-1-2.1C4.6-5.4 3.9-5.6 3.4-5.3z" fill="#f47e60" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(66 26)"> <path d="M-4.5-3.7L1.9-6l0.5-0.2L2-7.2l-6.9 2.5C-5.7-4.4-6.1-3.5-6-2.7c0 0.1 0 0.2 0.1 0.3l3 8.2 C-2.5 6.9-1.3 7.4-0.2 7l5.6-2C5.9 4.8 6.2 4.2 6 3.7L3.2-3.9l-0.4-1L2.4-4.7L1.9-4.5l-3.2 1.2l-2.7 1c-0.3 0.1-0.6 0-0.8-0.2 c-0.1-0.1-0.1-0.1-0.1-0.2C-5.1-3.1-4.9-3.6-4.5-3.7z" fill="#f8b26a" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(55 6)"> <polygon points="0 -4.9 1.6 -1.7 5.1 -1.1 2.6 1.3 3.2 4.9 0 3.2 -3.2 4.9 -2.6 1.3 -5.1 -1.1 -1.6 -1.7" fill="#abbd81" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </polygon> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> </g> <g clip-path="url(#ldio-ssf8off6o8l-cp)"> <g transform="translate(0 -75)"> <g> <g transform="translate(50 41)"> <path d="M6.5-6.7C6.1-6.9 5.7-7.2 5.3-7.4C5-7.5 4.6-7.7 4.3-7.8C3.1-2.2-4-3.7-2.9-9.3c-0.4 0-0.7 0-1.1 0 c-0.5 0-1 0.1-1.4 0.2c-1.8 0.3-3.6 0.9-5.3 1.8l1.1 4.2l3.1-0.8L-8.7 6.9L3.2 9.3L5.4-1.5l2.5 2l2.7-3.4C9.5-4.4 8.1-5.7 6.5-6.7z" fill="#e15b64" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(35 17)"> <path d="M3.4-5.3L2.5-5l0.8-2.3L1.1-6.3l-1.2-2.2l-1.6 4.6l-4.6-1.6l0.9 2.3l-2.2 1.2l2.3 0.8L-6-0.9 c-0.6 0.3-0.8 0.9-0.5 1.5l1 2.1C-5.2 3.4-4.6 3.6-4 3.3l0.1-0.1l2.1 4.5C-1.4 8.4-0.7 8.7 0 8.3l1.7-0.8l1.7-0.8L5 5.9l1.7-0.8 C7.4 4.8 7.7 4 7.4 3.3L5.2-1.1l0.1-0.1c0.6-0.3 0.8-0.9 0.5-1.5l-1-2.1C4.6-5.4 3.9-5.6 3.4-5.3z" fill="#f47e60" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(66 26)"> <path d="M-4.5-3.7L1.9-6l0.5-0.2L2-7.2l-6.9 2.5C-5.7-4.4-6.1-3.5-6-2.7c0 0.1 0 0.2 0.1 0.3l3 8.2 C-2.5 6.9-1.3 7.4-0.2 7l5.6-2C5.9 4.8 6.2 4.2 6 3.7L3.2-3.9l-0.4-1L2.4-4.7L1.9-4.5l-3.2 1.2l-2.7 1c-0.3 0.1-0.6 0-0.8-0.2 c-0.1-0.1-0.1-0.1-0.1-0.2C-5.1-3.1-4.9-3.6-4.5-3.7z" fill="#f8b26a" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </path> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> <g> <g transform="translate(55 6)"> <polygon points="0 -4.9 1.6 -1.7 5.1 -1.1 2.6 1.3 3.2 4.9 0 3.2 -3.2 4.9 -2.6 1.3 -5.1 -1.1 -1.6 -1.7" fill="#abbd81" > <animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="0.7462686567164178s" repeatCount="indefinite" ></animateTransform> </polygon> </g> <animateTransform attributeName="transform" type="translate" keyTimes="0;1" values="0 0;0 75" dur="1.4925373134328357s" repeatCount="indefinite" ></animateTransform> </g> </g> </g> </svg> <h3>加载中:{{ progress }}%</h3> </template> <script setup> let props = defineProps({ progress: Number, }); </script> <style scoped> h3 { position: fixed; top: 100px; left: 50%; font-size: 30px; font-weight: 900; } </style>
访问页面
<template> <Header></Header> <router-view></router-view> </template> <script setup> import Header from './components/Header.vue' import { useRoute, useRouter } from "vue-router"; // 获取路由信息 let route = useRoute(); // 执行路由的跳转 let router = useRouter(); </script> <style> </style>
完善Header.vue
设置布局
<template> <div class="header" > <div class="logo" @click="router.push('/')"> <img src="../assets/img/logo_rect.jpg" alt="logo" /> </div> <a-input-search v-model:value="data.value" placeholder="搜索商品或商品号" class="input-search" @search="onSearch" /> <a-menu v-model:selectedKeys="data.current" mode="horizontal"> <a-menu-item key="help"> <template #icon> <question-circle-outlined /> </template> 帮助 </a-menu-item> <a-menu-item key="salesroom"> <template #icon> <ShopOutlined /> </template> 门店 </a-menu-item> <a-sub-menu key="account"> <template #icon> <UserOutlined /> </template> <template #title>账户</template> </a-sub-menu> <a-sub-menu key="buycart"> <template #icon> <CarOutlined /> </template> <template #title>购物车</template> <a-menu-item-group title="购物商品"> <a-menu-item v-for="(item, i) in store.state.buycarts" :key="item.id"> <div class="prod-item"> <div class="left"> <img :src="item.imgsrc" :alt="item.title" /> </div> <div class="middle"> <div class="title">{{ item.title }}</div> <div class="content"> <span class="num">数量:{{ item.num }}</span> <div class="control"> <span class="btn" @click.stop="store.commit('addBuycartsNum', i)" >+</span > <span class="btn" @click.stop="store.commit('minusBuycartsNum', i)" >-</span > </div> </div> </div> <div class="right"> <div class="price">¥ {{ item.price * item.num }}</div> </div> </div> </a-menu-item> <a-menu-item key="totalPrice"> <div class="total"> <span>总价:</span> <span class="num"> ¥ {{ store.getters.totalPrice }}</span> </div> </a-menu-item> </a-menu-item-group> </a-sub-menu> </a-menu> </div> </template> <script setup> import { useRoute, useRouter } from "vue-router"; import { useStore } from "vuex"; import { reactive } from "vue"; import { ShopOutlined, UserOutlined, CarOutlined, QuestionCircleOutlined, } from "@ant-design/icons-vue"; const store = useStore(); const router = useRouter(); const data = reactive({ value: "", current: ["help"], }); const onSearch = () => {}; </script> <style scoped> .header{ display: flex; justify-content: space-between; align-content: center; position: fixed; z-index: 10000; width: 100%; } .logo{ margin-right: 40px; } </style>
完善样式
安装less、less-loader
# .scss and .sass
npm install -D sass
# .less
npm install less-loader less --save-dev
# .styl and .stylus
npm install -D stylus
<style lang="less" scoped> .header { display: flex; justify-content: space-between; align-items: center; position: fixed; z-index: 1000000; width: 100%; background-color: #fff; box-shadow: 0 0 10px #ccc; transition: all 0.5s; } .header.hidden { transform: translate(0, -100%); } .logo { margin-right: 40px; img { height: 30px; } } .input-search { display: flex; flex: 1; height: 40px; align-items: center; } </style>
<template> <div class="homeswiper"> <div class="swiperBg"> <a-carousel arrows autoplay> <template #prevArrow> <div class="custom-slick-arrow" style="left: 10px; z-index: 1"> <left-circle-outlined /> </div> </template> <template #nextArrow> <div class="custom-slick-arrow" style="right: 10px"> <right-circle-outlined /> </div> </template> <div><h3>1</h3></div> <div><h3>2</h3></div> <div><h3>3</h3></div> <div><h3>4</h3></div> </a-carousel> </div> </div> </template> <script setup> import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue'; </script> <!-- scoped 设置样式作用域仅仅在当前有效 :deep 控制当前组件内部组件样式 --> <style lang="less" scoped> /* For demo */ .ant-carousel :deep(.slick-slide) { text-align: center; height: 160px; line-height: 160px; background: #364d79; overflow: hidden; } .ant-carousel :deep(.slick-arrow.custom-slick-arrow) { width: 25px; height: 25px; font-size: 25px; color: #fff; background-color: rgba(31, 45, 61, 0.11); opacity: 0.3; z-index: 1; } .ant-carousel :deep(.custom-slick-arrow:before) { display: none; } .ant-carousel :deep(.custom-slick-arrow:hover) { opacity: 0.5; } .ant-carousel :deep(.slick-slide h3) { color: #fff; } </style>
在HomeCom首页当中引入改组件
<template> <div class="homepage"> <HomeSwiper></HomeSwiper> </div> </template> <script setup> import HomeSwiper from "./Home/HomeSwiper.vue" import * as api from '../api/index'; import {reactive} from "vue"; let result = await api.getHomepage(); const hero = reactive(result.hero); </script> <style scoped> </style>
设置倾斜
<template> <div class="homepage"> <HomeSwiper></HomeSwiper> </div> </template> <script setup> import HomeSwiper from "./Home/HomeSwiper.vue" import * as api from '../api/index'; import {reactive} from "vue"; let result = await api.getHomepage(); const hero = reactive(result.hero); </script> <style lang="less" scoped> .homepage{ padding-top: 46px; height: 575px; position: relative; } </style>
<template> <div class="homeswiper"> <div class="swiperBg"> <a-carousel arrows autoplay> <template #prevArrow> <div class="custom-slick-arrow" style="left: 10px; z-index: 1"> <left-circle-outlined /> </div> </template> <template #nextArrow> <div class="custom-slick-arrow" style="right: 10px"> <right-circle-outlined /> </div> </template> <div><h3>1</h3></div> <div><h3>2</h3></div> <div><h3>3</h3></div> <div><h3>4</h3></div> </a-carousel> </div> </div> </template> <script setup> import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue'; </script> <!-- scoped 设置样式作用域仅仅在当前有效 :deep 控制当前组件内部组件样式 --> <style lang="less" scoped> .swiperBg { position: absolute; display: block; width: 100%; height: 575px; background-color: #00d9ff; transform-origin: 0 0; transform: skew(0, -8deg); transition: all 0.5s; } .swiper-item { height: 575px; .swiper-box { display: flex; min-width: 1200px; justify-content: center; .swiper-left { width: 340px; padding-right: 100px; display: flex; flex-direction: column; align-items: flex-end; margin-top: 100px; text-align: right; } h3 { font-size: 30px; font-weight: 900; margin: 0; line-height: 40px; } h1 { font-size: 60px; font-weight: 900; margin: 0; line-height: 70px; } .banner-right { margin-top: 45px; transform-origin: 0 100%; transform: skew(0, -8deg); overflow: hidden; border-radius: 20px; border-bottom-right-radius: 80px; img { transform-origin: 0 100%; transform: skew(0, 8deg); border-radius: 20px; } } } } /* For demo */ .ant-carousel :deep(.slick-slide) { text-align: center; height: 575px; line-height: 160px; overflow: hidden; } .ant-carousel :deep(.slick-arrow.custom-slick-arrow) { width: 25px; height: 25px; font-size: 25px; color: #fff; background-color: rgba(31, 45, 61, 0.11); opacity: 0.3; z-index: 1; } .ant-carousel :deep(.custom-slick-arrow:before) { display: none; } .ant-carousel :deep(.custom-slick-arrow:hover) { opacity: 0.5; } .ant-carousel :deep(.slick-slide h3) { color: #fff; } </style>
在HomeCom.vue得到请求对应的数据,将数据设置到子组件当中:banner="data.banner"
<template> <div class="homepage"> <HomeSwiper :banner="data.banner"></HomeSwiper> </div> </template> <script setup> import HomeSwiper from "./Home/HomeSwiper.vue" import * as api from '../api/index'; import {reactive} from "vue"; let result = await api.getHomepage(); const data = reactive({ banner: result.banner }); </script> <style lang="less" scoped> .homepage{ padding-top: 46px; height: 575px; position: relative; } </style>
在HomeSwiper子组件当中获取父组件数据,实现走马灯切换图片
<script setup> import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue'; import {reactive,defineProps} from "vue"; const props = defineProps({banner:Array}) const data = reactive({ bgColor: props.banner[0].bg_color, }) console.log(props) function changeFn(from,to) { data.bgColor = props.banner[to].bg_color } </script>
<template> <div class="homeswiper"> <div class="swiperBg" :style="{ backgroundColor: data.bgColor }"></div> <a-carousel arrows autoplay :beforeChange="changeFn"> <template #prevArrow> <div class="custom-slick-arrow" style="left: 10px; z-index: 1"> <left-circle-outlined /> </div> </template> <template #nextArrow> <div class="custom-slick-arrow" style="right: 10px"> <right-circle-outlined /> </div> </template> <div class="swiper-item" v-for="(item, i) in props.banner"> <div class="swiper-box"> <div class="swiper-left"> <h3 :style="{ color: item.text_color }"> {{ item.desktop_sub_title || item.sub_title }} </h3> <h1 :style="{ color: item.text_color }"> {{ item.desktop_title || item.title }} </h1> </div> <div class="banner-right"> <img :src="`https://pixl.decathlon.com.cn/${item.picture_desktop}/banner.jpg`" :alt="item.title" /> </div> </div> </div> </a-carousel> </div> </template> <script setup> import { LeftCircleOutlined, RightCircleOutlined } from "@ant-design/icons-vue"; import { reactive } from "vue"; const props = defineProps({ banner: Array }); const data = reactive({ bgColor: props.banner[0].bg_color, }); function changeFn(from, to) { // console.log(to); data.bgColor = props.banner[to].bg_color; } </script> <style lang="less" scoped> .swiperBg { position: absolute; display: block; width: 100%; height: 575px; background-color: orangered; transform-origin: 0 0; transform: skew(0, -8deg); transition: all 0.5s; } .swiper-item { height: 575px; .swiper-box { display: flex; min-width: 1200px; justify-content: center; .swiper-left { width: 340px; padding-right: 100px; display: flex; flex-direction: column; align-items: flex-end; margin-top: 100px; text-align: right; } h3 { font-size: 30px; font-weight: 900; margin: 0; line-height: 40px; } h1 { font-size: 60px; font-weight: 900; margin: 0; line-height: 70px; } .banner-right { margin-top: 45px; transform-origin: 0 100%; transform: skew(0, -8deg); overflow: hidden; border-radius: 20px; border-bottom-right-radius: 80px; img { transform-origin: 0 100%; transform: skew(0, 8deg); border-radius: 20px; } } } } /* For demo */ .ant-carousel :deep(.slick-slide) { text-align: center; height: 575px; line-height: 160px; overflow: hidden; } .ant-carousel :deep(.slick-arrow.custom-slick-arrow) { width: 25px; height: 25px; font-size: 25px; color: #fff; background-color: rgba(31, 45, 61, 0.11); opacity: 0.3; z-index: 1; } .ant-carousel :deep(.custom-slick-arrow:before) { display: none; } .ant-carousel :deep(.custom-slick-arrow:hover) { opacity: 0.5; } .ant-carousel :deep(.slick-slide h3) { color: #fff; } </style>
<template> <div class="homepage"> <HomeSwiper :banner="data.banner"></HomeSwiper> <div class="live"> <h1>生活类别100+</h1> <div class="live-list"> <div class="live-item" v-for="index in 10" v-if="data.sports.length !== 0" > <div class="live-btn"> <img :src="data.sports[index].menuThumbnail" :alt="data.sports[index].displayName" /> <h3>{{ data.sports[index].displayName }}</h3> </div> </div> </div> <a-button type="primary" size="large" @click="router.push('/product')" >立即享受生活</a-button > </div> </div> </template> <script setup> import HomeSwiper from "./Home/HomeSwiper.vue"; import * as api from "../api/index"; import { reactive } from "vue"; import { useRouter } from "vue-router"; let result = await api.getHomepage(); const router = useRouter(); const data = reactive({ banner: result.banner, sports: result.sports }); console.log(result); </script> <style lang="less" scoped> .homepage { padding-top: 46px; height: 575px; position: relative; } .live { width: 1200px; margin: 40px auto; display: flex; flex-direction: column; align-items: center; justify-content: center; padding-bottom: 40px; h1 { font-size: 30px; } .live-list { display: flex; flex-wrap: wrap; .live-item { width: 240px; } .live-btn { display: flex; height: 73px; width: 200px; background-color: #fff; border-radius: 8px 8px 25px 8px; margin-bottom: 30px; box-shadow: 5px 5px 5px #ccc; align-items: center; justify-content: center; position: relative; img { position: absolute; width: 80px; height: 80px; left: -15px; top: -15px; } h3 { font-weight: 900; font-size: 20px; } } } } </style>
刷新页面
设置请求的接口
export const getProducts = () => {
return request({
method: "GET",
url: "/products",
});
};
在store当中设置对应状态管理
isFullscreen:false
getFullScreen(state,payload){
state.isFullscreen = payload;
}
完善Product.vue
实现状态栏隐藏
鼠标滚动
完善样式
全部代码
<template> <h1>Product</h1> <h1>产品id是{{ route.params.id }}</h1> <div class="loading" v-show="data.isLoading"> <Loading></Loading> </div> <div class="product" v-show="!data.isLoading"> <div class="prod-list" :class="{hidden:store.state.isFullscreen}"> <h1> <SketchOutlined>产品推荐</SketchOutlined> </h1> <div class="products"> <div class="prod-item" v-for="(prod,pI) in data.products"> <div class="prod-title"> {{ prod.title }} </div> <div class="img"> <img :src="prod.imgsrc" :alt="prod.title"> </div> <a-button type="primary" block > <template #icon> <ShoppingCartOutlined></ShoppingCartOutlined> </template> 加入购物车 </a-button> </div> </div> <div class="products"> </div> </div> <div class="scene-list" :class="{hidden:store.state.isFullscreen}"> <h3><RadarChartOutlined>切换使用场景</RadarChartOutlined> </h3> </div> </div> </template> <script setup> import Loading from "../components/Loading.vue"; import { SketchOutlined, RadarChartOutlined, ShoppingCartOutlined, } from "@ant-design/icons-vue"; import { useRoute } from 'vue-router' import {onMounted, reactive} from 'vue' import * as api from '../api/index.js' import {useStore} from 'vuex' const route = useRoute(); const store = useStore(); console.log(route) const data = reactive({ products :[], isLoading:true, scenes:[] }) //生命周期函数x onMounted(async () => { //await 等待数据返回结果 ,使用await 的同时 必须使用async let result = await api.getProducts() console.log(result); data.isLoading = false; data.products = result.list; data.scenes = result.hdr; }) window.addEventListener('mousewheel' ,(e)=>{ // console.log(e) if(e.deltaY > 0){ store.commit("getFullScreen",true) } if(e.deltaY < 0){ store.commit("getFullScreen",false) } }) </script> <style lang="less" scoped> .desc { position: fixed; z-index: 100000; background-color: rgba(255, 255, 255, 0.5); width: 600px; top: 100px; left: 50%; margin-left: -300px; transition: all 0.5s; transform: translate(-100vw, 0); padding: 15px; } .desc.active { transform: translate(0, 0); } .prod-list { width: 300px; height: 100vh; padding: 60px 0 0; position: fixed; z-index: 100000; transition: all 0.5s; background-color: rgb(208, 255, 0); left: 0; top: 0; h1 { font-size: 20px; font-weight: 900; padding: 10px 25px 0; } .products { display: flex; flex-direction: column; justify-content: center; align-items: center; .prod-item { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 250px; background-color: #fff; border-radius: 20px; overflow: hidden; margin: 10px 0; box-shadow: 2px 2px 5px #666; transition: all 0.3s; &.active { box-shadow: 2px 2px 5px #666, 0px 0px 10px red; } &:hover { transform: translate(0px, -5px); box-shadow: 2px 2px 5px #666, 0px 0px 10px orangered; // background-color: orange; } img { width: 190px; } .prod-title { padding: 0 20px; } } } } .prod-list.hidden { transform: translate(-100%, 0); } .scene-list { width: 300px; height: 100vh; padding: 60px 0 0; position: fixed; z-index: 100000; transition: all 0.5s; background-color: rgba(182, 73, 73, 0.8); right: 0; top: 0; h3 { font-size: 20px; font-weight: 900; padding: 0 30px; } .scenes { display: flex; flex-direction: column; justify-content: center; align-items: center; } .scene-item { padding: 6px 0; img { width: 250px; border-radius: 10px; box-shadow: 2px 2px 10px #666; transition: all 0.3s; &.active { box-shadow: 2px 2px 5px #666, 0px 0px 10px red; } &:hover { transform: translate(0px, -5px); box-shadow: 2px 2px 5px #666, 0px 0px 10px orangered; } } } } .scene-list.hidden { transform: translate(100%, 0); } </style>
继续完善列表功能
<div class="scenes">
<div class="scene-item" v-for="(scene,index) in data.scenes">
<img :src="`./files/hdr/${scene}.jpg`" :alt="scene">
</div>
</div>
设置点以及其他功能开发
pIndex:0,
sceneIndex: 0
实现点击选择
防止手残全部代码
<template> <h1>Product</h1> <h1>产品id是{{ route.params.id }}</h1> <div class="loading" v-show="data.isLoading"> <Loading></Loading> </div> <div class="product" v-show="!data.isLoading"> <div class="prod-list" :class="{hidden:store.state.isFullscreen}"> <h1> <SketchOutlined>产品推荐</SketchOutlined> </h1> <div class="products"> <div class="prod-item" :class="{ active : pI == data.pIndex }" v-for="(prod,pI) in data.products" @click="changeModel(prod,pI)"> <div class="prod-title"> {{ prod.title }} </div> <div class="img"> <img :src="prod.imgsrc" :alt="prod.title"> </div> <a-button type="primary" block > <template #icon> <ShoppingCartOutlined></ShoppingCartOutlined> </template> 加入购物车 </a-button> </div> </div> <div class="products"> </div> </div> <div class="scene-list" :class="{hidden:store.state.isFullscreen}"> <h3><RadarChartOutlined>切换使用场景</RadarChartOutlined> </h3> <div class="scenes"> <div class="scene-item" v-for="(scene,index) in data.scenes" @click="changeHdr(scene,index)"> <img :class="{ active : index == data.sceneIndex }" :src="`./files/hdr/${scene}.jpg`" :alt="scene"> </div> </div> </div> </div> </template> <script setup> import Loading from "../components/Loading.vue"; import { SketchOutlined, RadarChartOutlined, ShoppingCartOutlined, } from "@ant-design/icons-vue"; import { useRoute } from 'vue-router' import {onMounted, reactive} from 'vue' import * as api from '../api/index.js' import {useStore} from 'vuex' const route = useRoute(); const store = useStore(); console.log(route) const data = reactive({ products :[], isLoading:true, scenes:[], pIndex:0, sceneIndex: 0 }) //生命周期函数x onMounted(async () => { //await 等待数据返回结果 ,使用await 的同时 必须使用async let result = await api.getProducts() console.log(result); data.isLoading = false; data.products = result.list; data.scenes = result.hdr; }) function changeModel(prod,pI){ data.pIndex = pI; } function changeHdr(scene,index){ data.sceneIndex = index; } window.addEventListener('mousewheel' ,(e)=>{ // console.log(e) if(e.deltaY > 0){ store.commit("getFullScreen",true) } if(e.deltaY < 0){ store.commit("getFullScreen",false) } }) </script> <style lang="less" scoped> .desc { position: fixed; z-index: 100000; background-color: rgba(255, 255, 255, 0.5); width: 600px; top: 100px; left: 50%; margin-left: -300px; transition: all 0.5s; transform: translate(-100vw, 0); padding: 15px; } .desc.active { transform: translate(0, 0); } .prod-list { width: 300px; height: 100vh; padding: 60px 0 0; position: fixed; z-index: 100000; transition: all 0.5s; background-color: rgba(255, 255, 255, 0.8); left: 0; top: 0; h1 { font-size: 20px; font-weight: 900; padding: 10px 25px 0; } .products { display: flex; flex-direction: column; justify-content: center; align-items: center; .prod-item { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 250px; background-color: #fff; border-radius: 20px; overflow: hidden; margin: 10px 0; box-shadow: 2px 2px 5px #666; transition: all 0.3s; &.active { box-shadow: 2px 2px 5px #666, 0px 0px 10px red; } &:hover { transform: translate(0px, -5px); box-shadow: 2px 2px 5px #666, 0px 0px 10px orangered; // background-color: orange; } img { width: 190px; } .prod-title { padding: 0 20px; } } } } .prod-list.hidden { transform: translate(-100%, 0); } .scene-list { width: 300px; height: 100vh; padding: 60px 0 0; position: fixed; z-index: 100000; transition: all 0.5s; background-color: rgba(255, 255, 255, 0.8); right: 0; top: 0; h3 { font-size: 20px; font-weight: 900; padding: 0 30px; } .scenes { display: flex; flex-direction: column; justify-content: center; align-items: center; } .scene-item { padding: 6px 0; img { width: 250px; border-radius: 10px; box-shadow: 2px 2px 10px #666; transition: all 0.3s; &.active { box-shadow: 2px 2px 5px #666, 0px 0px 10px red; } &:hover { transform: translate(0px, -5px); box-shadow: 2px 2px 5px #666, 0px 0px 10px orangered; } } } } .scene-list.hidden { transform: translate(100%, 0); } </style>
buycarts:[]
addBuycarts(state,payload){
state.buycarts.push(payload)
},
addBuycartsNum(state,payload){
state.buycarts[payload].num++
},
minusBuycartsNum(state,payload){
state.buycarts[payload].num--;
if(state.buycarts[payload].num == 0){
state.buycarts.splice(payload,1);//删除对应的内容
}
}
getters:{
totalPrice(state){
let total = state.buycarts.reduce((pre,item) =>{ //reduce数组进行求和
return pre + item.price * item.num;
},0);
return total;
},
},
设置加入购物车
<a-button type="primary" block @click.stop="addBuycart(prod)">
<template #icon>
<ShoppingCartOutlined></ShoppingCartOutlined>
</template>
加入购物车
</a-button>
function addBuycart(prod){
let product = {...prod,num:1};
store.commit('addBuycarts',product)
}
完善购物车样式
<style> .ant-menu-submenu { z-index: 1000000000 !important; } body .ant-menu-sub .ant-menu-item { height: auto; } .ant-menu-sub .ant-menu-item .prod-item { display: flex; width: 400px; justify-content: space-between; } .ant-menu .prod-item img { width: 100px; height: 100px; } .ant-menu-item .prod-item .middle { width: 220px; height: 100px; display: flex; flex-direction: column; justify-content: space-between; } .ant-menu-item .prod-item .middle .title { font-size: 16px; font-weight: 900; } .ant-menu-item .prod-item .middle .content { display: flex; justify-content: space-between; } .ant-menu-item .prod-item .middle .btn { display: inline-block; width: 30px; height: 30px; text-align: center; line-height: 30px; border: 1px solid #ccc; margin: 0 5px; } .ant-menu-item .prod-item .right .price { font-weight: 900; color: orange; font-size: 16px; display: flex; justify-content: center; align-items: center; } .ant-menu-item .total { font-weight: 900; display: flex; justify-content: flex-end; font-size: 16px; } .ant-menu-item .total .num { color: orange; padding-right: 20px; } </style>
访问:http://127.0.0.1:5173/#/product/212121212
添加商品
完善功能实现重复商品添加购物车(累加数量)
function addBuycart(prod){ let product = {...prod,num:1}; let index = 0; let isExist = store.state.buycarts.some((item,i)=>{ if(product.id == item.id){ index = i return true }else { return false } }) if(isExist){ store.commit('addBuycartsNum',index) }else { store.commit('addBuycarts',product) } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。