添加 商品详情 模板页面
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cool-unix",
|
"name": "cool-unix",
|
||||||
"version": "8.0.28",
|
"version": "8.0.29",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build-ui": "node ./uni_modules/cool-ui/scripts/generate-types.js",
|
"build-ui": "node ./uni_modules/cool-ui/scripts/generate-types.js",
|
||||||
|
|||||||
@@ -522,6 +522,12 @@
|
|||||||
"navigationBarTitleText": "商品分类"
|
"navigationBarTitleText": "商品分类"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "shop/goods-detail/index",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "shop/shopping-cart",
|
"path": "shop/shopping-cart",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ const list = computed<Item[]>(() => [
|
|||||||
path: "/pages/template/shop/goods-category"
|
path: "/pages/template/shop/goods-category"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("商品详情")
|
label: t("商品详情"),
|
||||||
|
path: "/pages/template/shop/goods-detail/index"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("商品列表、筛选")
|
label: t("商品列表、筛选")
|
||||||
|
|||||||
81
pages/template/shop/goods-detail/comment.uvue
Normal file
81
pages/template/shop/goods-detail/comment.uvue
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<view class="flex">
|
||||||
|
<view class="flex flex-row items-center mb-3">
|
||||||
|
<cl-text>买家评论 78</cl-text>
|
||||||
|
|
||||||
|
<cl-icon name="arrow-right-s-line" :pt="{ className: 'ml-auto' }"></cl-icon>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flex flex-col">
|
||||||
|
<view class="flex flex-row my-3" v-for="item in list" :key="item.id">
|
||||||
|
<cl-avatar :size="72" rounded :src="item.avatar"></cl-avatar>
|
||||||
|
|
||||||
|
<view class="flex-1 ml-4">
|
||||||
|
<view class="flex flex-row items-center justify-between">
|
||||||
|
<cl-text>{{ item.name }}</cl-text>
|
||||||
|
<cl-text color="info" :pt="{ className: 'text-xs' }">{{
|
||||||
|
item.time
|
||||||
|
}}</cl-text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<cl-text ellipsis :lines="2" :pt="{ className: 'mt-1 text-sm' }">{{
|
||||||
|
item.content
|
||||||
|
}}</cl-text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="ml-3 relative">
|
||||||
|
<cl-image
|
||||||
|
:height="100"
|
||||||
|
:width="100"
|
||||||
|
:pt="{
|
||||||
|
inner: {
|
||||||
|
className: '!rounded-lg'
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
src="https://unix.cool-js.com/images/demo/bg1.png"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<view class="bg-black/60 rounded-full px-1 absolute top-9 right-1">
|
||||||
|
<text class="text-xs text-white">+3</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
type Comment = {
|
||||||
|
id: number;
|
||||||
|
avatar: string;
|
||||||
|
name: string;
|
||||||
|
content: string;
|
||||||
|
time: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const list = ref<Comment[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
avatar: "https://unix.cool-js.com/images/demo/avatar-1.jpg",
|
||||||
|
name: "李明",
|
||||||
|
content: "导游讲解很专业,风景绝美,是一次难忘的旅行体验!",
|
||||||
|
time: "几分钟前"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
avatar: "https://unix.cool-js.com/images/demo/avatar-2.jpg",
|
||||||
|
name: "小芳",
|
||||||
|
content: "酒店干净卫生,位置也很方便,强烈推荐给大家!",
|
||||||
|
time: "1小时前"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
avatar: "https://unix.cool-js.com/images/demo/avatar-3.jpg",
|
||||||
|
name: "王科",
|
||||||
|
content: "行程安排合理,吃住都很满意,导游态度超级好。",
|
||||||
|
time: "5天前"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
15
pages/template/shop/goods-detail/desc.uvue
Normal file
15
pages/template/shop/goods-detail/desc.uvue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<view class="flex">
|
||||||
|
<image v-for="item in list" :key="item" :src="item" mode="widthFix" class="w-full" />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
const list = ref<string[]>([
|
||||||
|
"https://unix.cool-js.com/images/demo/goods/desc-1.jpg",
|
||||||
|
"https://unix.cool-js.com/images/demo/goods/desc-2.jpg",
|
||||||
|
"https://unix.cool-js.com/images/demo/goods/desc-3.jpg"
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
116
pages/template/shop/goods-detail/index.uvue
Normal file
116
pages/template/shop/goods-detail/index.uvue
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<template>
|
||||||
|
<cl-page>
|
||||||
|
<Topbar />
|
||||||
|
|
||||||
|
<cl-banner
|
||||||
|
:list="bannerList"
|
||||||
|
:height="800"
|
||||||
|
:pt="{
|
||||||
|
className: '!rounded-none',
|
||||||
|
image: {
|
||||||
|
className: '!rounded-none'
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<view class="card dark:!bg-surface-700" id="info">
|
||||||
|
<Info />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="card !py-1 dark:!bg-surface-700">
|
||||||
|
<view class="row is-border dark:!border-surface-600">
|
||||||
|
<cl-icon name="apps-line"></cl-icon>
|
||||||
|
|
||||||
|
<view class="flex-1 ml-3">
|
||||||
|
<cl-text>已选:黑色 128GB + 碎屏险</cl-text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<cl-icon name="arrow-right-s-line"></cl-icon>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="row is-border !items-start dark:!border-surface-600">
|
||||||
|
<cl-icon name="truck-line" :pt="{ className: '!mt-1' }"></cl-icon>
|
||||||
|
|
||||||
|
<view class="flex-1 ml-3">
|
||||||
|
<cl-text color="info">预计11月1日 周三 送达</cl-text>
|
||||||
|
<cl-text color="info" :pt="{ className: '!mt-1' }">深圳益田假日广场</cl-text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="row">
|
||||||
|
<cl-icon name="shield-check-line"></cl-icon>
|
||||||
|
|
||||||
|
<view class="flex-1 ml-3">
|
||||||
|
<cl-text color="info">7天无理由退货 · 正品保证 · 极速发货 · 无忧售后</cl-text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="card dark:!bg-surface-700" id="comment">
|
||||||
|
<Comment />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="card !p-0 dark:!bg-surface-700" id="desc">
|
||||||
|
<Desc />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<cl-footer>
|
||||||
|
<view class="flex flex-row overflow-visible">
|
||||||
|
<view class="flex flex-row mr-auto overflow-visible">
|
||||||
|
<view class="flex justify-center items-center px-4">
|
||||||
|
<cl-icon name="heart-line" :size="42"></cl-icon>
|
||||||
|
<cl-text :pt="{ className: 'text-xs mt-1' }">收藏</cl-text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
class="flex justify-center items-center px-4 overflow-visible"
|
||||||
|
@tap="router.to('/pages/template/shop/shopping-cart')"
|
||||||
|
>
|
||||||
|
<cl-icon name="shopping-cart-line" :size="42"></cl-icon>
|
||||||
|
<cl-text :pt="{ className: 'text-xs mt-1' }">购物车</cl-text>
|
||||||
|
|
||||||
|
<cl-badge
|
||||||
|
type="error"
|
||||||
|
:value="3"
|
||||||
|
position
|
||||||
|
:pt="{ className: '!right-[24rpx] !top-[-10rpx] !scale-80' }"
|
||||||
|
></cl-badge>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<cl-button text border :pt="{ className: '!w-[220rpx]' }">加入购物车</cl-button>
|
||||||
|
<cl-button :pt="{ className: '!w-[220rpx]' }">立即购买</cl-button>
|
||||||
|
</view>
|
||||||
|
</cl-footer>
|
||||||
|
</cl-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { router } from "@/cool";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import Comment from "./comment.uvue";
|
||||||
|
import Info from "./info.uvue";
|
||||||
|
import Desc from "./desc.uvue";
|
||||||
|
import Topbar from "./topbar.uvue";
|
||||||
|
|
||||||
|
const bannerList = ref<string[]>([
|
||||||
|
"https://unix.cool-js.com/images/demo/goods/banner-1.jpg",
|
||||||
|
"https://unix.cool-js.com/images/demo/goods/banner-2.jpg",
|
||||||
|
"https://unix.cool-js.com/images/demo/goods/banner-3.jpg",
|
||||||
|
"https://unix.cool-js.com/images/demo/goods/banner-4.jpg"
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.card {
|
||||||
|
@apply p-4 bg-white mb-3;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
@apply flex flex-row items-center py-4;
|
||||||
|
|
||||||
|
&.is-border {
|
||||||
|
@apply border-b border-t-0 border-l-0 border-r-0 border-solid border-surface-100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
35
pages/template/shop/goods-detail/info.uvue
Normal file
35
pages/template/shop/goods-detail/info.uvue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<view class="flex">
|
||||||
|
<cl-text :pt="{ className: 'text-lg font-bold mb-3' }"
|
||||||
|
>Apple/苹果 iPhone 15 (A3092) 128GB 黑色 支持移动联通电信5G 双卡双待手机</cl-text
|
||||||
|
>
|
||||||
|
|
||||||
|
<view class="flex flex-row items-center mb-3 overflow-visible">
|
||||||
|
<view class="flex flex-row items-end overflow-visible">
|
||||||
|
<cl-text color="error" :pt="{ className: 'font-bold text-sm' }">¥</cl-text>
|
||||||
|
<cl-text
|
||||||
|
type="amount"
|
||||||
|
:value="8499"
|
||||||
|
color="error"
|
||||||
|
:size="44"
|
||||||
|
currency=""
|
||||||
|
:pt="{
|
||||||
|
className: 'font-bold ml-1'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
</cl-text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<cl-text color="info" :pt="{ className: 'text-sm ml-auto' }" rounded
|
||||||
|
>已售 100 件</cl-text
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flex flex-row mb-3">
|
||||||
|
<cl-tag type="error" plain>满199减50</cl-tag>
|
||||||
|
<cl-tag type="error" plain>满299减100</cl-tag>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
124
pages/template/shop/goods-detail/topbar.uvue
Normal file
124
pages/template/shop/goods-detail/topbar.uvue
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<cl-sticky>
|
||||||
|
<cl-topbar fixed safe-area-top>
|
||||||
|
<cl-tabs
|
||||||
|
:list="list"
|
||||||
|
v-model="active"
|
||||||
|
height="36px"
|
||||||
|
:gutter="20"
|
||||||
|
@change="onTabChange"
|
||||||
|
></cl-tabs>
|
||||||
|
|
||||||
|
<template #append>
|
||||||
|
<view class="h-[44px] w-[30px] flex items-center justify-center mr-1">
|
||||||
|
<cl-icon name="search-line"></cl-icon>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</cl-topbar>
|
||||||
|
</cl-sticky>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { debounce, getSafeAreaHeight, isNull } from "@/cool";
|
||||||
|
import { usePage, type ClTabsItem } from "@/uni_modules/cool-ui";
|
||||||
|
import { getCurrentInstance, onMounted, onUnmounted, ref } from "vue";
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance()!;
|
||||||
|
const page = usePage();
|
||||||
|
|
||||||
|
// 当前激活tab
|
||||||
|
const active = ref("info");
|
||||||
|
|
||||||
|
// 滚动时激活tab
|
||||||
|
const scrollActive = ref("");
|
||||||
|
|
||||||
|
// 卡片距离顶部偏移量
|
||||||
|
const tops = ref<number[]>([]);
|
||||||
|
|
||||||
|
// tab项列表
|
||||||
|
const list = ref<ClTabsItem[]>([
|
||||||
|
{ label: "商品", value: "info" },
|
||||||
|
{ label: "评价", value: "comment" },
|
||||||
|
{ label: "详情", value: "desc" }
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有.card顶部坐标
|
||||||
|
*/
|
||||||
|
async function getTops(): Promise<void> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
uni.createSelectorQuery()
|
||||||
|
.in(proxy?.$root)
|
||||||
|
.selectAll(".card")
|
||||||
|
.boundingClientRect((res) => {
|
||||||
|
const top = page.getScrollTop() - 44 - getSafeAreaHeight("top"); // 去头部高度
|
||||||
|
|
||||||
|
// 只计算有id的card
|
||||||
|
tops.value = (res as NodeInfo[])
|
||||||
|
.filter((e) => e.id != "" && !isNull(e.id))
|
||||||
|
.map((e) => (e.top ?? 0) + top);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.exec();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tab切换
|
||||||
|
*/
|
||||||
|
async function onTabChange(value: string) {
|
||||||
|
// 设置滚动时激活tab
|
||||||
|
scrollActive.value = value;
|
||||||
|
|
||||||
|
// 重新获取卡片位置
|
||||||
|
await getTops();
|
||||||
|
|
||||||
|
// 查找符合当前位置的tab索引
|
||||||
|
const index = list.value.findIndex((e) => e.value == value);
|
||||||
|
if (index < 0) return;
|
||||||
|
|
||||||
|
// 滚动到对应卡片位置
|
||||||
|
page.scrollTo(tops.value[index] + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步当前tab
|
||||||
|
*/
|
||||||
|
const setActive = debounce(() => {
|
||||||
|
active.value = scrollActive.value;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 滚动时激活tab
|
||||||
|
*/
|
||||||
|
function onScroll(top: number) {
|
||||||
|
let index = -1;
|
||||||
|
|
||||||
|
// 查找符合当前位置的tab索引
|
||||||
|
for (let i = 0; i < tops.value.length; i++) {
|
||||||
|
if (top >= tops.value[i]) {
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置激活tab
|
||||||
|
if (index >= 0 && index < list.value.length) {
|
||||||
|
scrollActive.value = list.value[index].value as string;
|
||||||
|
setActive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取卡片位置
|
||||||
|
getTops();
|
||||||
|
|
||||||
|
// 监听页面滚动
|
||||||
|
page.onScroll(onScroll);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 移除监听
|
||||||
|
page.offScroll(onScroll);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -21,4 +21,5 @@ export type ClBannerProps = {
|
|||||||
showDots?: boolean;
|
showDots?: boolean;
|
||||||
disableTouch?: boolean;
|
disableTouch?: boolean;
|
||||||
height?: any;
|
height?: any;
|
||||||
|
imageMode?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user