feat: 下单完成(下一步:订单列表)
This commit is contained in:
parent
5f446bc97a
commit
25f9813687
10
.vscode/extensions.json
vendored
10
.vscode/extensions.json
vendored
@ -3,6 +3,14 @@
|
|||||||
"Vue.volar",
|
"Vue.volar",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"EditorConfig.EditorConfig",
|
"EditorConfig.EditorConfig",
|
||||||
"esbenp.prettier-vscode"
|
"esbenp.prettier-vscode",
|
||||||
|
"zhuangtongfa.Material-theme",
|
||||||
|
"PKief.material-icon-theme",
|
||||||
|
"ms-vscode.vscode-typescript-next",
|
||||||
|
"pranaygp.vscode-css-peek",
|
||||||
|
"ecmel.vscode-html-css",
|
||||||
|
"formulahendry.auto-rename-tag",
|
||||||
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"unocss.unocss"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
11
components.d.ts
vendored
11
components.d.ts
vendored
@ -10,15 +10,22 @@ declare module 'vue' {
|
|||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
VanArea: typeof import('vant/es')['Area']
|
||||||
VanButton: typeof import('vant/es')['Button']
|
VanButton: typeof import('vant/es')['Button']
|
||||||
VanCell: typeof import('vant/es')['Cell']
|
VanCell: typeof import('vant/es')['Cell']
|
||||||
VanCellGroup: typeof import('vant/es')['CellGroup']
|
VanCellGroup: typeof import('vant/es')['CellGroup']
|
||||||
VanField: typeof import('vant/es')['Field']
|
VanField: typeof import('vant/es')['Field']
|
||||||
|
VanForm: typeof import('vant/es')['Form']
|
||||||
VanIcon: typeof import('vant/es')['Icon']
|
VanIcon: typeof import('vant/es')['Icon']
|
||||||
VanRate: typeof import('vant/es')['Rate']
|
VanNoticeBar: typeof import('vant/es')['NoticeBar']
|
||||||
|
VanPopover: typeof import('vant/es')['Popover']
|
||||||
|
VanPopup: typeof import('vant/es')['Popup']
|
||||||
VanSearch: typeof import('vant/es')['Search']
|
VanSearch: typeof import('vant/es')['Search']
|
||||||
|
VanStepper: typeof import('vant/es')['Stepper']
|
||||||
|
VanSwipe: typeof import('vant/es')['Swipe']
|
||||||
|
VanSwipeItem: typeof import('vant/es')['SwipeItem']
|
||||||
|
VanSwitch: typeof import('vant/es')['Switch']
|
||||||
VanTab: typeof import('vant/es')['Tab']
|
VanTab: typeof import('vant/es')['Tab']
|
||||||
VanTabs: typeof import('vant/es')['Tabs']
|
VanTabs: typeof import('vant/es')['Tabs']
|
||||||
VanUploader: typeof import('vant/es')['Uploader']
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import axios from '@/utils/axios'
|
import axios from '@/utils/axios'
|
||||||
import user from './user'
|
import user from './user'
|
||||||
import shop from './commodity'
|
import shop from './shop'
|
||||||
import common from './common'
|
import common from './common'
|
||||||
|
|
||||||
const totalApiConfig = { user, shop, common }
|
const totalApiConfig = { user, shop, common }
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
// 商品维度接口
|
// 商品维度接口
|
||||||
const shop = {
|
const shop = {
|
||||||
|
// 商品
|
||||||
searchCommodityList: ['/index/page/list'], // 获取商品列表接口
|
searchCommodityList: ['/index/page/list'], // 获取商品列表接口
|
||||||
getCommodityDetail: ['/index/product/detail'], // 获取商品详情
|
getCommodityDetail: ['/index/product/detail'], // 获取商品详情
|
||||||
getReviewList: ['/comment/list'], // 获取评价接口
|
getReviewList: ['/comment/list'], // 获取评价接口
|
||||||
|
addCart: ['/shipping/cart/addOrUpdate'], // 添加购物车接口
|
||||||
|
order: ['/order/add'], // 下单
|
||||||
|
// 订单
|
||||||
|
orderList: ['/order/list'], // 订单列表
|
||||||
}
|
}
|
||||||
|
|
||||||
export default shop
|
export default shop
|
||||||
@ -1,4 +1,5 @@
|
|||||||
$theme-color: #01cf24;
|
$theme-color: #01cf24;
|
||||||
|
$theme-light-color: #e4fdea;
|
||||||
$theme-disabled-color: #cefad5;
|
$theme-disabled-color: #cefad5;
|
||||||
|
|
||||||
.theme-bg-color {
|
.theme-bg-color {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import api from '@/api'
|
||||||
import { closeToast, showLoadingToast, type ToastWrapperInstance } from 'vant'
|
import { closeToast, showLoadingToast, type ToastWrapperInstance } from 'vant'
|
||||||
|
|
||||||
interface ApiType {
|
interface ApiType {
|
||||||
@ -61,3 +62,8 @@ window.useTipDialog = (
|
|||||||
tipDialogData.show = true
|
tipDialogData.show = true
|
||||||
tipDialogData.callback = callback
|
tipDialogData.callback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加购物车
|
||||||
|
export const handleAddCart = async (skuId: number, num: number = 1) => {
|
||||||
|
await api.shop.addCart.post({ num, skuId })
|
||||||
|
}
|
||||||
|
|||||||
@ -11,24 +11,24 @@
|
|||||||
</h4>
|
</h4>
|
||||||
<div class="flex-1 overflow-y-scroll">
|
<div class="flex-1 overflow-y-scroll">
|
||||||
<van-cell-group class="address-list__container">
|
<van-cell-group class="address-list__container">
|
||||||
<van-cell :class="['address-list__item']" v-for="item in addressList" :key="item.id">
|
<van-cell :class="['address-list__item', { 'theme-color': chooseAddressData.id === item.id }]" v-for="item in addressList" :key="item.id" @click="chooseAddressData = item">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="item__info">
|
<div class="item__info">
|
||||||
<h6 class="text-[.1rem]">{{ item.area }}</h6>
|
<h6 class="text-[.1rem]">{{ item.contry + item.province + item.city + item.district }}</h6>
|
||||||
<p class="item__info__detail">{{ item.detail }}</p>
|
<p class="item__info__detail">{{ item.detail }}</p>
|
||||||
<p class="item__info__subtitle text-[.13rem]">{{ item.name }} {{ item.tel }}</p>
|
<p class="item__info__subtitle text-[.13rem]">{{ item.buyerName }} {{ item.buyerPhone }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #value>
|
<template #value>
|
||||||
<div class="item__editor text-base">
|
<div class="item__editor text-base">
|
||||||
<van-icon v-if="!isManage" name="edit" color="#999" @click="onAddOrEditAddress('edit', item.originData)" />
|
<van-icon v-if="!isManage" name="edit" color="#999" @click.stop="onAddOrEditAddress('edit', item.originData)" />
|
||||||
<van-icon v-else name="delete-o" />
|
<van-icon v-else name="delete-o" @click.stop="onDeleteAddress(item.id)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
<div class="bg-white w-full h-12 p-2 box-border">
|
<div class="bg-white w-full h-12 p-2 box-border">
|
||||||
<button class="block w-full h-full border-none bg-red color-white rounded-[.04rem]">确定</button>
|
<button class="block w-full h-full border-none bg-red color-white rounded-[.04rem]" @click="onSubmit">确定</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
@ -42,12 +42,15 @@ const model = defineModel<boolean>()
|
|||||||
const addressFormType = defineModel<'add' | 'edit'>('addressFormType')
|
const addressFormType = defineModel<'add' | 'edit'>('addressFormType')
|
||||||
const showAddAddressFormDialog = defineModel<boolean>('showAddAddressFormDialog')
|
const showAddAddressFormDialog = defineModel<boolean>('showAddAddressFormDialog')
|
||||||
const isUpDateAddressList = defineModel<boolean>('isUpDateAddressList')
|
const isUpDateAddressList = defineModel<boolean>('isUpDateAddressList')
|
||||||
|
const props = defineProps<{ curAddressData: Recordable<any> }>()
|
||||||
const addressList = ref([])
|
const addressList = ref([])
|
||||||
|
const chooseAddressData = ref({})
|
||||||
|
|
||||||
const emits = defineEmits(['edit', 'confirm'])
|
const emits = defineEmits(['edit', 'confirm'])
|
||||||
|
|
||||||
watch(model, (val) => {
|
watch(model, (val) => {
|
||||||
val && handleGetAddressData()
|
val && handleGetAddressData()
|
||||||
|
val && (chooseAddressData.value = { ...props.curAddressData })
|
||||||
})
|
})
|
||||||
|
|
||||||
// 是否需要更新地址列表
|
// 是否需要更新地址列表
|
||||||
@ -68,20 +71,26 @@ const onAddOrEditAddress = (type: 'edit' | 'add', data?: any) => {
|
|||||||
|
|
||||||
const handleGetAddressData = () => {
|
const handleGetAddressData = () => {
|
||||||
api.user.getAddressList.post<any>().then((res) => {
|
api.user.getAddressList.post<any>().then((res) => {
|
||||||
addressList.value = res.data.rows.map((item) => {
|
addressList.value = res.data.rows
|
||||||
const originData = { ...item }
|
.filter((item) => item.status !== 'delete')
|
||||||
return {
|
.map((item) => {
|
||||||
id: item.id,
|
const originData = { ...item }
|
||||||
name: item.buyerName,
|
return {
|
||||||
tel: item.buyerPhone,
|
isDefault: item.status === 'default',
|
||||||
area: item.contry + item.province + item.city + item.district,
|
...originData,
|
||||||
detail: item.detail,
|
}
|
||||||
isDefault: item.status === 'default',
|
})
|
||||||
originData,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onDeleteAddress = async (id: number) => {
|
||||||
|
await api.user.updateAddress.post({ id, status: 'delete' })
|
||||||
|
handleGetAddressData()
|
||||||
|
}
|
||||||
|
const onSubmit = () => {
|
||||||
|
model.value = false
|
||||||
|
emits('confirm', chooseAddressData.value)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -94,6 +103,9 @@ const handleGetAddressData = () => {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.theme-color {
|
||||||
|
background: $theme-light-color;
|
||||||
|
}
|
||||||
:deep(.van-cell) {
|
:deep(.van-cell) {
|
||||||
.van-cell__title {
|
.van-cell__title {
|
||||||
width: 0;
|
width: 0;
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
<li :class="['all-review__tab-li', { active: item.active }]" v-for="item in tabs" :key="item.value">{{ item.name }}</li>
|
<li :class="['all-review__tab-li', { active: item.active }]" v-for="item in tabs" :key="item.value">{{ item.name }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="all-review__contain flex-1 overflow-y-scroll">
|
<ul class="all-review__contain flex-1 overflow-y-scroll">
|
||||||
<li class="p-3" v-for="item in reviews" :key="item.id">
|
<li class="p-3" v-for="item in list" :key="item.id">
|
||||||
<review-single />
|
<review-single :data="item" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
@ -16,13 +16,13 @@
|
|||||||
import reviewSingle from './review-single.vue'
|
import reviewSingle from './review-single.vue'
|
||||||
|
|
||||||
const model = defineModel<boolean>()
|
const model = defineModel<boolean>()
|
||||||
|
defineProps<{ list: Recordable<any>[] }>()
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ name: '全部', value: 0, active: true },
|
{ name: '全部', value: 0, active: true },
|
||||||
{ name: '最新评价', value: 3, active: false },
|
{ name: '最新评价', value: 3, active: false },
|
||||||
{ name: '5/4星好评', value: 1, active: false },
|
{ name: '5/4星好评', value: 1, active: false },
|
||||||
{ name: '图/视频', value: 2, active: false },
|
{ name: '图/视频', value: 2, active: false },
|
||||||
]
|
]
|
||||||
const reviews = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 5 }]
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -3,21 +3,22 @@
|
|||||||
<h3 class="flex items-center text-sm">
|
<h3 class="flex items-center text-sm">
|
||||||
<img class="w-8 h-8 rounded-full" src="" alt="" />
|
<img class="w-8 h-8 rounded-full" src="" alt="" />
|
||||||
<div class="ml-1">
|
<div class="ml-1">
|
||||||
<p class="">饭**子</p>
|
<p class="">{{ data.buyerName }}</p>
|
||||||
<p class="mt-0.5 text-xs color-[#666]">2025.08.23</p>
|
<p class="mt-0.5 text-xs color-[#666]">{{ handleData.formatTime(data.createTime, 'YYYY.MM.DD') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="review-content mt-3">
|
<div class="review-content mt-3">
|
||||||
<p>公司大丰收东方闪电防守打法四大分三大胜多负少的</p>
|
<p>{{ data.productComment }}</p>
|
||||||
<p class="flex items-center mt-2">
|
<p class="flex items-center mt-2">
|
||||||
<img class="w-16 h-16 mr-1 rounded-[4px]" src="" alt="" v-for="(item, index) in list" :key="index" />
|
<img class="w-16 h-16 mr-1 rounded-[4px]" :src="item.commentUrl" alt="" v-for="(item, index) in data.vvCommentDetailEntities" :key="index" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const list = ref(['', ''])
|
import { handleData } from 'lz-utils-lib'
|
||||||
|
withDefaults(defineProps<{ data: any }>(), { data: {} })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style></style>
|
<style></style>
|
||||||
|
|||||||
@ -5,44 +5,113 @@
|
|||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div class="cart-wrap">
|
<div class="cart-wrap">
|
||||||
<p class="flex justify-between" @click="showAddressList = true">
|
<p class="flex justify-between" @click="showAddressList = true">
|
||||||
<van-icon name="location-o" class="mr-1" /><span class="max-w-12 mr-1">张四</span> <b class="font- flex-1">我的详细地址是什么东西的地方的好的么</b><van-icon name="arrow" />
|
<van-icon name="location-o" class="mr-1" /><span class="max-w-12 mr-1">{{ curAddressData.buyerName }}</span>
|
||||||
|
<b class="font- flex-1">{{ curAddressData.province + curAddressData.city + curAddressData.district + curAddressData.detail }}</b
|
||||||
|
><van-icon name="arrow" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="cart-wrap mt-2">
|
<div class="cart-wrap mt-2">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<img class="w-16 h-16 mr-4" style="border-radius: 0.04rem" src="" alt="" />
|
<img class="w-16 h-16 mr-4" style="border-radius: 0.04rem" :src="curPageData.imageUrl" alt="" />
|
||||||
<p>¥<span>29.9</span></p>
|
<div>
|
||||||
|
<p>
|
||||||
|
¥<span>{{ curPageData.price }}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<van-stepper v-model="formData.num" input-width=".4rem" button-size=".2rem" min="1" :max="curPageData.num" />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cart-cagetory-wrap">
|
<div class="cart-cagetory-wrap">
|
||||||
<h4>更多系列</h4>
|
<template v-for="(arr, key) in skuData" :key="key">
|
||||||
<div class="cagetory">
|
<h4>{{ key }}</h4>
|
||||||
<p>手表系列</p>
|
<div class="cagetory">
|
||||||
<p>手表系列水</p>
|
<p
|
||||||
<p>手表</p>
|
v-for="item in arr"
|
||||||
<p>手表123</p>
|
:key="item.propertyId"
|
||||||
<p>手表12</p>
|
:class="{ 'theme-border-color theme-color': item.propertyId === curPropertyIds.find((it) => it.productPropertyName === key).id }"
|
||||||
</div>
|
@click="onChooseProperty(item, key)"
|
||||||
<h4>颜色</h4>
|
>
|
||||||
<div class="cagetory">
|
{{ item.label }}
|
||||||
<p>手表系列</p>
|
</p>
|
||||||
<p>手表系列水</p>
|
</div>
|
||||||
<p>手表</p>
|
</template>
|
||||||
<p>手表123</p>
|
|
||||||
<p>手表12</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="fixed bg-white w-full h-12 left-0 bottom-0 p-2 box-border">
|
<div class="fixed bg-white w-full h-12 left-0 bottom-0 p-2 box-border">
|
||||||
<button class="block w-full h-full border-none theme-bg-color color-white rounded-[.04rem]">确定</button>
|
<button class="block w-full h-full border-none theme-bg-color color-white rounded-[.04rem]" @click="onSubmit">{{ scene === 'cart' ? '加入购物车' : '立即下单' }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import api from '@/api'
|
||||||
|
|
||||||
const model = defineModel<boolean>()
|
const model = defineModel<boolean>()
|
||||||
const showAddressList = defineModel<boolean>('showAddressList')
|
const showAddressList = defineModel<boolean>('showAddressList')
|
||||||
|
const props = defineProps<{ list: any[]; curAddressData: Recordable<any>; scene: 'order' | 'cart' }>()
|
||||||
|
const skuData = ref({}) // sku分类,key为属性的类目,value为属性的分类{ 颜色: [{ '黄色'}]}
|
||||||
|
const commodityData = ref({}) // 商品对象, value为每种商品的skuId,图片,销售价,成交价,库存(key为属性id的集合)
|
||||||
|
const curPropertyIds = ref([]) // 当前选中的属性id的集合[{ id: 1, name: '颜色'},{id: 2, name: '重量'}] name是用来赋值的
|
||||||
|
const curPageData = ref({ price: '', imageUrl: '', num: 0 }) // 当前商品页面数据
|
||||||
|
const formData = ref({ skuId: NaN, num: 1 })
|
||||||
|
|
||||||
|
watch(model, (val) => {
|
||||||
|
if (val) {
|
||||||
|
const { result, commodityMap, firstShopIds } = handleGetSkuList(props.list)
|
||||||
|
skuData.value = result
|
||||||
|
commodityData.value = commodityMap
|
||||||
|
curPropertyIds.value = firstShopIds
|
||||||
|
const { pageData, skuId } = handleGetPageData(commodityData.value, curPropertyIds.value)
|
||||||
|
curPageData.value = pageData
|
||||||
|
formData.value = { skuId, num: 1 }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleGetSkuList = (list: any[]) => {
|
||||||
|
const result = {} as any // sku分类
|
||||||
|
const commodityMap = {} //
|
||||||
|
let firstShopIds = []
|
||||||
|
list.forEach((item, i) => {
|
||||||
|
const commodityValue = { id: item.id, imageUrl: item.imageUrl, salePrice: item.salePrice, price: item.promotionPrice, num: item.stock }
|
||||||
|
let commodityPropertyId = ''
|
||||||
|
if (i === 0) {
|
||||||
|
firstShopIds = item.vvSkuPropertyValueList.map((child) => ({ id: child.productPropertyValueId, productPropertyName: child.productPropertyName }))
|
||||||
|
}
|
||||||
|
item.vvSkuPropertyValueList.forEach((item2, index) => {
|
||||||
|
commodityPropertyId += item2.productPropertyValueId + (index < item.vvSkuPropertyValueList.length - 1 ? '_' : '')
|
||||||
|
if (!result[item2.productPropertyName]) result[item2.productPropertyName] = []
|
||||||
|
if (result[item2.productPropertyName].some((child) => child.propertyId === item2.productPropertyValueId)) return
|
||||||
|
result[item2.productPropertyName].push({ propertyId: item2.productPropertyValueId, label: item2.productPropertyValue })
|
||||||
|
})
|
||||||
|
commodityMap[commodityPropertyId] = commodityValue
|
||||||
|
})
|
||||||
|
|
||||||
|
return { result, commodityMap, firstShopIds }
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChooseProperty = (item, categoryName) => {
|
||||||
|
curPropertyIds.value.find((it) => it.productPropertyName === categoryName).id = item.propertyId
|
||||||
|
const { pageData, skuId } = handleGetPageData(commodityData.value, curPropertyIds.value)
|
||||||
|
curPageData.value = pageData
|
||||||
|
formData.value = { skuId, num: 1 }
|
||||||
|
}
|
||||||
|
const handleGetPageData = (commodityData, curPropertyIds) => {
|
||||||
|
const data = commodityData[curPropertyIds.map((item) => item.id).join('_')]
|
||||||
|
return { pageData: { price: data.price, imageUrl: data.imageUrl, num: data.num }, skuId: data.id }
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (props.scene === 'cart') {
|
||||||
|
await api.shop.addCart.post(formData.value)
|
||||||
|
showToast('加入购物车成功')
|
||||||
|
} else {
|
||||||
|
await api.shop.order.post({ buyerAddressId: 2, vvTradeOrderLineDTOList: [formData.value] })
|
||||||
|
showToast('下单成功')
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -63,6 +132,8 @@ const showAddressList = defineModel<boolean>('showAddressList')
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
& > p {
|
& > p {
|
||||||
|
border: 1px solid #f2f2f2;
|
||||||
|
box-sizing: border-box;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
height: 0.28rem;
|
height: 0.28rem;
|
||||||
line-height: 0.28rem;
|
line-height: 0.28rem;
|
||||||
@ -72,6 +143,9 @@ const showAddressList = defineModel<boolean>('showAddressList')
|
|||||||
border-radius: 0.04rem;
|
border-radius: 0.04rem;
|
||||||
margin-right: 0.08rem;
|
margin-right: 0.08rem;
|
||||||
margin-bottom: 0.08rem;
|
margin-bottom: 0.08rem;
|
||||||
|
&.theme-border-color {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="commodity-detail">
|
<div id="commodity-detail">
|
||||||
<header class="h-70 bg-[#aaa]"></header>
|
<header class="h-70 bg-[#aaa]">
|
||||||
|
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
|
||||||
|
<van-swipe-item><img class="h-full w-full" :src="detail.mainImg" alt="" /></van-swipe-item>
|
||||||
|
<van-swipe-item v-if="detail.video">{{ detail.video }}</van-swipe-item>
|
||||||
|
<van-swipe-item v-for="img in detail.subImgs" :key="img">{{ img }}</van-swipe-item>
|
||||||
|
</van-swipe>
|
||||||
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<div class="detail-info wrap">
|
<div class="detail-info wrap">
|
||||||
<h2 class="text-2xl color-[#000]">陕西珍珠首饰项链</h2>
|
<h2 class="text-2xl color-[#000]">{{ detail.title }}</h2>
|
||||||
<h4 class="mt-2 text-sm">副标题</h4>
|
<!-- <h4 class="mt-2 text-sm">副标题</h4>
|
||||||
<ul class="detail-wrap">
|
<ul class="detail-wrap">
|
||||||
<li><span>配送</span> <em>立即下单,预计最快今天23:00发货</em></li>
|
<li><span>配送</span> <em>立即下单,预计最快今天23:00发货</em></li>
|
||||||
<li><span>服务</span> <button>7天无理由退款</button></li>
|
<li><span>服务</span> <button>7天无理由退款</button></li>
|
||||||
</ul>
|
</ul> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="review-info wrap">
|
<div class="review-info wrap">
|
||||||
<h3 class="wrap-title"><b>用户评价(45.6万+)</b><span @click="onShowAllReview">全部评价 ></span></h3>
|
<h3 class="wrap-title">
|
||||||
|
<b>用户评价({{ reviews.length }}+)</b><span @click="onShowAllReview">全部评价 ></span>
|
||||||
|
</h3>
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<review-single />
|
<review-single :data="reviews[0]" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h3 class="wrap-title"><b>商品详情</b><span>查看全部信息</span></h3>
|
<h3 class="wrap-title">
|
||||||
<info-table class="mt-3" />
|
<b>商品详情</b>
|
||||||
|
<!-- <span>查看全部信息</span> -->
|
||||||
|
</h3>
|
||||||
|
<!-- <info-table class="mt-3" /> -->
|
||||||
<div class="imgs">
|
<div class="imgs">
|
||||||
<img class="w-full h-40" src="" alt="" />
|
<img v-for="img in detail.detailImgs" :src="img" alt="" class="w-full h-40" />
|
||||||
<img class="w-full h-40" src="" alt="" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
@ -46,17 +56,18 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 button-wrap">
|
<div class="flex-1 button-wrap">
|
||||||
<button class="bg-orange text-white w-30!">加入购物车</button>
|
<button class="bg-orange text-white w-30!" @click="onSubmit('cart')">加入购物车</button>
|
||||||
<button class="theme-bg-color color-white" @click="onPayNowBtn">立即购买</button>
|
<button class="theme-bg-color color-white" @click="onSubmit('order')">立即购买</button>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<shopping-cart v-model="showShopCart" v-model:showAddressList="showAddressList" v-model:curAddressData="curAddressData"></shopping-cart>
|
<shopping-cart v-model="showShopCart" v-model:showAddressList="showAddressList" v-model:curAddressData="curAddressData" :list="skuList" :scene="cartType"></shopping-cart>
|
||||||
<all-review v-model="showAllReview"></all-review>
|
<all-review v-model="showAllReview" :list="reviews"></all-review>
|
||||||
<address-list
|
<address-list
|
||||||
v-model="showAddressList"
|
v-model="showAddressList"
|
||||||
v-model:isUpDateAddressList="isUpDateAddressList"
|
v-model:isUpDateAddressList="isUpDateAddressList"
|
||||||
v-model:showAddAddressFormDialog="showAddAddressFormDialog"
|
v-model:showAddAddressFormDialog="showAddAddressFormDialog"
|
||||||
v-model:addressFormType="addressFormType"
|
v-model:addressFormType="addressFormType"
|
||||||
|
:curAddressData="curAddressData"
|
||||||
@edit="(val) => (editAddressData = val)"
|
@edit="(val) => (editAddressData = val)"
|
||||||
@confirm="(val) => (curAddressData = val)"
|
@confirm="(val) => (curAddressData = val)"
|
||||||
></address-list>
|
></address-list>
|
||||||
@ -74,7 +85,16 @@ import shoppingCart from './components/shopping-cart.vue'
|
|||||||
import addressList from './components/address-list.vue'
|
import addressList from './components/address-list.vue'
|
||||||
import addressForm from './components/address-form.vue'
|
import addressForm from './components/address-form.vue'
|
||||||
|
|
||||||
|
const detail = reactive({
|
||||||
|
mainImg: '',
|
||||||
|
video: '',
|
||||||
|
subImgs: [],
|
||||||
|
detailImgs: [],
|
||||||
|
title: '',
|
||||||
|
})
|
||||||
|
|
||||||
const showShopCart = ref(false) // 展示购物车
|
const showShopCart = ref(false) // 展示购物车
|
||||||
|
const cartType = ref<'order' | 'cart'>('order')
|
||||||
const showAllReview = ref(false) // 展示全部评价
|
const showAllReview = ref(false) // 展示全部评价
|
||||||
const showAddressList = ref(false) // 展示收货地址列表
|
const showAddressList = ref(false) // 展示收货地址列表
|
||||||
const showAddAddressFormDialog = ref(false) // 展示收货地址form
|
const showAddAddressFormDialog = ref(false) // 展示收货地址form
|
||||||
@ -85,7 +105,9 @@ const curAddressData = ref({}) // 当前选中的收货地址
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const init = () => {
|
const init = () => {
|
||||||
|
handleGetCommodityDetail(+route.query.id)
|
||||||
handleGetReviews(+route.query.id)
|
handleGetReviews(+route.query.id)
|
||||||
|
handleGetAddressList()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看全部评价
|
// 查看全部评价
|
||||||
@ -93,13 +115,35 @@ const onShowAllReview = () => {
|
|||||||
showAllReview.value = true
|
showAllReview.value = true
|
||||||
}
|
}
|
||||||
// 点击立即购买按钮
|
// 点击立即购买按钮
|
||||||
const onPayNowBtn = () => {
|
const onSubmit = (type: 'order' | 'cart') => {
|
||||||
|
cartType.value = type
|
||||||
showShopCart.value = true
|
showShopCart.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取评价
|
||||||
|
const reviews = ref([])
|
||||||
const handleGetReviews = (productId: number) => {
|
const handleGetReviews = (productId: number) => {
|
||||||
api.shop.getReviewList.post({ productId }).then((res) => {
|
api.shop.getReviewList.post<any>({ productId }).then((res) => {
|
||||||
console.log(res)
|
reviews.value = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const skuList = ref([])
|
||||||
|
// 获取商品详情
|
||||||
|
const handleGetCommodityDetail = (productId: number) => {
|
||||||
|
api.shop.getCommodityDetail.post<any>({ productId }).then((res) => {
|
||||||
|
detail.mainImg = res.data.mainImageUrl
|
||||||
|
detail.video = res.data.videoUrl
|
||||||
|
detail.subImgs = res.data.vvProductDetailList.filter((item) => item.type === 1)?.map((item) => item.testzc)
|
||||||
|
detail.detailImgs = res.data.vvProductDetailList.find((item) => item.type === 2)?.testzc
|
||||||
|
detail.title = res.data.title
|
||||||
|
skuList.value = res.data.vvSkuList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGetAddressList = () => {
|
||||||
|
api.user.getAddressList.post<any>({}).then((res) => {
|
||||||
|
curAddressData.value = res.data.rows.find((item) => item.status === 'default')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
@ -108,6 +152,9 @@ init()
|
|||||||
<style scope lang="scss">
|
<style scope lang="scss">
|
||||||
#commodity-detail {
|
#commodity-detail {
|
||||||
background: #f2f2f2;
|
background: #f2f2f2;
|
||||||
|
.my-swipe {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
.wrap {
|
.wrap {
|
||||||
margin: 0.08rem 0.12rem;
|
margin: 0.08rem 0.12rem;
|
||||||
border-radius: 0.12rem;
|
border-radius: 0.12rem;
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const list = [
|
const list = [
|
||||||
{ title: '搜索结果', path: 'search-container' },
|
{ title: '搜索结果', path: 'search-container' },
|
||||||
{ title: '商品详情', path: 'commodity-detail' },
|
{ title: '商品详情', path: 'commodity-detail', query: { id: 4 } },
|
||||||
{ title: '我的订单', path: 'order' },
|
{ title: '我的订单', path: 'order' },
|
||||||
{ title: '订单详情', path: 'order/detail' },
|
{ title: '订单详情', path: 'order/detail' },
|
||||||
{ title: '评价中心', path: 'review/center' },
|
{ title: '评价中心', path: 'review/center' },
|
||||||
@ -18,7 +18,7 @@ const list = [
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const onClick = (item: any) => {
|
const onClick = (item: any) => {
|
||||||
router.push({ name: item.path })
|
router.push({ name: item.path, query: item.query })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -58,6 +58,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import api from '@/api'
|
||||||
import { showToast } from 'vant'
|
import { showToast } from 'vant'
|
||||||
|
|
||||||
const searchValue = ref('')
|
const searchValue = ref('')
|
||||||
@ -122,6 +123,22 @@ const router = useRouter()
|
|||||||
const goDetail = (id: number) => {
|
const goDetail = (id: number) => {
|
||||||
router.push({ path: '/order/detail', query: { id } })
|
router.push({ path: '/order/detail', query: { id } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
handleGetList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGetList = () => {
|
||||||
|
api.shop.orderList.post().then((res) => {
|
||||||
|
orders.value = res.data.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
storeName: item.title,
|
||||||
|
}))
|
||||||
|
console.warn('----- my data is res: ', res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -4,13 +4,13 @@
|
|||||||
<van-search class="flex-1 p-0!" background="#f2f2f2" v-model="searchValue" placeholder="请输入搜索关键词" @search="onSearch(searchValue)" />
|
<van-search class="flex-1 p-0!" background="#f2f2f2" v-model="searchValue" placeholder="请输入搜索关键词" @search="onSearch(searchValue)" />
|
||||||
<van-icon name="shopping-cart-o" size=".2rem" class="ml-2" />
|
<van-icon name="shopping-cart-o" size=".2rem" class="ml-2" />
|
||||||
</header>
|
</header>
|
||||||
<van-tabs v-model:active="searchSort" sticky :color="appStore.themeColor" @change="onChangeSort">
|
<van-tabs v-model:active="searchSort" sticky :color="appStore.themeColor" @click="onChangeSort(searchSort)">
|
||||||
<van-tab title="综合" name="0"></van-tab>
|
<van-tab title="综合" name="0"></van-tab>
|
||||||
<van-tab title="销量" name="1"></van-tab>
|
<van-tab title="销量" name="1"></van-tab>
|
||||||
<van-tab title="价格" name="2"></van-tab>
|
<van-tab title="价格" name="2"></van-tab>
|
||||||
</van-tabs>
|
</van-tabs>
|
||||||
<ul class="search-condition bg-white pt-2 flex-1 overflow-y-scroll">
|
<ul class="search-condition bg-white pt-2 flex-1 overflow-y-scroll">
|
||||||
<li v-for="item in searchList" :key="item.id" class="flex item-center p-2 relative" @click="onGoDetail(item.id)">
|
<li v-for="item in searchList" :key="item.productId" class="flex item-center p-2 relative" @click="onGoDetail(item.productId)">
|
||||||
<img :src="item.mainImageUrl" alt="" class="w-24 h-24" />
|
<img :src="item.mainImageUrl" alt="" class="w-24 h-24" />
|
||||||
<div class="ml-2">
|
<div class="ml-2">
|
||||||
<h4 class="text-[.13rem]">{{ item.title }}</h4>
|
<h4 class="text-[.13rem]">{{ item.title }}</h4>
|
||||||
@ -27,7 +27,18 @@
|
|||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<van-icon name="add" :color="appStore.themeColor" size=".2rem" class="absolute! right-3 bottom-6 h-5" />
|
<van-icon
|
||||||
|
name="add"
|
||||||
|
:color="appStore.themeColor"
|
||||||
|
size=".2rem"
|
||||||
|
class="absolute! right-3 bottom-6 h-5"
|
||||||
|
@click="
|
||||||
|
(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleAddCart(item.skuId)
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -35,10 +46,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
import { handleAddCart } from '@/utils/page'
|
||||||
import useAppStore from '@/stores/modules/app'
|
import useAppStore from '@/stores/modules/app'
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const searchValue = ref(route.query.initValue as string)
|
const searchValue = ref(route.query.initValue as string)
|
||||||
const searchSort = ref('0')
|
const searchSort = ref('0')
|
||||||
|
const priceSort = ref('DESC')
|
||||||
|
|
||||||
const searchList = ref([])
|
const searchList = ref([])
|
||||||
|
|
||||||
@ -51,9 +64,25 @@ const onGoDetail = (id: number) => {
|
|||||||
|
|
||||||
const onSearch = async (searchValue: string, sort = '0') => {
|
const onSearch = async (searchValue: string, sort = '0') => {
|
||||||
searchSort.value = sort
|
searchSort.value = sort
|
||||||
const result = await api.shop.searchCommodityList.post<any>({ productName: searchValue, frontPage: 0, isTest: 0, sort: searchSort.value }) // testzc排序
|
let sortParams = {}
|
||||||
|
// ASC: 升序, DESC: 降序
|
||||||
|
if (sort === '0') {
|
||||||
|
// 综合
|
||||||
|
priceSort.value = 'DESC'
|
||||||
|
sortParams = { createTimestampSort: 'DESC' }
|
||||||
|
} else if (sort === '1') {
|
||||||
|
// 销量
|
||||||
|
priceSort.value = 'DESC'
|
||||||
|
sortParams = { salePriceSort: 'DESC' }
|
||||||
|
} else if (sort === '2') {
|
||||||
|
// 价格
|
||||||
|
priceSort.value = priceSort.value === 'ASC' ? 'DESC' : 'ASC'
|
||||||
|
sortParams = { priceSort: priceSort.value }
|
||||||
|
}
|
||||||
|
const result = await api.shop.searchCommodityList.post<any>({ productName: searchValue, frontPage: 0, isTest: 0, ...sortParams }) // testzc排序
|
||||||
searchList.value = result.data.rows.map((item) => ({
|
searchList.value = result.data.rows.map((item) => ({
|
||||||
id: item.id,
|
skuId: item.skuId,
|
||||||
|
productId: item.productId,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
mainImageUrl: item.mainImageUrl,
|
mainImageUrl: item.mainImageUrl,
|
||||||
originPrice: String(item.showSalePrice),
|
originPrice: String(item.showSalePrice),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user