Explorar o código

添加模板页

icssoa hai 7 meses
pai
achega
b9f20beee2

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 	"name": "cool-unix",
-	"version": "8.0.10",
+	"version": "8.0.11",
 	"license": "MIT",
 	"scripts": {
 		"build-ui": "node ./uni_modules/cool-ui/scripts/generate-types.js",

+ 28 - 5
pages.json

@@ -11,6 +11,12 @@
 			"style": {
 				"navigationStyle": "custom"
 			}
+		},
+		{
+			"path": "pages/index/template",
+			"style": {
+				"navigationBarTitleText": "%模板%"
+			}
 		}
 	],
 	"subPackages": [
@@ -20,19 +26,19 @@
 				{
 					"path": "index",
 					"style": {
-						"navigationBarTitleText": "%page.设置%"
+						"navigationBarTitleText": "%设置%"
 					}
 				},
 				{
 					"path": "general",
 					"style": {
-						"navigationBarTitleText": "%page.通用设置%"
+						"navigationBarTitleText": "%通用设置%"
 					}
 				},
 				{
 					"path": "notice",
 					"style": {
-						"navigationBarTitleText": "%page.通知设置%"
+						"navigationBarTitleText": "%通知设置%"
 					}
 				},
 				{
@@ -44,7 +50,7 @@
 				{
 					"path": "cs",
 					"style": {
-						"navigationBarTitleText": "%page.联系客服%"
+						"navigationBarTitleText": "%联系客服%"
 					}
 				}
 			]
@@ -55,7 +61,7 @@
 				{
 					"path": "edit",
 					"style": {
-						"navigationBarTitleText": "%page.编辑资料%"
+						"navigationBarTitleText": "%编辑资料%"
 					}
 				},
 				{
@@ -413,6 +419,17 @@
 					}
 				}
 			]
+		},
+		{
+			"root": "pages/template",
+			"pages": [
+				{
+					"path": "shop/goods-category",
+					"style": {
+						"navigationBarTitleText": "%商品分类%"
+					}
+				}
+			]
 		}
 	],
 	"globalStyle": {
@@ -437,6 +454,12 @@
 				"text": "%首页%"
 			},
 			{
+				"pagePath": "pages/index/template",
+				"iconPath": "/static/icon/tabbar/template.png",
+				"selectedIconPath": "/static/icon/tabbar/template2.png",
+				"text": "%模板%"
+			},
+			{
 				"pagePath": "pages/index/my",
 				"iconPath": "/static/icon/tabbar/my.png",
 				"selectedIconPath": "/static/icon/tabbar/my2.png",

+ 1 - 1
pages/index/home.uvue

@@ -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 - 0
pages/index/template.uvue

@@ -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>

+ 335 - 0
pages/template/shop/goods-category.uvue

@@ -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>

+ 6 - 1
router/index.ts

@@ -3,6 +3,7 @@ import { router, useStore } from "@/cool";
 const ignoreToken = [
 	"/pages/index/home",
 	"/pages/index/my",
+	"/pages/index/template",
 	"/pages/user/login",
 	"/pages/user/doc"
 ];
@@ -10,7 +11,11 @@ const ignoreToken = [
 router.beforeEach((to, next) => {
 	const { user } = useStore();
 
-	if (ignoreToken.includes(to.path) || to.path.startsWith("/pages/demo")) {
+	if (
+		ignoreToken.includes(to.path) ||
+		to.path.startsWith("/pages/demo") ||
+		to.path.startsWith("/pages/template")
+	) {
 		next();
 	} else {
 		if (!user.isNull()) {

BIN=BIN
static/icon/tabbar/template.png


BIN=BIN
static/icon/tabbar/template2.png