添加模板页

This commit is contained in:
icssoa
2025-08-20 08:53:58 +08:00
parent 10e17efb67
commit b9f20beee2
8 changed files with 482 additions and 8 deletions

View File

@@ -46,7 +46,7 @@
<view class="p-3">
<view class="group" v-for="item in data" :key="item.label">
<cl-text :pt="{ className: '!text-sm !text-surface-400 mb-2 ml-1' }">{{
<cl-text :pt="{ className: '!text-sm !text-surface-400 mb-2 ml-2' }">{{
item.label
}}</cl-text>

111
pages/index/template.uvue Normal file
View File

@@ -0,0 +1,111 @@
<template>
<cl-page>
<view class="p-3">
<cl-list
v-for="(item, index) in list"
:key="index"
:title="item.label"
:pt="{
className: 'mb-5'
}"
>
<cl-list-item
v-for="child in item.children"
:key="child.label"
:label="child.label"
:arrow="child.path != null"
:pt="{
label: {
className: '!w-auto'
}
}"
@tap="toPath(child)"
>
</cl-list-item>
</cl-list>
</view>
<!-- 自定义底部导航栏 -->
<tabbar></tabbar>
</cl-page>
</template>
<script setup lang="ts">
import Tabbar from "@/components/tabbar.uvue";
import { router } from "@/cool";
import { t } from "@/locale";
import { useUi } from "@/uni_modules/cool-ui";
import { computed } from "vue";
const ui = useUi();
type Item = {
label: string;
path?: string;
children?: Item[];
};
const list = computed<Item[]>(() => [
{
label: t("商城"),
children: [
{
label: t("商品分类"),
path: "/pages/template/shop/goods-category"
},
{
label: t("商品详情")
},
{
label: t("商品列表、筛选")
},
{
label: t("购物车")
},
{
label: t("订单列表、详情")
}
]
},
{
label: t("聊天"),
children: [
{
label: t("对话列表、历史记录")
}
]
},
{
label: "AI",
children: [
{
label: t("流式回复")
},
{
label: t("语言合成")
},
{
label: t("语音识别")
}
]
},
{
label: t("其他"),
children: [
{
label: t("文件管理")
}
]
}
]);
function toPath(item: Item) {
if (item.path == null) {
return ui.showToast({
message: t("该模板正在开发中")
});
}
router.to(item.path!);
}
</script>

View File

