商家詳情頁開發
在開發之前準備好mock接口,返回mock如下:
{
code: 200,
data: [
{
id: '1',
name: '某什麼瑪1',
imgUrl: '/i18n/9_16/img/near.png',
sales: 1000,
expressLimit: 0,
expressPrice: 5,
slogon: 'VIP尊享xx元減x元運費券(每月x張)'
},
{
id: '1',
name: '某什麼瑪2',
imgUrl: '/i18n/9_16/img/near.png',
sales: 2000,
expressLimit: 0,
expressPrice: 5,
slogon: 'VIP尊享xx元減x元運費券(每月x張)'
},
{
id: '1',
name: '某什麼瑪3',
imgUrl: '/i18n/9_16/img/near.png',
sales: 200,
expressLimit: 0,
expressPrice: 5,
slogon: 'VIP尊享xx元減x元運費券(每月x張)'
},
{
id: '1',
name: '某什麼瑪4',
imgUrl: '/i18n/9_16/img/near.png',
sales: 100,
expressLimit: 0,
expressPrice: 5,
slogon: 'VIP尊享xx元減x元運費券(每月x張)'
}
],
desc: '成功'
}
然後優化一下axios封裝工具類src\utils\request.js
:
import axios from 'axios'
const baseURL = 'https://www.fastmock.site/mock/xxxxxx/mock'
const timeout = 10000
/** axios初始化實例 */
const instance = axios.create({
baseURL: baseURL,
timeout: timeout
})
/** 封裝的axios get請求方法 */
export const get = (url, params = {}) => {
return new Promise((resolve, reject) => {
instance.get(url, {
params
}).then((res) => {
resolve(res.data)
}, err => {
reject(err)
})
})
}
/** 封裝的axios post請求方法 */
export const post = (url, data = {}) => {
return new Promise((resolve, reject) => {
instance.post(url, data, {
headers: {
'Content-Tpye': 'application/json'
}
}).then((res) => {
resolve(res.data)
}, err => {
reject(err)
})
})
}
修改src\views\home\Nearby.vue
:
<template>
<!-- 主體商鋪展示內容 -->
<div class="nearby">
<h3 class="nearby__title">附近店鋪</h3>
<!-- 5個 -->
<div class="nearby__item" v-for="(item, index) in nearbyList" :key="index">
<img :src="item.headImg" class="nearby__item__img" />
<div class="nearby__item__content">
<div class="nearby__item__content__title">{{ item.title }}</div>
<div class="nearby__item__content__tags">
<span class="nearby__item__content__tag">月售:{{ item.sales }}</span>
<span class="nearby__item__content__tag"
>起送:¥{{ item.expressLimit }}</span
>
<span class="nearby__item__content__tag"
>基礎運費:¥{{ item.expressPrice }}</span
>
</div>
<p class="nearby__item__content__highlight">
{{ item.highlight }}
</p>
</div>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
// import { useRouter } from 'vue-router'
import { get } from '@/utils/request'
const useNearbyListEffect = () => {
const nearbyList = ref([])
const getNearbyList = async () => {
try {
const resultData = await get('/api/user/hot_list')
if (resultData?.code === 200 && resultData?.data?.length) {
resultData.data.forEach(item => {
nearbyList.value.push({
id: item?.id,
title: item?.name,
headImg: item?.imgUrl,
sales: item?.sales,
expressLimit: item?.expressLimit,
expressPrice: item?.expressPrice,
highlight: item?.slogon
})
})
} else {
console.log('暫無數據')
}
} catch (e) {
console.log('數據請求失敗')
}
}
return { nearbyList, getNearbyList }
}
export default {
name: 'Nearby',
setup () {
const { nearbyList, getNearbyList } = useNearbyListEffect()
getNearbyList()
return {
nearbyList
}
}
}
</script>
<style lang="scss" scoped>
@import '@/style/viriables';
@import '@/style/mixins';
.nearby {
&__title {
margin: 0.16rem 0 0.02rem 0;
font-size: 0.18rem;
color: $content-font-color;
font-weight: normal; //不加粗展示
}
}
</style>
遠程數據加載完畢。
商家詳情界面開發
1.0 創建商檢詳情頁面的路由
src\router\index.js
:
import {
createRouter,
createWebHistory
} from 'vue-router'
const routes = [{
path: '/',
name: 'Home',
component: () => import(/* webpackChunkName: "home" */ '../views/home/Home.vue')
}, {
path: '/login',
name: 'Login',
component: () => import(/* webpackChunkName: "login" */ '../views/login/Login.vue'),
beforeEnter: (to, from, next) => {
// 只有訪問Login頁面之前纔會執行次函數
const {
isLogin
} = localStorage // 從本地存儲中取isLogin
// 如果登錄,就跳到首頁頁面;否則跳轉到登錄頁面
isLogin
? next({
name: 'Home'
})
: next()
}
},
{
path: '/register',
name: 'Register',
component: () => import(/* webpackChunkName: "register" */ '../views/register/Register.vue'),
beforeEnter: (to, from, next) => {
const {
isLogin
} = localStorage
isLogin
? next({
name: 'Home'
})
: next()
}
}, {
path: '/shop',
name: 'Shop',
component: () => import(/* webpackChunkName: "shop" */ '../views/shop/Shop.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
// beforeEach:全局,每次做路由跳轉之前都會執行這個操作。
router.beforeEach((to, from, next) => {
// to and from are Route Object,
// to:跳轉的時候想要跳轉的頁面的信息
// from :指從哪裏跳過來的信息
// next() must be called to resolve the hook}
// 中間件繼續執行的方法
// 從本地存儲中取isLogin
const {
isLogin
} = localStorage
console.log(to, from)
/** 判斷是否登錄 */
// 必須雙循環,才能防止死循環
// 如果沒有登錄,就跳到登錄頁面
const {
name
} = to
const
isLoginOrRegister = (name === 'Login' || name === 'Register');
(isLogin || isLoginOrRegister) ? next() : next({
name: 'Login'
})
})
export default router
將src\views\home\Nearby.vue
中店鋪的信息摘離成組件,因爲要在商店詳情頁面中使用。
新建src\components\ShopInfo\ShopInfo.vue
:
<template>
<!-- v-for="(item, index) in nearbyList" :key="index" -->
<div class="shop">
<img :src="item.headImg" class="shop__img" />
<div class="shop__content">
<div class="shop__content__title">{{ item.title }}</div>
<div class="shop__content__tags">
<span class="shop__content__tag">月售:{{ item.sales }}</span>
<span class="shop__content__tag">起送:¥{{ item.expressLimit }}</span>
<span class="shop__content__tag"
>基礎運費:¥{{ item.expressPrice }}</span
>
</div>
<p class="shop__content__highlight">
{{ item.highlight }}
</p>
</div>
</div>
</template>
<script>
export default {
name: 'ShopInfo',
props: ['item']
}
</script>
<style lang="scss" scoped>
@import '@/style/viriables';
@import '@/style/mixins';
.shop {
display: flex;
padding-top: 0.12rem;
// 圖片
&__img {
margin-right: 0.16rem;
width: 0.56rem;
height: 0.56rem;
}
//右側文字內容
&__content {
padding-bottom: 0.12rem;
border-bottom: 1px solid $content-bg-color;
flex: 1;
&__title {
line-height: 0.22rem;
font-size: 0.16rem;
color: $content-font-color;
}
&__tags {
margin-top: 0.08rem;
line-height: 0.18rem;
font-size: 0.13rem;
color: $content-font-color;
}
&__tag {
margin-right: 0.16rem;
}
&__highlight {
color: #e93b3b;
line-height: 0.18rem;
font-size: 0.13rem;
margin: 0.08rem 0 0 0;
}
}
}
</style>
調整src\views\home\Nearby.vue
:
<template>
<!-- 主體商鋪展示內容 -->
<div class="nearby">
<h3 class="nearby__title">附近店鋪</h3>
<!-- 主體商鋪展示內容 -->
<ShopInfo v-for="(item, index) in nearbyList" :key="index" :item="item" />
</div>
</template>
<script>
import { ref } from 'vue'
// import { useRouter } from 'vue-router'
import { get } from '@/utils/request'
import ShopInfo from '@/components/ShopInfo/ShopInfo.vue'
const useNearbyListEffect = () => {
const nearbyList = ref([])
const getNearbyList = async () => {
try {
const resultData = await get('/api/user/hot_list')
if (resultData?.code === 200 && resultData?.data?.length) {
resultData.data.forEach(item => {
nearbyList.value.push({
id: item?.id,
title: item?.name,
headImg: item?.imgUrl,
sales: item?.sales,
expressLimit: item?.expressLimit,
expressPrice: item?.expressPrice,
highlight: item?.slogon
})
})
} else {
console.log('暫無數據')
}
} catch (e) {
console.log('數據請求失敗')
}
}
return { nearbyList, getNearbyList }
}
export default {
name: 'Nearby',
components: { ShopInfo },
setup () {
const { nearbyList, getNearbyList } = useNearbyListEffect()
getNearbyList()
return {
nearbyList
}
}
}
</script>
商家詳情頁修改src\views\shop\Shop.vue
:
<template>
<div class="wrapper">
<ShopInfo :item="item" :hideBorder="true" />
</div>
</template>
<script>
import ShopInfo from '@/components/ShopInfo/ShopInfo.vue'
export default {
name: 'Shop',
components: { ShopInfo },
setup () {
const item = ''
return { item }
}
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 0 0.18rem;
}
</style>
爲了控制下邊的border劃線,需要修改一下組件src\components\ShopInfo\ShopInfo.vue
:
<template>
<!-- v-for="(item, index) in nearbyList" :key="index" -->
<div class="shop">
<img :src="item.headImg" class="shop__img" />
<div
:class="{
shop__content: true,
'shop__content--bordered': hideBorder ? false : true
}"
>
<div class="shop__content__title">{{ item.title }}</div>
<div class="shop__content__tags">
<span class="shop__content__tag">月售:{{ item.sales }}</span>
<span class="shop__content__tag">起送:¥{{ item.expressLimit }}</span>
<span class="shop__content__tag"
>基礎運費:¥{{ item.expressPrice }}</span
>
</div>
<p class="shop__content__highlight">
{{ item.highlight }}
</p>
</div>
</div>
</template>
<script>
export default {
name: 'ShopInfo',
props: ['item', 'hideBorder']
}
</script>
<style lang="scss" scoped>
@import '@/style/viriables';
@import '@/style/mixins';
.shop {
display: flex;
padding-top: 0.12rem;
// 圖片
&__img {
margin-right: 0.16rem;
width: 0.56rem;
height: 0.56rem;
}
//右側文字內容
&__content {
padding-bottom: 0.12rem;
&--bordered {
border-bottom: 1px solid $content-bg-color;
}
flex: 1;
&__title {
line-height: 0.22rem;
font-size: 0.16rem;
color: $content-font-color;
}
&__tags {
margin-top: 0.08rem;
line-height: 0.18rem;
font-size: 0.13rem;
color: $content-font-color;
}
&__tag {
margin-right: 0.16rem;
}
&__highlight {
color: #e93b3b;
line-height: 0.18rem;
font-size: 0.13rem;
margin: 0.08rem 0 0 0;
}
}
}
</style>
準備下圖標:
src\views\shop\Shop.vue
:
<template>
<div class="wrapper">
<div class="search">
<div class="search__back">
<i class="custom-icon custom-icon-back"></i>
</div>
<div class="search__content">
<span class="search__content__icon"
><i class="custom-icon custom-icon-search"></i
></span>
<input class="search__content__input" />
</div>
</div>
<ShopInfo :item="item" :hideBorder="true" />
</div>
</template>
<script>
import ShopInfo from '@/components/ShopInfo/ShopInfo.vue'
export default {
name: 'Shop',
components: { ShopInfo },
setup () {
const item = {
id: '1',
title: '某什麼瑪1',
headImg: '/i18n/9_16/img/near.png',
sales: 1000,
expressLimit: 0,
expressPrice: 5,
highlight: 'VIP尊享xx元減x元運費券(每月x張)'
}
return { item }
}
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 0 0.18rem;
}
.search {
margin: 0.2rem 0 0.16rem 0;
display: flex;
&__back {
width: 0.3rem;
height: 0.32rem; //高度會將父元素撐開
}
&__content {
display: flex;
flex: 1;
background: #f5f5f5;
border-radius: 0.16rem;
&__icon {
width: 0.44rem;
height: 0.32rem;
}
&__input {
padding-right: 0.2rem;
width: 100%;
display: block;
border: none;
outline: none;
background: none;
height: 0.32rem;
}
}
}
</style>
調整,主要是設置行高,並把字體大小放大:
<template>
<div class="wrapper">
<div class="search">
<div class="search__back">
<i class="search__back__icon custom-icon custom-icon-back"></i>
</div>
......
</div>
<ShopInfo :item="item" :hideBorder="true" />
</div>
</template>
<script>
......
</script>
<style lang="scss" scoped>
@import '@/style/viriables';
.wrapper {
padding: 0 0.18rem;
}
.search {
margin: 0.2rem 0 0.16rem 0;
display: flex;
&__back {
width: 0.3rem;
line-height: 0.32rem; //高度會將父元素撐開
&__icon {
font-size: 0.2rem;
color: #b6b6b6;
}
}
......
</style>
進一步優化:
<template>
<div class="wrapper">
<div class="search">
<div class="search__back">
<i class="search__back__icon custom-icon custom-icon-back"></i>
</div>
<div class="search__content">
<span
><i class="search__content__icon custom-icon custom-icon-search"></i
></span>
<input class="search__content__input" placeholder="請輸入商品名稱" />
</div>
</div>
<ShopInfo :item="item" :hideBorder="true" />
</div>
</template>
<script>
import ShopInfo from '@/components/ShopInfo/ShopInfo.vue'
export default {
name: 'Shop',
components: { ShopInfo },
setup () {
const item = {
id: '1',
title: '某什麼瑪1',
headImg: '/i18n/9_16/img/near.png',
sales: 1000,
expressLimit: 0,
expressPrice: 5,
highlight: 'VIP尊享xx元減x元運費券(每月x張)'
}
return { item }
}
}
</script>
<style lang="scss" scoped>
@import '@/style/viriables';
.wrapper {
padding: 0 0.18rem;
}
.search {
margin: 0.2rem 0 0.16rem 0;
display: flex;
line-height: 0.32rem; //高度會將父元素撐開
&__back {
width: 0.3rem;
&__icon {
font-size: 0.2rem;
color: #b6b6b6;
}
}
&__content {
display: flex;
flex: 1;
background: #f5f5f5;
border-radius: 0.16rem;
&__icon {
padding-left: 0.1rem;
padding-right: 0.1rem;
width: 0.44rem;
text-align: center;
color: #b7b7b7;
}
&__input {
padding-right: 0.2rem;
width: 100%;
display: block;
border: none;
outline: none;
background: none;
height: 0.32rem;
font-size: 0.14rem;
&::placeholder {
color: #333;
}
}
}
}
</style>
最終效果如下:
增加點擊返回事件:
<template>
<div class="wrapper">
<div class="search">
<div class="search__back" @click="handleBackClick">
<i class="search__back__icon custom-icon custom-icon-back"></i>
</div>
<div class="search__content">
<span
><i class="search__content__icon custom-icon custom-icon-search"></i
></span>
<input class="search__content__input" placeholder="請輸入商品名稱" />
</div>
</div>
<ShopInfo :item="item" :hideBorder="true" />
</div>
</template>
<script>
// 路由跳轉方法
import { useRouter } from 'vue-router'
import ShopInfo from '@/components/ShopInfo/ShopInfo.vue'
export default {
name: 'Shop',
components: { ShopInfo },
setup () {
const router = useRouter()
const item = {
id: '1',
title: '某什麼瑪1',
headImg: '/i18n/9_16/img/near.png',
sales: 1000,
expressLimit: 0,
expressPrice: 5,
highlight: 'VIP尊享xx元減x元運費券(每月x張)'
}
const handleBackClick = () => {
router.back()
}
return { item, handleBackClick }
}
}
</script>
在Home首頁,點擊某個店鋪跳到詳情頁,可以如下編寫代碼:
src\views\home\Nearby.vue
:
<template>
<!-- 主體商鋪展示內容 -->
<div class="nearby">
<h3 class="nearby__title">附近店鋪</h3>
<!-- router-link必須有to的屬性 -->
<router-link to="/shop" v-for="(item, index) in nearbyList" :key="index">
<!-- 主體商鋪展示內容 -->
<ShopInfo :item="item" />
</router-link>
</div>
</template>
<script>
import { ref } from 'vue'
// import { useRouter } from 'vue-router'
import { get } from '@/utils/request'
import ShopInfo from '@/components/ShopInfo/ShopInfo.vue'
const useNearbyListEffect = () => {
const nearbyList = ref([])
const getNearbyList = async () => {
try {
const resultData = await get('/api/user/hot_list')
if (resultData?.code === 200 && resultData?.data?.length) {
resultData.data.forEach(item => {
nearbyList.value.push({
id: item?.id,
title: item?.name,
headImg: item?.imgUrl,
sales: item?.sales,
expressLimit: item?.expressLimit,
expressPrice: item?.expressPrice,
highlight: item?.slogon
})
})
} else {
console.log('暫無數據')
}
} catch (e) {
console.log('數據請求失敗')
}
}
return { nearbyList, getNearbyList }
}
export default {
name: 'Nearby',
components: { ShopInfo },
setup () {
const { nearbyList, getNearbyList } = useNearbyListEffect()
getNearbyList()
return {
nearbyList
}
}
}
</script>
<style lang="scss" scoped>
@import '@/style/viriables';
@import '@/style/mixins';
.nearby {
&__title {
margin: 0.16rem 0 0.02rem 0;
font-size: 0.18rem;
color: $content-font-color;
font-weight: normal; //不加粗展示
}
}
</style>
這時候發現每個文字下都有下劃線,路由的to屬性會在最外層包裹一個a標籤,解決方案如下:
src\views\home\Nearby.vue
<template>
<!-- 主體商鋪展示內容 -->
<div class="nearby">
<h3 class="nearby__title">附近店鋪</h3>
<!-- router-link必須有to的屬性 -->
<router-link to="/shop" v-for="(item, index) in nearbyList" :key="index">
<!-- 主體商鋪展示內容 -->
<ShopInfo :item="item" />
</router-link>
</div>
</template>
<script>
import { ref } from 'vue'
// import { useRouter } from 'vue-router'
import { get } from '@/utils/request'
import ShopInfo from '@/components/ShopInfo/ShopInfo.vue'
const useNearbyListEffect = () => {
const nearbyList = ref([])
const getNearbyList = async () => {
try {
const resultData = await get('/api/user/hot_list')
if (resultData?.code === 200 && resultData?.data?.length) {
resultData.data.forEach(item => {
nearbyList.value.push({
id: item?.id,
title: item?.name,
headImg: item?.imgUrl,
sales: item?.sales,
expressLimit: item?.expressLimit,
expressPrice: item?.expressPrice,
highlight: item?.slogon
})
})
} else {
console.log('暫無數據')
}
} catch (e) {
console.log('數據請求失敗')
}
}
return { nearbyList, getNearbyList }
}
export default {
name: 'Nearby',
components: { ShopInfo },
setup () {
const { nearbyList, getNearbyList } = useNearbyListEffect()
getNearbyList()
return {
nearbyList
}
}
}
</script>
<style lang="scss" scoped>
@import '@/style/viriables';
@import '@/style/mixins';
.nearby {
&__title {
margin: 0.16rem 0 0.02rem 0;
font-size: 0.18rem;
color: $content-font-color;
font-weight: normal; //不加粗展示
}
a {
text-decoration: none;
}
}
</style>
這裏繼續摘離2種顏色:
修改src\style\viriables.scss
:
/**
* 內容主體文字顏色
**/
$content-font-color: #333;
/**
* 無內容、背景灰、留白灰的顏色
**/
$content-bg-color: #f1f1f1;
/**
* 文字灰色字體
*
**/
$centent-notice-fontcolor: #777;
/**
* 搜索框的背景色
**/
$search-bg-color: #f5f5f5;
/**
* 搜索框內文字顏色
**/
$search-font-color: #b7b7b7;
修改用到的2個地方:
src\views\home\StaticPart.vue
:
......
.search {
margin-bottom: 0.12rem;
line-height: 0.32rem; //行高:將會自動撐開
background: $search-bg-color;
color: $search-font-color;
border-radius: 0.16rem;
font-size: 0.14rem;
&__icon {
display: inline-block;
padding: 0 0.05rem 0 0.16rem;
font-size: 0.15rem;
}
&__text {
display: inline-block;
font-size: 0.14rem;
}
}
......
src\views\shop\Shop.vue
......
&__content {
display: flex;
flex: 1;
background: $search-bg-color;
border-radius: 0.16rem;
&__icon {
padding-left: 0.1rem;
padding-right: 0.1rem;
width: 0.44rem;
text-align: center;
color: $search-font-color;
}
&__input {
padding-right: 0.2rem;
width: 100%;
display: block;
border: none;
outline: none;
background: none;
height: 0.32rem;
font-size: 0.14rem;
color: $content-font-color;
&::placeholder {
color: $content-font-color;
}
}
}
......