@@ -0,0 +1,335 @@
<template>
<cl-page>
<view class="flex flex-col h-full">
<view class="flex flex-row p-3">
<cl-input
:pt="{
className: parseClass(['flex-1 !border-2 !rounded-xl'])
}"
prefix-icon="search-line"
placeholder="iPhone 16 Pro Max"
></cl-input>
</view>
<view class="flex flex-row flex-1">
<!-- 左侧分类列表 -->
<view class="h-full w-[200rpx] bg-white dark:bg-surface-800 mr-2 rounded-tr-xl">
<scroll-view direction="vertical" :show-scrollbar="false" class="h-full">
<view
class="h-[100rpx] p-2"
v-for="(item, index) in list"
:key="item.label"
@click="onCategoryChange(index)"
>
<view
class="flex flex-row items-center justify-center h-full rounded-lg"
:class="[
categoryActive == index
? isDark
? 'bg-primary-500'
: 'bg-primary-50'
: ''
]"
>
<cl-text
:pt="{
className: parseClass([
[
categoryActive == index,
isDark ? '!text-white' : '!text-primary-500',
isDark ? '!text-surface-300' : '!text-surface-500'
]
])
}"
>{{ item.label }}</cl-text
>
</view>
</view>
</scroll-view>
</view>
<!-- 右侧商品列表 -->
<view class="flex-1">
<scroll-view
direction="vertical"
:scroll-into-view="scrollIntoView"
:scroll-with-animation="!scrollLock"
class="h-full"
@scroll="onScroll"
>
<view class="pr-2">
<view
class="category-item flex rounded-xl bg-white dark:bg-surface-800 mb-2 pb-3"
v-for="(item, index) in list"
:key="item.label"
:id="`category-item-${index}`"
>
<cl-text
:pt="{
className: 'p-3'
}"
>{{ item.label }}</cl-text
>
<view class="px-1">
<cl-row :gutter="10">
<cl-col
v-for="goods in item.list"
:key="goods.name"
:span="8"
>
<view class="flex items-center flex-col justify-center">
<cl-image :src="goods.image"></cl-image>
<cl-text
:ellipsis="true"
:pt="{ className: '!text-xs text-center mt-2' }"
>{{ goods.name }}</cl-text
>
</view>
</cl-col>
</cl-row>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</cl-page>
</template>
<script setup lang="ts">
import { isDark, parseClass } from "@/cool";
import { getCurrentInstance, ref } from "vue";
const { proxy } = getCurrentInstance()!;
// 商品类型
type Goods = {
name: string;
price: number;
image: string;
};
// 分类类型
type Category = {
label: string;
top?: number;
list: Goods[];
};
// 商品分类示例数据
const list = ref<Category[]>([
{
label: "推荐",
list: [
{
name: "iPhone 15 Pro",
price: 8999,
image: "/static/goods/iphone15pro.png"
},
{
name: "华为 Mate 60 Pro",
price: 6999,
image: "/static/goods/mate60pro.png"
},
{
name: "小米 14 Ultra",
price: 5999,
image: "/static/goods/xiaomi14ultra.png"
}
]
},
{
label: "Apple",
list: [
{
name: "iPhone 15 Pro",
price: 8999,
image: "/static/goods/iphone15pro.png"
},
{
name: "iPhone 14",
price: 5999,
image: "/static/goods/iphone14.png"
}
]
},
{
label: "华为",
list: [
{
name: "华为 Mate 60 Pro",
price: 6999,
image: "/static/goods/mate60pro.png"
},
{
name: "华为 P60",
price: 4999,
image: "/static/goods/p60.png"
}
]
},
{
label: "小米",
list: [
{
name: "小米 14 Ultra",
price: 5999,
image: "/static/goods/xiaomi14ultra.png"
},
{
name: "小米 13",
price: 3999,
image: "/static/goods/xiaomi13.png"
}
]
},
{
label: "三星",
list: [
{
name: "三星 Galaxy S24",
price: 7999,
image: "/static/goods/galaxys24.png"
},
{
name: "三星 Galaxy Z Flip5",
price: 8999,
image: "/static/goods/galaxyzflip5.png"
}
]
},
{
label: "OPPO",
list: [
{
name: "OPPO Find X7",
price: 4999,
image: "/static/goods/findx7.png"
}
]
},
{
label: "VIVO",
list: [
{
name: "VIVO X100 Pro",
price: 5999,
image: "/static/goods/x100pro.png"
}
]
},
{
label: "荣耀",
list: [
{
name: "荣耀 Magic6",
price: 4599,
image: "/static/goods/magic6.png"
}
]
},
{
label: "一加",
list: [
{
name: "一加 12",
price: 4299,
image: "/static/goods/oneplus12.png"
}
]
},
{
label: "红米",
list: [
{
name: "红米 K70 Pro",
price: 3299,
image: "/static/goods/k70pro.png"
}
]
},
{
label: "魅族",
list: [
{
name: "魅族 21",
price: 3999,
image: "/static/goods/meizu21.png"
}
]
},
{
label: "坚果",
list: [
{
name: "坚果 R2",
price: 2999,
image: "/static/goods/nutR2.png"
}
]
},
{
label: "其他",
list: [
{
name: "诺基亚 X30",
price: 2599,
image: "/static/goods/nokiax30.png"
}
]
}
]);
// 滚动到指定分类
const scrollIntoView = ref("");
// 滚动锁定
const scrollLock = ref(false);
// 当前选中的分类
const categoryActive = ref(0);
// 分类切换
function onCategoryChange(index: number) {
categoryActive.value = index;
scrollIntoView.value = `category-item-${index}`;
scrollLock.value = true;
setTimeout(() => {
scrollLock.value = false;
}, 50);
}
// 滚动时,更新当前选中的分类
function onScroll(e: UniScrollEvent) {
if (scrollLock.value) return;
const top = e.detail.scrollTop;
list.value.forEach((e, i) => {
if (top >= e.top!) {
categoryActive.value = i;
}
});
}
// 初始化
function init() {
uni.createSelectorQuery()
.in(proxy!.$root)
.selectAll(".category-item")
.boundingClientRect((res) => {
(res as NodeInfo[]).forEach((e, i, arr) => {
list.value[i].top = (e.top ?? 0) - (arr[0].top ?? 0);
});
})
.exec();
}
onReady(() => {
init();
});
</script>