一、下载HBuilder
https://www.dcloud.io/hbuilderx.html
上述网址下载对应版本,下载完成后进行解压,不需要安装,解压完成后,点击HBuilder X.exe文件进行运行程序
二、创建uni-app项目
此处我是按照文档创建的uni-ui项目模板
三、运行uni-app小程序项目
切记上述运行前提是必须安装微信开发者工具,且微信开发者工具要开启服务端口
四、下载安装ThorUI
ThorUI的官方网址:https://thorui.cn/doc/docs/started.html
1、在HBuilderx中的终端进行安装
npm install thorui-uni
2、开启“easycom”组件模式,官网:https://uniapp.dcloud.net.cn/collocation/pages.html#easycom
以下是我踩得坑,一定要注意自己安装的位置
五、下载安装uview-plus(基于vue3)
1、安装
官方网址:https://uiadmin.net/uview-plus/components/install.html
安装命令
npm install uview-plus
也可以通过HBuilder X的方式直接导入
下载地址:https://ext.dcloud.net.cn/plugin?name=uview-plus
注:此处的下载导入需要扫码看广告视频,不太推荐
2、配置uview-plus
详细请看官网:https://uiadmin.net/uview-plus/components/npmSetting.html
- 安装scss依赖
// 安装sass
npm i sass -D
// 安装sass-loader,注意需要版本10,否则可能会导致vue与sass的兼容问题而报错
npm i sass-loader@10 -D
- 引入uview-plus主JS库
// main.js
import uviewPlus from 'uview-plus'
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.use(uviewPlus)
return {
app
}
}
// #endif
- 在引入uview-plus的全局SCSS主题文件
/* uni.scss */
@import 'uview-plus/theme.scss';
- 引入uview-plus基础样式
注意!
在App.vue
中首行的位置引入,注意给style标签加入lang="scss"属性
<style lang="scss">
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
@import "uview-plus/index.scss";
</style>
- 配置easycom组件模式
注意:
pages.json
中只有一个easycom
字段,否则请自行合并多个引入规则。
// pages.json
{
"easycom": {
"autoscan": true,
// 注意一定要放在custom里,否则无效,https://ask.dcloud.net.cn/question/131175
"custom": {
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
}
},
// 此为本身已有的内容
"pages": [
// ......
]
}
3、按照上述配置后会出现以下报错,不显示页面
VM5088:1788 页面【node-modules/uview-plus/components/u-popup/u-popup]错误:
TypeError: Cannot read property 'options' of undefined
at vendor.js? [sm]:7425
at Array.forEach (<anonymous>)
at parseComponent (vendor.js? [sm]:7424)
at Object.createComponent2 [as createComponent] (vendor.js? [sm]:7468)
at u-popup.js? [sm]:219
at WASubContext.js?t=wechat&s=1722383909825&v=3.5.2:1
at p.runWith (WASubContext.js?t=wechat&s=1722383909825&v=3.5.2:1)
at q (WASubContext.js?t=wechat&s=1722383909825&v=3.5.2:1)
at <anonymous>:1785:7
at doWhenAllScriptLoaded (<anonymous>:1974:21)(env: Windows,mp,1.06.2301160; lib: 3.5.2)
(anonymous) @ VM5088:1788
doWhenAllScriptLoaded @ VM5045:1974
(anonymous) @ VM5045:15
loadBabelModules @ assubloader.js:1
async function (async)
loadBabelModules @ assubloader.js:1
window.loadBabelMod @ VM5015:10
(anonymous) @ possibleConstructorReturn.js:2
WAServiceMainContext.js?t=wechat&s=1722383909825&v=3.5.2:1 TypeError: Cannot read property 'options' of undefined
at vendor.js? [sm]:7425
at Array.forEach (<anonymous>)
at parseComponent (vendor.js? [sm]:7424)
at Object.createComponent2 [as createComponent] (vendor.js? [sm]:7468)
at u-popup.js? [sm]:219
at WASubContext.js?t=wechat&s=1722383909825&v=3.5.2:1
at p.runWith (WASubContext.js?t=wechat&s=1722383909825&v=3.5.2:1)
at q (WASubContext.js?t=wechat&s=1722383909825&v=3.5.2:1)
at <anonymous>:1785:7
at doWhenAllScriptLoaded (<anonymous>:1974:21)(env: Windows,mp,1.06.2301160; lib: 3.5.2)
解决办法:
HBuilder X关闭后,重新启动项目,微信开发者工具就不报错了
系统整体颜色:#2979ff
六、添加底部导航栏
1、在iconfont寻找图标:https://www.iconfont.cn/?spm=a313x.search_index.i3.2.72f83a81Kzq4c1
2、创建页面,添加到pages.json页面
3、在pages…json中添加tabBar
{
/*
开启easycom组件模式,不需要再执行第一种引入操作即可使用
注意组件的位置是否和示例中一致,如果不一致需要自行调整下方地址
*/
"easycom": {
"autoscan": true,
"custom": {
"tui-(.*)": "thorui-uni/lib/thorui/tui-$1/tui-$1.vue",
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
}
},
"pages": [{
"path": "pages/index/index"
},
{
"path": "pages/classification/classification",
"style": {
"navigationBarTitleText": "商品分类",
"enablePullDownRefresh": false
}
},
{
"path": "pages/shoppingCart/shoppingCart",
"style": {
"navigationBarTitleText": "购物中心",
"enablePullDownRefresh": false
}
},
{
"path": "pages/personalCenter/personalCenter",
"style": {
"navigationBarTitleText": "个人中心",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "吉吉小卖部",
"navigationBarBackgroundColor": "#2979ff",
"backgroundColor": "#F8F8F8"
},
"tabBar": {
"selectedColor": "#2979ff",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/images/tabbar/首页-未选中.png",
"selectedIconPath": "static/images/tabbar/首页.png",
"text": "首页"
}, {
"pagePath": "pages/classification/classification",
"iconPath": "static/images/tabbar/全部业务-未选中.png",
"selectedIconPath": "static/images/tabbar/全部业务.png",
"text": "分类"
},
{
"pagePath": "pages/shoppingCart/shoppingCart",
"iconPath": "static/images/tabbar/购物车空.png",
"selectedIconPath": "static/images/tabbar/购物车满.png",
"text": "购物车"
}, {
"pagePath": "pages/personalCenter/personalCenter",
"iconPath": "static/images/tabbar/我的-未选中.png",
"selectedIconPath": "static/images/tabbar/我的.png",
"text": "我的"
}
]
}
}
七、使用uviewplus自定义底部导航栏菜单
注:此处踩了太多坑
主要采用pinia库和uniapp进行编写
1、pinia的介绍
在选择 Pinia 还是 Vuex 时,需要考虑到你的项目的需求和使用的 Vue 版本。如果是使用 Vue 3,并且更注重性能和体积方面的优化 Pinia 可能是一个更好的选择,而对于 Vue 2 应用程序 或更喜欢 Vuex 的丰富生态系统和广泛支持的开发者来说,Vuex 仍然是一个强大而可靠的选择。Vuex是Vue.js官方提供的状态管理库,而Pinia是由Vue作者维护的另一个状态管理库。Vuex采用了集中式的架构,将所有的状态存储在一个单一的全局状态树中,通过mutations和actions来修改和处理状态。而Pinia采用了去中心化的架构,将状态分布在多个模块中,每个模块拥有自己的状态、mutations和actions。
总结:若使用底部导航栏就使用pinia,其实我也不明白他俩有什么区别,上述文字均来自CSDN,另外若使用HBuilder X,则不需要安装pinia,HBuilder X自带了。
2、配置pinia
1)在main.js文件中引入pinia库
// #ifndef VUE3
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
import uviewPlus from 'uview-plus'
import tabBarVue from './pages/tabBar/tabBar.vue'
import App from './App.vue'
import * as Pinia from 'pinia';
export function createApp() {
const app = createSSRApp(App)
app.use(uviewPlus)
app.use(Pinia.createPinia());
app.component('tabBarVue',tabBarVue)
return {
app,
tabBarVue,
Pinia //此处必须返回Pinia
}
}
// #endif
2) 文件中创建store文件夹,store中新建user.js文件,编写共享状态代码
import { defineStore } from 'pinia';
//创建用户小仓库
const useUserStore = defineStore('User', {
state: () => {
return {
activeTab: 0 // 默认选中索引,我默认选第一个
}
},
actions: {
// 设置active的值
setTabbarActive(active) {
this.activeTab = active;
}
}
})
//暴露用户小仓库
export default useUserStore
3、页面中引入pinia库
在page文件夹下创建tabbar组件,并在main.js文件中引入(请看上述截图)
- 配置pages.json中的tabbar
"tabBar": {
// "custom":true, 此处看网上有人说要配置这里,才会显示自定义的底部导航栏,但我写了之后报错了,不知道是为什么?
"selectedColor": "#2979ff",
"list": [{
"pagePath": "pages/pageHome/pageHome"
}, {
"pagePath": "pages/classification/classification"
},
{
"pagePath": "pages/shoppingCart/shoppingCart"
}, {
"pagePath": "pages/personalCenter/personalCenter"
}
]
}
- 编辑tabBar.vue文件
<template>
<view>
<up-tabbar :value="useStore.activeTab" :fixed="true" :placeholder="false" activeColor="#2979ff"
:safeAreaInsetBottom="false">
<up-tabbar-item v-for="(item,index) in tabbarItems" :key="index" :text="item.text"
@click="pageChange(item,index)">
<template #inactive-icon>
<image class="u-page__item__slot-icon" :src="item.iconPath"></image>
</template>
<template #active-icon>
<image class="u-page__item__slot-icon" :src="item.selectedIconPath"></image>
</template>
</up-tabbar-item>
</up-tabbar>
</view>
</template>
<script setup>
import {
ref
} from 'vue';
import useUserStore from '../../store/user.js'
const useStore = useUserStore()
uni.hideTabBar()
const value = ref(0)
//引入pinia仓库
const tabbarItems = [{
"pagePath": "../pageHome/pageHome",
"iconPath": "../../static/images/tabbar/首页-未选中.png",
"selectedIconPath": "../../static/images/tabbar/首页.png",
"text": "首页"
}, {
"pagePath": "../classification/classification",
"iconPath": "../../static/images/tabbar/全部业务-未选中.png",
"selectedIconPath": "../../static/images/tabbar/全部业务.png",
"text": "分类"
},
{
"pagePath": "../shoppingCart/shoppingCart",
"iconPath": "../../static/images/tabbar/购物车空.png",
"selectedIconPath": "../../static/images/tabbar/购物车满.png",
"text": "购物车"
}, {
"pagePath": "../personalCenter/personalCenter",
"iconPath": "../../static/images/tabbar/我的-未选中.png",
"selectedIconPath": "../../static/images/tabbar/我的.png",
"text": "我的"
}
]
//点击tabbar按钮
const pageChange = (item, index) => {
console.log("useStore.activeTab:" + useStore.activeTab)
console.log(index)
if (useStore.activeTab !== index) {
useStore.setTabbarActive(index)
const path = item.pagePath
uni.switchTab({
url: path
})
}
}
</script>
<style>
.u-page__item__slot-icon {
height: 20px;
width: 20px;
padding-top: 8px;
}
</style>
- 在各个组件中进行引入,例如:pageHome
<template>
<tabBarVue></tabBarVue>
<view>
系统首页
</view>
</template>
<script setup></script>
<style></style>
八、制作系统首页
效仿黑马程序员小兔鲜小程序素材
小程序默认页码修改在pages.json的list中的第一个为显示页
{
"pages": [
"pages/start/start",
"pages/index/index",
// ...其他页面路径
],
// ...其他配置项
}
1、安全区域
不同手机的安全区域不同,适配安全区域能防止页面重要内容被遮挡。
可通过 uni.getSystemInfoSync()
获取屏幕边界到安全区的距离。
2、自定义导航栏配置
注:因导航栏都差不多所以没有用up-navbar
1)创建customNavBar组件
<template>
<view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
<!-- logo文字 -->
<view class="logo">
<image class="logo-image" src="@/static/images/navbar/logo.png"></image>
<text class="logo-text">新鲜 · 亲民 · 快捷</text>
</view>
<!-- 搜索条 -->
<view class="search">
<text class="icon-search">搜索商品</text>
<text class="icon-scan"></text>
</view>
</view>
</template>
<script setup lang="ts">
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
</script>
<style lang="scss">
/* 自定义导航条 */
.navbar {
background-image: url(@/static/images/navbar/navigator_bg.jpg);
background-size: cover;
position: relative;
display: flex;
flex-direction: column;
padding-top: 20px;
.logo {
display: flex;
align-items: center;
height: 64rpx;
padding-left: 30rpx;
padding-top: 20rpx;
.logo-image {
width: 100rpx;
height: 80rpx;
border-radius: 50%;
}
.logo-text {
flex: 1;
line-height: 30rpx;
color: #fff;
margin: 2rpx 0 0 20rpx;
padding-left: 20rpx;
border-left: 1rpx solid #fff;
font-size: 32rpx;
}
}
.search {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10rpx 0 26rpx;
height: 64rpx;
margin: 16rpx 20rpx;
color: #fff;
font-size: 28rpx;
border-radius: 32rpx;
background-color: rgba(255, 255, 255, 0.5);
}
.icon-search {
&::before {
margin-right: 10rpx;
}
}
.icon-scan {
font-size: 30rpx;
padding: 15rpx;
}
}
</style>
2)全局注册
import customNavbarVue from './pages/customNavbar/customNavbar.vue'
export function createApp() {
const app = createSSRApp(App)
app.use(uviewPlus)
app.use(Pinia.createPinia());
app.component('tabBarVue',tabBarVue)
app.component('customNavbarVue',customNavbarVue)
return {
app,
tabBarVue,
customNavbarVue,
Pinia //此处必须返回Pinia
}
}
3)在首页homePage中引入
注:此处需要修改显示的首页为homePage而不是index,方法参考上述方法
<template>
<customNavbarVue></customNavbarVue>
<tabBarVue></tabBarVue>
<view>
系统首页
</view>
</template>
<script setup>
</script>
<style>
</style>
3、制作轮播图广告页
编写组件XtxSwiper,引入到首页对应部分
<template>
<view class="carousel">
<up-swiper
:list="SwiperList"
keyName="image"
showTitle
:autoplay="true"
:duration="200"
circular
></up-swiper>
</view>
</template>
<script setup>
import { ref } from 'vue'
const activeIndex = ref(0)
// 使用 reactive 创建响应式对象数组
const SwiperList = ref([
{
image: 'https://cdn.uviewui.com/uview/swiper/swiper2.png',
title: '昨夜星辰昨夜风,画楼西畔桂堂东',
},
{
image: 'https://cdn.uviewui.com/uview/swiper/swiper1.png',
title: '身无彩凤双飞翼,心有灵犀一点通',
},
{
image: 'https://cdn.uviewui.com/uview/swiper/swiper3.png',
title: '谁念西风独自凉,萧萧黄叶闭疏窗,沉思往事立残阳',
},
]);
</script>
<style lang="scss">
/* 轮播图 */
.carousel {
height: 280rpx;
position: relative;
overflow: hidden;
transform: translateY(0);
background-color: #efefef;
.indicator {
position: absolute;
left: 0;
right: 0;
bottom: 16rpx;
display: flex;
justify-content: center;
.dot {
width: 30rpx;
height: 6rpx;
margin: 0 8rpx;
border-radius: 6rpx;
background-color: rgba(255, 255, 255, 0.4);
}
.active {
background-color: #fff;
}
}
.navigator,
.image {
width: 100%;
height: 100%;
}
}
</style>
4、制作选择样式
创建CategoryPanel组件,在首页注册使用
<template>
<view class="category">
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-冰箱-94.png"></image>
<text class="text">居家</text>
</navigator>
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-三明治用煎蛋-94.png"></image>
<text class="text">居家</text>
</navigator>
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-服装-94.png"></image>
<text class="text">服饰</text>
</navigator>
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-奶嘴-94.png"></image>
<text class="text">母婴</text>
</navigator>
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-口红表情符号-96.png"></image>
<text class="text">个护</text>
</navigator>
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-车辆-94.png"></image>
<text class="text">严选</text>
</navigator>
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-笔记本电脑-94.png"></image>
<text class="text">数码</text>
</navigator>
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-篮球-94.png"></image>
<text class="text">运动</text>
</navigator>
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-标签-94.png"></image>
<text class="text">杂项</text>
</navigator>
<navigator class="category-item" hover-class="none" url="/pages/pageHome/pageHome">
<image class="icon" src="@/static/images/news/icons8-奖-94.png"></image>
<text class="text">品牌</text>
</navigator>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// const list = ref([
// {
// id:0,
// icon: '',
// name: '昨夜星辰昨夜风,画楼西畔桂堂东',
// },
// { id:1,
// image: 'https://cdn.uviewui.com/uview/swiper/swiper1.png',
// title: '身无彩凤双飞翼,心有灵犀一点通',
// },
// { id:2,
// image: 'https://cdn.uviewui.com/uview/swiper/swiper3.png',
// title: '谁念西风独自凉,萧萧黄叶闭疏窗,沉思往事立残阳',
// },
// ]);
</script>
<style lang="scss">
/* 前台类目 */
.category {
margin: 20rpx 0 0;
padding: 10rpx 0;
display: flex;
flex-wrap: wrap;
min-height: 328rpx;
.category-item {
width: 150rpx;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
box-sizing: border-box;
.icon {
width: 100rpx;
height: 100rpx;
}
.text {
font-size: 26rpx;
color: #666;
}
}
}
</style>
上述的数据均为死数据
5、热门推荐
同上述一样编写组件,在homePage.vue中进行注册引入
<template>
<view class="panel hot">
<up-grid :border="false" col="2">
<up-grid-item v-for="(listItem,listIndex) in list" :key="listIndex" class="item">
<view class="title">
<text class="title-text">{{ listItem.title }}</text>
<text class="title-desc">{{ listItem.alt }}</text>
</view>
<view class="cards">
<!-- <image :key="listItem.pictures0" class="picture" mode="aspectFit" :src="listItem.pictures0"></image>
<image :key="listItem.pictures1" class="picture" mode="aspectFit" :src="listItem.pictures1"></image> -->
<image v-for="src in listItem.pictures" :key="src" class="picture" mode="aspectFit" :src="src"></image>
</view>
</up-grid-item>
</up-grid>
<up-toast ref="uToastRef" />
</view>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
// 创建响应式数据
const list = reactive([
{
id: 0,
alt: "精选全攻略",
pictures: [
"../../static/images/HotPanel/1-1.jpg",
"../../static/images/HotPanel/1-2.jpg"
],
target: "",
title: "特惠推荐",
type: ""
},
{
id: 1,
alt: "最受欢迎",
pictures: [
"../../static/images/HotPanel/1-3.jpg",
"../../static/images/HotPanel/1-4.jpg"
],
// pictures0: "../../static/images/HotPanel/1-3.jpg",
// pictures1: "../../static/images/HotPanel/1-4.jpg",
target: "",
title: "爆款推荐",
type: ""
},
{
id: 2,
alt: "精心优选",
pictures: [
"../../static/images/HotPanel/2-1.jpg",
"../../static/images/HotPanel/2-2.jpg"
],
// pictures0: "../../static/images/HotPanel/2-1.jpg",
// pictures1: "../../static/images/HotPanel/2-2.jpg",
target: "",
title: "一站买全",
type: ""
},
{
id: 3,
alt: "生活加分项",
pictures: [
"../../static/images/HotPanel/2-3.jpg",
"../../static/images/HotPanel/2-4.jpg"
],
// pictures0: "../../static/images/HotPanel/2-3.jpg",
// pictures1: "../../static/images/HotPanel/2-4.jpg",
target: "",
title: "新鲜好物",
type: ""
}
]);
// 创建对子组件的引用
const uToastRef = ref(null);
// 定义方法
const click = (name) => {
if (uToastRef.value) {
uToastRef.value.success(`点击了第${name + 1}个`); // 注意:这里加1是因为通常我们是从第1个开始计数的
}
};
</script>
<style lang="scss">
/* 热门推荐 */
.hot {
display: flex;
flex-wrap: wrap;
min-height: 508rpx;
margin: 20rpx 20rpx 0;
border-radius: 10rpx;
background-color: #fff;
.title {
display: flex;
align-items: center;
padding: 24rpx 24rpx 0;
font-size: 32rpx;
color: #262626;
position: relative;
.title-desc {
font-size: 24rpx;
color: #7f7f7f;
margin-left: 18rpx;
}
}
.item {
display: flex;
flex-direction: column;
width: 50%;
height: 254rpx;
border-right: 1rpx solid #eee;
border-top: 1rpx solid #eee;
.title {
justify-content: start;
}
&:nth-child(2n) {
border-right: 0 none;
}
&:nth-child(-n + 2) {
border-top: 0 none;
}
.image {
width: 100rpx;
height: 100rpx;
}
}
.cards {
flex: 1;
padding: 15rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
}
.picture {
flex: 1;
display: flex;
justify-content: space-between;
align-items: center;
width: 150rpx;
height: 150rpx;
}
.grid-text {
font-size: 14px;
color: #909399;
padding: 10rpx 0 20rpx 0rpx;
/* #ifndef APP-PLUS */
box-sizing: border-box;
/* #endif */
}
</style>
6、猜你喜欢
<template>
<!-- 猜你喜欢 -->
<view class="caption">
<text class="text">猜你喜欢</text>
</view>
<view class="guess">
<up-grid :border="false" col="2">
<up-grid-item v-for="(listItem,listIndex) in list" :key="listIndex" class="guess-item">
<image :src="listItem.picture" class="image" mode="aspectFill"></image>
<view class="name"> {{listItem.name}} </view>
<view class="price">
<text class="small">¥</text>
<text>{{listItem.price}}</text>
</view>
</up-grid-item>
</up-grid>
<up-toast ref="uToastRef" />
</view>
<view class="loading-text"> 正在加载... </view>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
const list = reactive([
{
id: 0,
name: "富到流油的高邮咸鸭蛋75克*10枚",
picture: "../../static/images/HotPanel/1-1.jpg",
price: "33.00"
},
{
id: 1,
name: "川味牛肉辣椒酱190克",
picture: "../../static/images/HotPanel/1-3.jpg",
price: "38.00"
},
{
id: 2,
name: "北海道风味大虾面61克*6杯",
picture: "../../static/images/HotPanel/1-4.jpg",
price: "39.00"
},
{
id: 3,
name: "【囤货装】日本橙油精华去油污泡沫3瓶装",
picture: "../../static/images/HotPanel/2-1.jpg",
price: "67.50"
},{
id: 4,
name: "快速吸水防回渗,宠物训导垫S码80片/袋",
picture: "../../static/images/HotPanel/2-2.jpg",
price: "35.80"
},{
id: 5,
name: "还原猫咪排泄天性,进口膨润土砂6千克",
picture: "../../static/images/HotPanel/2-3.jpg",
price: "50.88"
}
]);
</script>
<style lang="scss">
:host {
display: block;
}
/* 分类标题 */
.caption {
display: flex;
justify-content: center;
line-height: 1;
padding: 36rpx 0 40rpx;
font-size: 32rpx;
color: #262626;
.text {
display: flex;
justify-content: center;
align-items: center;
padding: 0 28rpx 0 30rpx;
&::before,
&::after {
content: '';
width: 20rpx;
height: 20rpx;
background-image: url(@/static/images/bubble.png);
background-size: contain;
margin: 0 10rpx;
}
}
}
/* 猜你喜欢 */
.guess {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 0 20rpx;
.guess-item {
width: 345rpx;
padding: 24rpx 20rpx 20rpx;
margin-bottom: 20rpx;
border-radius: 10rpx;
overflow: hidden;
background-color: #fff;
}
.image {
width: 304rpx;
height: 304rpx;
}
.name {
height: 75rpx;
margin: 10rpx 0;
font-size: 26rpx;
color: #262626;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.price {
line-height: 1;
margin-top: 1rpx;
padding-right: 185rpx;
padding-bottom: 35rpx;
color: #cf4444;
font-size: 26rpx;
}
.small {
font-size: 80%;
}
}
.picture {
flex: 1;
display: flex;
justify-content: space-between;
align-items: center;
width: 150rpx;
height: 150rpx;
}
// 加载提示文字
.loading-text {
text-align: center;
font-size: 28rpx;
color: #666;
padding: 20rpx 0;
}
</style>
创建组件在homePage页面引用
<!-- 滚动容器 -->
<scroll-view scroll-y @scrolltolower="onScrolltolower">
<!-- 猜你喜欢 -->
<XtxGuessVue ref="guessRef" />
</scroll-view>
7、TODO 推荐模块跳转到详情页面
九、TODO 分类页面
<script setup lang="ts">
import XtxSwiperVue from '../XtxSwiper/XtxSwiper.vue';
import customNavbarVue from '../customNavbar/customNavbar.vue';
import tabBarVue from '../tabBar/tabBar.vue';
</script>
<template>
<customNavbarVue/>
<view class="viewport">
<!-- 搜索框 -->
<!-- <view class="search">
<view class="input">
<text class="icon-search">女靴</text>
</view>
</view> -->
<!-- 分类 -->
<view class="categories">
<!-- 左侧:一级分类 -->
<scroll-view class="primary" scroll-y>
<view v-for="(item, index) in 10" :key="item" class="item" :class="{ active: index === 0 }">
<text class="name"> 居家 </text>
</view>
</scroll-view>
<!-- 右侧:二级分类 -->
<scroll-view class="secondary" scroll-y>
<!-- 焦点图 -->
<XtxSwiperVue class="banner" :list="[]" />
<!-- 内容区域 -->
<view class="panel" v-for="item in 3" :key="item">
<view class="title">
<up-text class="little" size="12px" text="宠物用品"></up-text>
<!-- <navigator class="more" hover-class="none">全部
<up-icon name="arrow-right"></up-icon></navigator> -->
<up-text suffixIcon="arrow-rightward" iconStyle="font-size: 12px" size="12px" text="全部" align="right"></up-text>
</view>
<view class="section">
<navigator
v-for="goods in 4"
:key="goods"
class="goods"
hover-class="none"
:url="`/pages/goods/goods?id=`"
>
<image
class="image"
src="https://yanxuan-item.nosdn.127.net/674ec7a88de58a026304983dd049ea69.jpg"
></image>
<view class="name ellipsis">木天蓼逗猫棍</view>
<view class="price">
<text class="symbol">¥</text>
<text class="number">16.00</text>
</view>
</navigator>
</view>
</view>
</scroll-view>
</view>
</view>
<tabBarVue/>
</template>
<style lang="scss">
page {
height: 100%;
overflow: hidden;
}
.little{
padding-top: 1px;
}
.viewport {
height: 100%;
display: flex;
flex-direction: column;
}
.search {
padding: 0 30rpx 20rpx;
background-color: #fff;
.input {
display: flex;
align-items: center;
justify-content: space-between;
height: 64rpx;
padding-left: 26rpx;
color: #8b8b8b;
font-size: 28rpx;
border-radius: 32rpx;
background-color: #f3f4f4;
}
}
.icon-search {
&::before {
margin-right: 10rpx;
}
}
/* 分类 */
.categories {
flex: 1;
min-height: 400rpx;
display: flex;
}
/* 一级分类 */
.primary {
overflow: hidden;
width: 180rpx;
flex: none;
background-color: #f6f6f6;
.item {
display: flex;
justify-content: center;
align-items: center;
height: 96rpx;
font-size: 26rpx;
color: #595c63;
position: relative;
&::after {
content: '';
position: absolute;
left: 42rpx;
bottom: 0;
width: 96rpx;
border-top: 1rpx solid #e3e4e7;
}
}
.active {
background-color: #fff;
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 8rpx;
height: 100%;
background-color: #27ba9b;
}
}
}
.primary .item:last-child::after,
.primary .active::after {
display: none;
}
/* 二级分类 */
.secondary {
background-color: #fff;
.carousel {
height: 250rpx;
width: 280px;
margin: 0 10rpx 20rpx;
border-radius: 4rpx;
overflow: hidden;
}
.panel {
margin: 0 35rpx 5rpx 10px;
}
.title {
display: flex;
height: 60rpx;
line-height: 60rpx;
color: #333;
font-size: 28rpx;
border-bottom: 1rpx solid #f7f7f8;
.more {
float: right;
padding-left: 20rpx;
font-size: 24rpx;
color: #999;
}
}
.more {
&::after {
font-family: 'erabbit' !important;
content: '\e6c2';
}
}
.section {
width: 100%;
display: flex;
flex-wrap: wrap;
padding: 20rpx 0;
.goods {
width: 150rpx;
margin: 0rpx 30rpx 20rpx 0;
&:nth-child(3n) {
margin-right: 0;
}
image {
width: 150rpx;
height: 150rpx;
}
.name {
padding: 1rpx;
padding-top: 5px;
font-size: 22rpx;
color: #333;
}
.price {
padding: 5rpx;
font-size: 18rpx;
color: #cf4444;
}
.number {
font-size: 24rpx;
margin-left: 2rpx;
}
}
}
}
.banner{
width: 110%;
}
</style>
<template>
<view class="u-wrap">
<view class="u-search-box">
<view class="u-search-inner">
<up-icon name="search" color="#909399" :size="28"></up-icon>
<text class="u-search-text">搜索</text>
</view>
</view>
<view class="u-menu-wrap">
<scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop"
:scroll-into-view="itemId">
<view v-for="(item,index) in tabbar" :key="index" class="u-tab-item" :class="[current == index ? 'u-tab-item-active' : '']"
@tap.stop="swichMenu(index)">
<text class="u-line-1">{{item.name}}</text>
</view>
</scroll-view>
<scroll-view :scroll-top="scrollRightTop" scroll-y scroll-with-animation class="right-box" @scroll="rightScroll">
<view class="page-view">
<view class="class-item" :id="'item' + index" v-for="(item , index) in tabbar" :key="index">
<view class="item-title">
<text>{{item.name}}</text>
</view>
<view class="item-container">
<view class="thumb-box" v-for="(item1, index1) in item.foods" :key="index1">
<image class="item-menu-image" :src="item1.icon" mode=""></image>
<view class="item-menu-name">{{item1.name}}</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import classifyData from '@/common/classify.data.js';
export default {
data() {
return {
scrollTop: 0, //tab标题的滚动条位置
oldScrollTop: 0,
current: 0, // 预设当前项的值
menuHeight: 0, // 左边菜单的高度
menuItemHeight: 0, // 左边菜单item的高度
itemId: '', // 栏目右边scroll-view用于滚动的id
tabbar: classifyData,
menuItemPos: [],
arr: [],
scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
timer: null, // 定时器
}
},
onLoad() {
},
onReady() {
this.getMenuItemTop()
},
methods: {
// 点击左边的栏目切换
async swichMenu(index) {
if(this.arr.length == 0) {
await this.getMenuItemTop();
}
if (index == this.current) return;
this.scrollRightTop = this.oldScrollTop;
this.$nextTick(function(){
this.scrollRightTop = this.arr[index];
this.current = index;
this.leftMenuStatus(index);
})
},
// 获取一个目标元素的高度
getElRect(elClass, dataVal) {
new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this);
query.select('.' + elClass).fields({
size: true
}, res => {
// 如果节点尚未生成,res值为null,循环调用执行
if (!res) {
setTimeout(() => {
this.getElRect(elClass);
}, 10);
return;
}
this[dataVal] = res.height;
resolve();
}).exec();
})
},
// 观测元素相交状态
async observer() {
this.tabbar.map((val, index) => {
let observer = uni.createIntersectionObserver(this);
// 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
// 如果跟.right-box底部相交,就动态设置左边栏目的活动状态
observer.relativeTo('.right-box', {
top: 0
}).observe('#item' + index, res => {
if (res.intersectionRatio > 0) {
let id = res.id.substring(4);
this.leftMenuStatus(id);
}
})
})
},
// 设置左边菜单的滚动状态
async leftMenuStatus(index) {
this.current = index;
// 如果为0,意味着尚未初始化
if (this.menuHeight == 0 || this.menuItemHeight == 0) {
await this.getElRect('menu-scroll-view', 'menuHeight');
await this.getElRect('u-tab-item', 'menuItemHeight');
}
// 将菜单活动item垂直居中
this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
},
// 获取右边菜单每个item到顶部的距离
getMenuItemTop() {
new Promise(resolve => {
let selectorQuery = uni.createSelectorQuery();
selectorQuery.selectAll('.class-item').boundingClientRect((rects) => {
// 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
if(!rects.length) {
setTimeout(() => {
this.getMenuItemTop();
}, 10);
return ;
}
rects.forEach((rect) => {
// 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
this.arr.push(rect.top - rects[0].top);
resolve();
})
}).exec()
})
},
// 右边菜单滚动
async rightScroll(e) {
this.oldScrollTop = e.detail.scrollTop;
if(this.arr.length == 0) {
await this.getMenuItemTop();
}
if(this.timer) return ;
if(!this.menuHeight) {
await this.getElRect('menu-scroll-view', 'menuHeight');
}
setTimeout(() => { // 节流
this.timer = null;
// scrollHeight为右边菜单垂直中点位置
let scrollHeight = e.detail.scrollTop + this.menuHeight / 2;
for (let i = 0; i < this.arr.length; i++) {
let height1 = this.arr[i];
let height2 = this.arr[i + 1];
// 如果不存在height2,意味着数据循环已经到了最后一个,设置左边菜单为最后一项即可
if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
this.leftMenuStatus(i);
return ;
}
}
}, 10)
}
}
}
</script>
<style lang="scss" scoped>
.u-wrap {
height: calc(100vh);
/* #ifdef H5 */
height: calc(100vh - var(--window-top));
/* #endif */
display: flex;
flex-direction: column;
}
.u-search-box {
padding: 18rpx 30rpx;
}
.u-menu-wrap {
flex: 1;
display: flex;
overflow: hidden;
}
.u-search-inner {
background-color: rgb(234, 234, 234);
border-radius: 100rpx;
display: flex;
align-items: center;
padding: 10rpx 16rpx;
}
.u-search-text {
font-size: 26rpx;
color: $u-tips-color;
margin-left: 10rpx;
}
.u-tab-view {
width: 200rpx;
height: 100%;
}
.u-tab-item {
height: 110rpx;
background: #f6f6f6;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #444;
font-weight: 400;
line-height: 1;
}
.u-tab-item-active {
position: relative;
color: #000;
font-size: 30rpx;
font-weight: 600;
background: #fff;
}
.u-tab-item-active::before {
content: "";
position: absolute;
border-left: 4px solid $u-primary;
height: 32rpx;
left: 0;
top: 39rpx;
}
.u-tab-view {
height: 100%;
}
.right-box {
background-color: rgb(250, 250, 250);
}
.page-view {
padding: 16rpx;
}
.class-item {
margin-bottom: 30rpx;
background-color: #fff;
padding: 16rpx;
border-radius: 8rpx;
}
.class-item:last-child {
min-height: 100vh;
}
.item-title {
font-size: 26rpx;
color: $u-main-color;
font-weight: bold;
}
.item-menu-name {
font-weight: normal;
font-size: 24rpx;
color: $u-main-color;
}
.item-container {
display: flex;
flex-wrap: wrap;
}
.thumb-box {
width: 33.333333%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin-top: 20rpx;
}
.item-menu-image {
width: 120rpx;
height: 120rpx;
}
</style>
十、TODO 商品详情页
1、下拉滚动不生效问题
使用竖向滚动时,需要给 <scroll-view>
一个固定高度,通过 css 设置 height;使用横向滚动时,需要给<scroll-view>
添加white-space: nowrap;
样式。
.viewport {
background-color: #f4f4f4;
height: 600px;
}
<script setup lang="ts">
import { ref } from 'vue'
import AddressPanel from '../AddressPanel/AddressPanel.vue'
import ServicePanel from '../ServicePanel/ServicePanel.vue'
import narBarVue from '../narBar/narBar.vue';
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
// 轮播图变化时
const currentIndex = ref(0)
const onChange: UniHelper.SwiperOnChange = (ev) => {
currentIndex.value = ev.detail.current
}
const iconColor = ref('#000000');
const inconName = ref('heart');
const changeHeart = () => {
if (inconName.value == 'heart') {
inconName.value = 'heart-fill';
iconColor.value = '#ff0000';
}else if (inconName.value == 'heart-fill') {
inconName.value = 'heart';
iconColor.value = '#000';
}
}
// 点击图片时
const onTapImage = (url: string) => {
// 大图预览
// uni.previewImage({
// current: url,
// urls: goods.value!.mainPictures,
// })
}
</script>
<template>
<narBarVue/>
<scroll-view scroll-y class="viewport">
<!-- 基本信息 -->
<view class="goods">
<!-- 商品主图 -->
<view class="preview">
<swiper circular>
<swiper-item>
<image
mode="aspectFill"
src="https://yanxuan-item.nosdn.127.net/99c83709ca5f9fd5c5bb35d207ad7822.png"
/>
</swiper-item>
<swiper-item>
<image
mode="aspectFill"
src="https://yanxuan-item.nosdn.127.net/f9107d47c08f0b99c097e30055c39e1a.png"
/>
</swiper-item>
<swiper-item>
<image
mode="aspectFill"
src="https://yanxuan-item.nosdn.127.net/754c56785cc8c39f7414752f62d79872.png"
/>
</swiper-item>
<swiper-item>
<image
mode="aspectFill"
src="https://yanxuan-item.nosdn.127.net/ef16f8127610ef56a2a10466d6dae157.jpg"
/>
</swiper-item>
<swiper-item>
<image
mode="aspectFill"
src="https://yanxuan-item.nosdn.127.net/1f0c3f5d32b0e804deb9b3d56ea6c3b2.png"
/>
</swiper-item>
</swiper>
<view class="indicator">
<text class="current">1</text>
<text class="split">/</text>
<text class="total">5</text>
</view>
</view>
<!-- 商品简介 -->
<view class="meta">
<view class="price">
<text class="symbol">¥</text>
<text class="number">29.90</text>
</view>
<view class="name ellipsis">云珍·轻软旅行长绒棉方巾 </view>
<view class="desc"> 轻巧无捻小方巾,旅行便携 </view>
</view>
<!-- 操作面板 -->
<view class="action">
<view class="item arrow">
<text class="label">选择</text>
<text class="text ellipsis"> 请选择商品规格 </text>
</view>
<view class="item arrow">
<text class="label">送至</text>
<text class="text ellipsis"> 请选择收获地址 </text>
</view>
<view class="item arrow">
<text class="label">服务</text>
<text class="text ellipsis"> 无忧退 快速退款 免费包邮 </text>
</view>
</view>
</view>
<!-- 商品详情 -->
<view class="detail panel">
<view class="title">
<text>详情</text>
</view>
<view class="content">
<view class="properties">
<!-- 属性详情 -->
<view class="item">
<text class="label">属性名</text>
<text class="value">属性值</text>
</view>
<view class="item">
<text class="label">属性名</text>
<text class="value">属性值</text>
</view>
</view>
<!-- 图片详情 -->
<image
mode="widthFix"
src="https://yanxuan-item.nosdn.127.net/a8d266886d31f6eb0d7333c815769305.jpg"
></image>
<image
mode="widthFix"
src="https://yanxuan-item.nosdn.127.net/a9bee1cb53d72e6cdcda210071cbd46a.jpg"
></image>
</view>
</view>
<!-- 同类推荐 -->
<view class="similar panel">
<view class="title">
<text>同类推荐</text>
</view>
<view class="content">
<navigator
v-for="item in 4"
:key="item"
class="goods"
hover-class="none"
:url="`/pages/goods/goods?id=`"
>
<image
class="image"
mode="aspectFill"
src="https://yanxuan-item.nosdn.127.net/e0cea368f41da1587b3b7fc523f169d7.png"
></image>
<view class="name ellipsis">简约山形纹全棉提花毛巾</view>
<view class="price">
<text class="symbol">¥</text>
<text class="number">18.50</text>
</view>
</navigator>
</view>
</view>
</scroll-view>
<!-- 用户操作 -->
<view class="toolbar" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
<view class="icons">
<up-icon label="收藏" :color="iconColor" labelPos="bottom" labelSize="12px" space="5px" labelColor="#000000" size="15px" :name="inconName" @click="changeHeart"></up-icon>
<!-- <button class="icons-button" open-type="contact">
<text class="icon-handset"></text>客服
</button> -->
<view style="padding-left: 15px;">
<up-icon label="客服" labelPos="bottom" color="#000" labelSize="12px" space="5px" labelColor="#000000" size="15px" name="server-man"></up-icon>
</view>
<navigator class="icons-button" url="/pages/cart/cart" open-type="switchTab">
<up-icon label="购物车" labelPos="bottom" labelSize="12px" color="#000" space="5px" labelColor="#000000" size="15px" name="shopping-cart"></up-icon>
</navigator>
</view>
<view class="buttons">
<view class="addcart"> 加入购物车 </view>
<view class="buynow"> 立即购买 </view>
</view>
</view>
</template>
<style lang="scss>
page {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.viewport {
background-color: #f4f4f4;
height: 600px;
}
.panel {
margin-top: 20rpx;
background-color: #fff;
.title {
display: flex;
justify-content: space-between;
align-items: center;
height: 90rpx;
line-height: 1;
padding: 30rpx 60rpx 30rpx 6rpx;
position: relative;
text {
padding-left: 10rpx;
font-size: 28rpx;
color: #333;
font-weight: 600;
border-left: 4rpx solid #2979ff;
}
navigator {
font-size: 24rpx;
color: #666;
}
}
}
.arrow {
&::after {
position: absolute;
top: 50%;
right: 30rpx;
content: '\e6c2';
color: #ccc;
font-family: 'erabbit' !important;
font-size: 32rpx;
transform: translateY(-50%);
}
}
/* 商品信息 */
.goods {
background-color: #fff;
.preview {
height: 750rpx;
position: relative;
.image {
width: 750rpx;
height: 750rpx;
}
.indicator {
height: 40rpx;
padding: 0 24rpx;
line-height: 40rpx;
border-radius: 30rpx;
color: #fff;
font-family: Arial, Helvetica, sans-serif;
background-color: rgba(0, 0, 0, 0.3);
position: absolute;
bottom: 30rpx;
right: 30rpx;
.current {
font-size: 26rpx;
}
.split {
font-size: 24rpx;
margin: 0 1rpx 0 2rpx;
}
.total {
font-size: 24rpx;
}
}
}
.meta {
position: relative;
border-bottom: 1rpx solid #eaeaea;
.price {
height: 130rpx;
padding: 25rpx 30rpx 0;
color: #fff;
font-size: 34rpx;
box-sizing: border-box;
background-color: #2979ff;
}
.number {
font-size: 56rpx;
}
.brand {
width: 160rpx;
height: 80rpx;
overflow: hidden;
position: absolute;
top: 26rpx;
right: 30rpx;
}
.name {
max-height: 88rpx;
line-height: 1.4;
margin: 20rpx;
font-size: 32rpx;
color: #333;
}
.desc {
line-height: 1;
padding: 0 20rpx 30rpx;
font-size: 24rpx;
color: #cf4444;
}
}
.action {
padding-left: 20rpx;
.item {
height: 90rpx;
padding-right: 60rpx;
border-bottom: 1rpx solid #eaeaea;
font-size: 26rpx;
color: #333;
position: relative;
display: flex;
align-items: center;
&:last-child {
border-bottom: 0 none;
}
}
.label {
width: 60rpx;
color: #898b94;
margin: 0 16rpx 0 10rpx;
}
.text {
flex: 1;
-webkit-line-clamp: 1;
}
}
}
/* 商品详情 */
.detail {
padding-left: 20rpx;
.content {
margin-left: -20rpx;
.image {
width: 100%;
}
}
.properties {
padding: 0 20rpx;
margin-bottom: 30rpx;
.item {
display: flex;
line-height: 2;
padding: 10rpx;
font-size: 26rpx;
color: #333;
border-bottom: 1rpx dashed #ccc;
}
.label {
width: 200rpx;
}
.value {
flex: 1;
}
}
}
/* 同类推荐 */
.similar {
.content {
padding: 0 20rpx 200rpx;
background-color: #f4f4f4;
display: flex;
flex-wrap: wrap;
.goods {
width: 340rpx;
padding: 24rpx 20rpx 20rpx;
margin: 20rpx 7rpx;
border-radius: 10rpx;
background-color: #fff;
}
.image {
width: 300rpx;
height: 260rpx;
}
.name {
height: 80rpx;
margin: 10rpx 0;
font-size: 26rpx;
color: #262626;
}
.price {
line-height: 1;
font-size: 20rpx;
color: #cf4444;
}
.number {
font-size: 26rpx;
margin-left: 2rpx;
}
}
navigator {
&:nth-child(even) {
margin-right: 0;
}
}
}
/* 底部工具栏 */
.toolbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
background-color: #fff;
height: 100rpx;
padding: 0 20rpx var(--window-bottom);
border-top: 1rpx solid #eaeaea;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: content-box;
.buttons {
display: flex;
& > view {
width: 220rpx;
text-align: center;
line-height: 72rpx;
font-size: 26rpx;
color: #fff;
border-radius: 72rpx;
}
.addcart {
background-color: #FF4500;
}
.buynow,
.payment {
background-color: #2979ff;
margin-left: 20rpx;
}
}
.icons {
padding-right: 10rpx;
display: flex;
align-items: center;
flex: 1;
.icons-button {
flex: 1;
text-align: center;
line-height: 1.4;
padding: 0;
margin: 0;
border-radius: 0;
font-size: 20rpx;
color: #333;
background-color: #fff;
&::after {
border: none;
}
}
text {
display: block;
font-size: 34rpx;
}
}
}
</style>
2、Vue3组件之间传值问题
- 在被引用的组件(子组件)定义
defineProps
方法,方法中添加传入的值
注:此处的父子组件比较好理解,这个引用和被引用不太好理解,姑且将传值的一方称作父组件,引值的称作子组件。
<template>
<view class="u-page__item">
<!-- <text class="u-page__item__title">自定义插槽</text> -->
<up-navbar leftText="返回" :fixed="false" :bgColor="bgColor" :title="valueToPass" :safeAreaInsetTop="false">
<template #left>
<view class="u-nav-slot">
<up-icon name="arrow-left" size="19" color="#fff" @click="goBack"></up-icon>
<up-line direction="column" :hairline="false" length="16" margin="0 8px" color="#fff"></up-line>
<up-icon name="home" size="20" color="#fff" @click="goBackHome"></up-icon>
</view>
</template>
</up-navbar>
</view>
</template>
<script setup>
import { ref,onMounted } from 'vue';
// 创建响应式数据
const bgColor = ref('#2979ff');
defineProps({
valueToPass: String
});
onMounted(() => {
// console.log(`传入的值` + valueToPass)
})
const goBack = () => {
uni.navigateBack({
delta: 1 // 返回的页面数,如果 delta 大于现有页面数,则返回到首页
});
}
const goBackHome = () => {
uni.reLaunch({
url: '../pageHome/pageHome' // 替换为你的首页路径
});
}
</script>
<style>
/* #ifndef APP-NVUE */
page {
/* background-color: $u-bg-color; */
}
/* #endif */
.u-page {
padding: 0;
flex: 1;
}
.u-nav-slot {
/* flex; */
display: flex;
align-items: center;
justify-content: space-between;
border-width: 0.5px;
border-radius: 100px;
/* border-color: $u-border-color; */
padding: 3px 0px;
padding-left: 1px;
padding-top: 20px;
opacity: 0.8;
}
</style>
2)父组件传值且必须是利用属性值来进行赋值
<narBarVue :valueToPass="narBarName"/>
const narBarName = ref('商品详情')
十一、登录页面
1、创建个人中心组件
<script setup lang="ts">
import XtxGuessVue from '../XtxGuess/XtxGuess.vue';
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
const gotoPage = () => {
uni.navigateTo({
url: '/pagesOrder/list/list?type=0'
})
}
const navigateToTargetPage = (type) => {
uni.navigateTo({
url: `/pagesOrder/list/list?type=${type}`
})
}
const afterSales = () => {
uni.navigateTo({
url: '/pagesOrder/list/list?type'
})
}
// 订单选项
const orderTypes = [
{ type: 1, text: '待付款', icon: 'https://gitee.com/li__son__male/my_node_pic/raw/master/202408031710400.png'},
{ type: 2, text: '待发货', icon: 'https://gitee.com/li__son__male/my_node_pic/raw/master/202408031710315.png' },
{ type: 3, text: '待收货', icon: 'https://gitee.com/li__son__male/my_node_pic/raw/master/202408031711665.png' },
{ type: 4, text: '待评价', icon: 'https://gitee.com/li__son__male/my_node_pic/raw/master/202408031712804.png' },
]
</script>
<template>
<scroll-view class="viewport" scroll-y enable-back-to-top>
<!-- 个人资料 -->
<view class="profile" :style="{ paddingTop: safeAreaInsets!.top + 'px' }">
<!-- 情况1:已登录 -->
<view class="overview" v-if="false">
<navigator url="/pagesMember/profile/profile" hover-class="none">
<image
class="avatar"
mode="aspectFill"
src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/avatar_3.jpg"
></image>
</navigator>
<view class="meta">
<view class="nickname"> 黑马程序员 </view>
<navigator class="extra" url="/pagesMember/profile/profile" hover-class="none">
<text class="update">更新头像昵称</text>
</navigator>
</view>
</view>
<!-- 情况2:未登录 -->
<view class="overview" v-else>
<navigator url="../login/login" hover-class="none">
<image
class="avatar gray"
mode="aspectFill"
src="http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-06/db628d42-88a7-46e7-abb8-659448c33081.png"
></image>
</navigator>
<view class="meta">
<navigator url="../login/login" hover-class="none" class="nickname">
未登录
</navigator>
<view class="extra">
<text class="tips">点击登录账号</text>
</view>
</view>
</view>
<navigator class="settings" url="/pagesMember/settings/settings" hover-class="none">
设置
</navigator>
</view>
<!-- 我的订单 -->
<view class="orders">
<view class="title">
<up-text text="我的订单"></up-text>
<up-icon label="查看全部订单" labelPos="left" labelSize="12px" space="5px" labelColor="#939393" size="12px" name="arrow-right" @click="gotoPage()"></up-icon>
</view>
<view class="section">
<!-- 订单 -->
<up-icon v-for="item in orderTypes" :label="item.text" labelPos="bottom" labelSize="13px" space="5px" labelColor="#939393" size="20px" :name="item.icon" @click="navigateToTargetPage(item.type)"></up-icon>
<!-- 客服 -->
<view style="padding-right: 15px;">
<up-icon label="售后" labelPos="bottom" labelSize="13px" space="5px" labelColor="#939393" size="20px" name="https://gitee.com/li__son__male/my_node_pic/raw/master/202408031718009.png" @click="afterSales()"></up-icon>
</view>
</view>
</view>
<!-- 猜你喜欢 -->
<view class="guess">
<XtxGuessVue ref="guessRef" />
</view>
</scroll-view>
</template>
<style lang="scss">
page {
height: 100%;
overflow: hidden;
background-color: #f7f7f8;
}
.viewport {
height: 100%;
background-repeat: no-repeat;
background-image: url(https://gitee.com/li__son__male/my_node_pic/raw/master/202408031627886.jpg);
background-size: 100% auto;
}
/* 用户信息 */
.profile {
margin-top: 20rpx;
position: relative;
.overview {
display: flex;
height: 120rpx;
padding: 0 36rpx;
color: #fff;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background-color: #eee;
}
.gray {
filter: grayscale(100%);
}
.meta {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
line-height: 30rpx;
padding: 16rpx 0;
margin-left: 20rpx;
}
.nickname {
max-width: 350rpx;
margin-bottom: 16rpx;
font-size: 30rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.extra {
display: flex;
font-size: 20rpx;
}
.tips {
font-size: 22rpx;
}
.update {
padding: 3rpx 10rpx 1rpx;
color: rgba(255, 255, 255, 0.8);
border: 1rpx solid rgba(255, 255, 255, 0.8);
margin-right: 10rpx;
border-radius: 30rpx;
}
.settings {
position: absolute;
bottom: 0;
right: 40rpx;
font-size: 30rpx;
color: #fff;
}
}
/* 我的订单 */
.orders {
position: relative;
z-index: 99;
padding: 30rpx;
margin: 50rpx 20rpx 0;
background-color: #fff;
border-radius: 10rpx;
box-shadow: 0 4rpx 6rpx rgba(240, 240, 240, 0.6);
.title {
display: flex;
height: 40rpx;
line-height: 40rpx;
font-size: 28rpx;
color: #1e1e1e;
.navigator {
font-size: 24rpx;
color: #939393;
float: right;
}
}
.section {
width: 100%;
display: flex;
justify-content: space-between;
padding: 40rpx 20rpx 10rpx;
.navigator,
.contact {
text-align: center;
font-size: 24rpx;
color: #333;
&::before {
display: block;
font-size: 60rpx;
color: #ff9545;
}
}
.contact {
padding: 0;
margin: 0;
border: 0;
background-color: transparent;
line-height: inherit;
}
}
}
/* 猜你喜欢 */
.guess {
background-color: #f7f7f8;
margin-top: 20rpx;
}
</style>
2、未登录的时候点击跳转到登录页面
<script setup lang="ts">
import narBar from '../narBar/narBar.vue'
import { ref } from 'vue';
const name = ref('登录页面')
</script>
<template>
<narBar :valueToPass="name" />
<view class="viewport">
<view class="logo">
<image style="border-radius: 50%; overflow: hidden; height: 100px; width: 100px;"
src="../../static/images/10001.png"></image>
</view>
<view class="login">
<!-- 网页端表单登录 -->
<!-- <input class="input" type="text" placeholder="请输入用户名/手机号码" /> -->
<!-- <input class="input" type="text" password placeholder="请输入密码" /> -->
<!-- <button class="button phone">登录</button> -->
<!-- 小程序端授权登录 -->
<up-button class="button phone"
color="#2979ff"
shape="circle"
icon="https://gitee.com/li__son__male/my_node_pic/raw/master/202408051010276.png" text="手机号快捷登录">
</up-button>
<view class="extra">
<view class="caption">
<text>其他登录方式</text>
</view>
<!-- <view class="loginType">
<view class="wechat item">
<up-icon size="40" name="weixin-fill" color="rgb(83,194,64)" label="微信" label-pos="bottom"></up-icon>
</view>
<view class="QQ item">
<up-icon size="40" style="margin-left: 50px;" name="qq-fill" color="rgb(17,183,233)" label="QQ" label-pos="bottom"></up-icon>
</view>
</view> -->
<view class="options">
<!-- 通用模拟登录 -->
<up-button>
<text class="icon icon-phone">模拟快捷登录</text>
</up-button>
</view>
</view>
<view class="tips">登录/注册即视为你同意《服务条款》和《小兔鲜儿隐私协议》</view>
</view>
</view>
</template>
<style lang="scss">
page {
height:80%;
}
.viewport {
display: flex;
flex-direction: column;
height: 100%;
padding: 20rpx 40rpx;
}
.logo {
flex: 1;
text-align: center;
image {
width: 320rpx;
height: 320rpx;
margin-top: 15vh;
}
}
.login {
display: flex;
flex-direction: column;
height: 60vh;
padding: 40rpx 20rpx 20rpx;
.input {
width: 100%;
height: 80rpx;
font-size: 28rpx;
border-radius: 72rpx;
border: 1px solid #ddd;
padding-left: 30rpx;
margin-bottom: 20rpx;
}
// .button {
// display: flex;
// align-items: center;
// justify-content: center;
// width: 100%;
// height: 80rpx;
// font-size: 28rpx;
// border-radius: 72rpx;
// color: #fff;
// .icon {
// font-size: 40rpx;
// margin-right: 6rpx;
// }
// }
.phone {
background-color: #2979ff;
}
// .wechat {
// background-color: #1E90FF;
// }
.loginType {
display: flex;
margin-top: 20px;
margin-left: 10px;
}
.extra {
flex: 1;
padding: 70rpx 70rpx 0;
.caption {
width: 440rpx;
line-height: 1;
border-top: 1rpx solid #ddd;
font-size: 26rpx;
color: #999;
position: relative;
text {
transform: translate(-40%);
background-color: #fff;
position: absolute;
top: -12rpx;
left: 50%;
}
}
.options {
display: flex;
justify-content: center;
align-items: center;
margin-top: 70rpx;
button {
padding: 0;
background-color: transparent;
}
}
.icon {
font-size: 24rpx;
color: #444;
display: flex;
flex-direction: column;
align-items: center;
&::before {
display: flex;
align-items: center;
justify-content: center;
width: 80rpx;
height: 80rpx;
margin-bottom: 6rpx;
font-size: 40rpx;
border: 1rpx solid #444;
border-radius: 50%;
}
}
.icon-weixin::before {
border-color: #1E90FF;
color: #1E90FF;
}
}
}
.tips {
position: absolute;
bottom: 80rpx;
left: 20rpx;
right: 20rpx;
font-size: 22rpx;
color: #999;
text-align: center;
}
</style>
3、弹窗文字滚动不显示问题
注:滚动事件的高度和弹窗的高度要设置
<script setup lang="ts">
import narBar from '../narBar/narBar.vue'
import { ref } from 'vue';
import protocal from './protocal.vue'
import { onLoad, onShow } from "@dcloudio/uni-app";
const name = ref('登录页面')
// 创建响应式数据
const show = ref(false);
// 定义方法
function open() {
// 打开逻辑,比如设置 show 为 true
show.value = true;
// console.log('open');
}
function close() {
// 关闭逻辑,设置 show 为 false
show.value = false;
// console.log('close');
}
const confirm = () => {
setTimeout(() => {
// 3秒后自动关闭
show.value = false;
}, 3000);
};
// uni-app 的生命周期钩子
onLoad((opt) => {
// 页面加载时执行的代码
console.log('页面加载了', opt);
});
onShow(() => {
// 页面显示时执行的代码
console.log('页面显示了');
});
// 下拉刷新状态
const isTriggered = ref(false)
const onScrolltolower = () => {
guessRef.value?.getMore()
}
// 自定义下拉刷新被触发
const onRefresherrefresh = async () => {
// 开启动画
isTriggered.value = true
// 重置猜你喜欢组件数据
// guessRef.value?.resetData() // 加载数据
await Promise.all([getHomeBannerData(), getHomeCategoryData(), getHomeHotData()]) // 关闭动画
isTriggered.value = false
}
</script>
<template>
<narBar :valueToPass="name" />
<view class="viewport">
<view class="logo">
<image style="border-radius: 50%; overflow: hidden; height: 100px; width: 100px;"
src="../../static/images/10001.png"></image>
</view>
<view class="login">
<!-- 网页端表单登录 -->
<!-- <input class="input" type="text" placeholder="请输入用户名/手机号码" /> -->
<!-- <input class="input" type="text" password placeholder="请输入密码" /> -->
<!-- <button class="button phone">登录</button> -->
<!-- 小程序端授权登录 -->
<up-button class="button phone" color="#2979ff" shape="circle"
icon="https://gitee.com/li__son__male/my_node_pic/raw/master/202408051010276.png" text="手机号快捷登录">
</up-button>
<view class="extra">
<view class="caption">
<text>其他登录方式</text>
</view>
<view class="options">
<!-- 通用模拟登录 -->
<up-button>
<text class="icon icon-phone">模拟快捷登录</text>
</up-button>
</view>
</view>
<view class="hint">
登录/注册代表同意
<text class="link" @click="open()">《服务条款》、《小兔鲜儿隐私协议》</text>
并授权使用您的账号信息(如昵称、头像、收获地址)以便您统一管理
</view>
<up-modal :show="show" height="700rpx" @confirm="confirm" @cancel="close" showConfirmButton="true" showCancelButton="true"
ref="uModal" :asyncClose="true" title="服务条款" confirmText="同意" cancelText="取消">
<template #confirmButton>
<up-button text="同意" shape="circle" color="#2979ff" @click="show = false"></up-button>
</template>
<scroll-view refresher-enabled :refresher-triggered="isTriggered"
class="scroll-view"scroll-y="true" style="height: 680rpx; width: 500px;" scroll-with-animation="true">
<protocal style="page:50%"/>
</scroll-view>
</up-modal>
<!-- <up-button @click="open()">打开</up-button> -->
</view>
</view>
</template>
<style lang="scss" scoped>
.viewport {
display: flex;
flex-direction: column;
height: 100%;
padding: 20rpx 40rpx;
}
.logo {
flex: 1;
text-align: center;
image {
width: 320rpx;
height: 320rpx;
margin-top: 15vh;
}
}
.login {
display: flex;
flex-direction: column;
height: 60vh;
padding: 40rpx 20rpx 20rpx;
.input {
width: 100%;
height: 80rpx;
font-size: 28rpx;
border-radius: 72rpx;
border: 1px solid #ddd;
padding-left: 30rpx;
margin-bottom: 20rpx;
}
.phone {
background-color: #2979ff;
}
.loginType {
display: flex;
margin-top: 20px;
margin-left: 10px;
}
.extra {
flex: 1;
padding: 70rpx 70rpx 0;
.caption {
width: 440rpx;
line-height: 1;
border-top: 1rpx solid #ddd;
font-size: 26rpx;
color: #999;
position: relative;
text {
transform: translate(-40%);
background-color: #fff;
position: absolute;
top: -12rpx;
left: 50%;
}
}
.options {
display: flex;
justify-content: center;
align-items: center;
margin-top: 70rpx;
button {
padding: 0;
background-color: transparent;
}
}
.icon {
font-size: 24rpx;
color: #444;
display: flex;
flex-direction: column;
align-items: center;
&::before {
display: flex;
align-items: center;
justify-content: center;
width: 80rpx;
height: 80rpx;
margin-bottom: 6rpx;
font-size: 40rpx;
border: 1rpx solid #444;
border-radius: 50%;
}
}
.icon-weixin::before {
border-color: #1E90FF;
color: #1E90FF;
}
}
}
.hint {
padding: 20rpx 40rpx;
font-size: 20rpx;
color: $u-tips-color;
.link {
color: $u-warning;
}
}
.tips {
position: absolute;
bottom: 80rpx;
left: 20rpx;
right: 20rpx;
font-size: 22rpx;
color: #999;
text-align: center;
}
</style>
隐私协议
<template>
<article class="article">
<!-- <h1 class="h1 tac">吉吉小卖部用户服务协议</h1>
<p class="p tac">版本生效日期:2023年11月6日</p> -->
<h2 class="h2">一、服务条款的确认及接受</h2>
<p class="p">
(一) 吉吉小卖部 网站各项电子服务的所有权和运作权归属于“传智教育”所有,<strong class="strong"
>仅用于IT培训教学使用,为保障您的个人信息安全请勿向平台录入任何个人敏感信息(如手机号、身份证号等)!</strong
>本网站提供的服务将完全按照其发布的服务条款和操作规则严格执行。您确认所有服务条款并完成注册程序时,本协议在您与本网站间成立并发生法律效力,同时您成为本网站正式用户。
</p>
<p class="p">
(二)
吉吉小卖部隐私政策、平台规则、单独协议均为本协议的补充协议,与本协议不可分割且具有同等法律效力。如您使用吉吉小卖部平台服务,视为您同意上述补充协议。
</p>
<p class="p">
(三)
您在使用本网站某一特定服务时,该服务可能会另有单独的协议、相关业务规则等(以下统称为“单独协议”),您在使用该项服务前请阅读并同意相关的单独协议;您使用前述特定服务,即视为您已阅读并同意接受相关单独协议。
</p>
<p class="p">
(四)
根据国家法律法规变化及本网站运营需要,传智教育有权对本协议条款及相关规则不时地进行修改,修改后的内容一旦依法以任何形式公布在本网站上即生效,并取代此前相关内容,您应不时关注本网站公告、提示信息及协议、规则等相关内容的变动。您知悉并确认,如您不同意更新后的内容,应立即停止使用本网站;如您继续使用本网站,即视为知悉变动内容并同意接受。
</p>
<p class="p">
(五)
除非另有明确的书面说明,吉吉小卖部不对本网站的运营及其包含在本网站上的信息、内容、材料、产品(包括软件)或服务作任何形式的、明示或默示的声明或担保(根据中华人民共和国法律另有规定的以外)。
</p>
<p class="p">
(六)
本协议内容中以加粗方式显著标识的条款,请您着重阅读。您点击“同意”按钮即视为您完全接受本协议,在点击之前请您再次确认已知悉并完全理解本协议的全部内容。
</p>
<h2 class="h2">二、账户注册、管理及使用</h2>
<h4 class="h4">(一) 用户资格</h4>
<p class="p">
您确认,在您开始注册程序使用吉吉小卖部平台服务前,您应当具备中华人民共和国法律规定的与您行为相适应的民事行为能力。若您不具备前述与您行为相适应的民事行为能力,需要在监护人的监护参与下才能注册并使用本网站,您及您的监护人应依照法律规定承担因此而导致的一切后果。
此外,您还需确保您不是任何国家、地区或国际组织实施的贸易限制、经济制裁或其他法律法规限制的对象,也未直接或间接为前述对象提供资金、商品或服务,否则您应当停止使用吉吉小卖部平台服务,同时,您理解违反前述要求可能会造成您无法正常注册及使用吉吉小卖部平台服务。
</p>
<h4 class="h4">(二) 账户注册</h4>
<p class="p">
1、
您按照注册页面提示填写信息、阅读并同意本协议且完成全部注册程序后,您可获得吉吉小卖部平台账户并成为吉吉小卖部平台用户。您设置的吉吉小卖部账号名/昵称/头像等不得违反国家法律法规及吉吉小卖部平台相关规则关于账号注册的管理规定,未经他人许可不得用他人名义(包括但不限于冒用他人姓名、名称、字号、头像等或采取其他足以让人引起混淆的方式)开设账号,不得恶意注册吉吉小卖部账号。否则吉吉小卖部平台经营者有权拒绝设置或收回您的账号名/昵称。账号名的回收不影响您以邮箱、手机号码登录吉吉小卖部平台并使用吉吉小卖部平台服务。
</p>
<p class="p">
2、
吉吉小卖部平台原则上只允许每位用户使用一个吉吉小卖部平台账户。如有证据证明或吉吉小卖部根据吉吉小卖部平台规则判断您存在不当注册或不当使用多个吉吉小卖部平台账户的情形,吉吉小卖部平台可采取冻结或关闭账户、取消订单、拒绝提供服务等措施,如给吉吉小卖部平台及相关方造成损失的,您还应承担赔偿责任。
</p>
<p class="p">
3、
在使用吉吉小卖部平台服务时,您应当按吉吉小卖部平台页面的提示准确完整地提供您的信息(包括您的姓名及电子邮件地址、联系电话、联系地址等),以便吉吉小卖部或其他服务方与您联系。您了解并同意,您有义务保持您提供信息的真实性及有效性。
</p>
<h4 class="h4">(三) 账户登录</h4>
<p class="p">
您有权使用您设置或确认的吉吉小卖部用户名、手机号码(以下简称“账户名称”)及您设置的密码(账户名称及密码合称“账户”)或通过授权使用您合法拥有的第三方软件或平台用户账号(如微信账号、QQ账号等)登录吉吉小卖部平台,但第三方软件或平台对此有限制或禁止的除外。当您以前述已有账号登录使用的,同样适用本协议中的相关条款。
</p>
<p class="p">
您理解并同意,除您登录吉吉小卖部平台以外,您还可以使用吉吉小卖部账号登录吉吉小卖部及其关联方或其他合作方提供的其他软件、服务。您以吉吉小卖部账号登录前述服务的,同样应受其他软件、服务实际提供方的用户协议及其他协议条款约束。
</p>
<h4 class="h4">(四) 实名认证</h4>
<p class="p">
作为吉吉小卖部平台经营者,为使您更好地使用吉吉小卖部平台的各项服务,保障您的账户安全,吉吉小卖部可要求您按照相关法律法规规定完成实名认证。
</p>
<h4 class="h4">(五) 更新维护</h4>
<p class="p">
您应当及时更新您提供的信息,在法律有明确规定要求吉吉小卖部作为平台服务提供者必须对部分用户的信息进行核实的情况下,吉吉小卖部将依法不时地对您的信息进行检查核实,您应当配合提供最新、真实、完整、有效的信息。
</p>
<p class="p">
如吉吉小卖部按您最后一次提供的信息与您联系未果、您未按吉吉小卖部的要求及时提供信息、您提供的信息存在明显不实或行政司法机关核实您提供的信息无效的,您将承担因此对您自身、他人及吉吉小卖部造成的全部损失与不利后果。吉吉小卖部可向您发出询问或要求整改的通知,并要求您进行重新认证,直至中止、终止对您提供部分或全部吉吉小卖部平台服务,吉吉小卖部对此不承担责任。
</p>
<h4 class="h4">(六) 账户管理</h4>
<p class="p">
1、
由于您的吉吉小卖部平台账户关联您的个人信息及吉吉小卖部平台商业信息,您的吉吉小卖部平台账户仅限您本人使用,您应对您账户下的所有行为结果负责,不得以任何方式转让,否则吉吉小卖部平台有权追究您的违约责任,且由此产生的责任及后果均由您自行承担。您直接或间接授权第三方使用您吉吉小卖部平台账户或获取您账户项下信息的行为后果亦由您自行承担。如吉吉小卖部根据吉吉小卖部平台规则中约定的违约认定程序及标准判断您吉吉小卖部平台账户的使用可能危及您的账户安全及/或吉吉小卖部平台信息安全的,吉吉小卖部平台可拒绝提供相应服务或终止本协议。
</p>
<p class="p">
2、
您的账户为您自行设置并由您保管,吉吉小卖部任何时候均不会主动要求您提供您的账户密码。因此,建议您务必保管好您的账户,并确保您在每个上网时段结束时退出登录并以正确步骤离开吉吉小卖部平台。
</p>
<p class="p">
3、
账户因您主动泄露或因您遭受他人攻击、诈骗等行为导致的损失及后果,吉吉小卖部并不承担责任,您应通过司法、行政等救济途径向侵权行为人追偿。如发现任何未经授权使用您账户登录吉吉小卖部平台或其他可能导致您账户遭窃、遗失的情况,建议您立即通知吉吉小卖部,您理解吉吉小卖部对您的任何请求采取行动均需要合理时间,且吉吉小卖部应您请求而采取的行动可能无法避免或阻止侵害后果的形成或扩大,除吉吉小卖部存在法定过错外,吉吉小卖部不承担责任。
</p>
<p class="p">
4、
您理解并同意,为充分使用账号资源,如您在注册后未及时进行初次登录使用或连续12个月未登录任一吉吉小卖部平台,且不存在未到期的有效业务的,吉吉小卖部有权收回您的账号,您可能无法通过您此前持有的账号登录吉吉小卖部平台,您该账号下任何个性化设置(如头像/昵称)即将无法恢复。在收回您的账号之前,吉吉小卖部将以适当方式做出提示,如您在收到相关提示后一定期限内仍未登录、使用账号,吉吉小卖部将收回账号。
</p>
<h4 class="h4">(七) 账号注销</h4>
<p class="p">
如您需要终止使用吉吉小卖部账号服务,符合以下条件的,您可以申请注销您的账号:(1)您仅能申请注销您本人的账号,并依照《吉吉小卖部账号注销须知》进行注销;(2)您仍应对您在注销账号前使用吉吉小卖部账号服务期间的行为承担责任。特别提示您,您申请同意注销账户的,视为您放弃账户信息以及该账户在平台的资产、虚拟权益等,且吉吉小卖部无法为您恢复前述服务。这可能对您主张售后服务带来不便。
</p>
<p class="p"></p>
<h3 class="h3">四、吉吉小卖部平台服务</h3>
<p class="p">
您有权在吉吉小卖部平台上享受商品及/或服务的浏览、收藏、购买与评价、配送和交付、平台活动、交易争议处理、信息交流及分享等服务,详情您可登录吉吉小卖部平台查看。
</p>
<h4 class="h4">(一) 商品及/或服务的浏览、收藏</h4>
<p class="p">
在您浏览我们网站或客户端的过程中,吉吉小卖部为您提供了信息分类、关键词检索、筛选、收藏及关注等功能,以更好地匹配您的需求。您可以对您感兴趣的商品及/或服务进行收藏、添加至购物车,关注您所感兴趣的店铺/品牌等。
</p>
<h4 class="h4">(二) 商品及/或服务的购买</h4>
<p class="p">
1、
当您在吉吉小卖部平台购买商品及/或服务时,请您仔细确认所购商品的名称、价格、数量、型号、规格、尺寸、联系地址、电话、收货人等信息。收货人与您本人不一致的,收货人的行为和意思表示视为您的行为和意思表示,您应对收货人的行为及意思表示的法律后果承担连带责任。
</p>
<p class="p">
2、
您的购买行为应当符合法律规定,为生活消费需要购买、使用商品或者接受服务,不得存在对商品及/或服务实施恶意购买、恶意维权等扰乱吉吉小卖部平台正常交易秩序的行为。基于维护吉吉小卖部平台交易秩序及交易安全的需要,吉吉小卖部发现上述情形时可主动执行包括但不限于暂停或停止服务、取消订单等吉吉小卖部认为有必要的管控措施等操作。
</p>
<p class="p">
3、 您理解并同意:本网站上销售商展示的商品和价格等信息<strong class="strong"
>仅用于教学展示使用</strong
>,如果您下单购买商品或服务并完成支付,不会产生实际购买商品或服务的合同关系,吉吉小卖部平台无需发货,该购买行为仅视为教学测试使用。
</p>
<h4 class="h4">(三) 配送和交付</h4>
<p class="p">
您在本网站购买的商品<strong class="strong">仅为教学测试使用</strong
>,并未实际产生交易金额,因此吉吉小卖部平台不会根据您填写的物流信息进行实际配送服务。
</p>
<p class="p"></p>
<h3 class="h3">五、用户个人信息保护及授权</h3>
<h4 class="h4">
(一) 您知悉并同意,为方便您使用本网站相关服务,本网站将存储您在使用时的必要信息
</h4>
<p class="p">
包括但不限于您的真实姓名、性别、生日、配送地址、联系方式、通讯录、相册、日历、定位信息等。除法律法规规定的情形外,未经您的许可吉吉小卖部不会向第三方公开、透露您的个人信息。吉吉小卖部对相关信息采取专业加密存储与传输方式,利用合理措施保障用户个人信息的安全。如吉吉小卖部平台网站或客户端未设置独立隐私政策但使用了吉吉小卖部平台账号登陆相应网站或客户端的,为保护您的隐私权,我们将参照适用吉吉小卖部隐私政策的要求对您的个人信息进行收集、存储、使用、披露和保护。吉吉小卖部希望通过隐私政策向您清楚地介绍吉吉小卖部对您个人信息的处理方式,因此吉吉小卖部建议您完整地阅读隐私政策,以帮助您更好地保护您的隐私权。
</p>
<h4 class="h4">(二) 您充分理解并同意:</h4>
<p class="p">
1、
您理解并同意,通过邮件、短信、电话、消息等平台渠道形式接收相关订单信息、促销活动、商品推荐等内容;您有权通过您注册时填写的手机号码或者电子邮箱获取您感兴趣的商品广告信息、促销优惠等商业性信息、推广或信息(包括商业或非商业信息);您如果不愿意接收此类信息,您有权通过吉吉小卖部提供的相应的退订功能进行退订。
</p>
<p class="p">
2、
为配合行政监管机关、司法机关执行工作,在法律规定范围内吉吉小卖部有权向上述行政、司法机关提供您在使用本网站时所储存的相关信息,包括但不限于您的注册信息等,或使用相关信息进行证据保全,包括但不限于公证、见证等;
</p>
<p class="p">
3、
吉吉小卖部依法保障您在安装或使用过程中的知情权和选择权,在您使用本网站服务过程中,涉及您设备自带功能的服务会提前征得您同意,您一经确认,吉吉小卖部有权开启包括但不限于收集地理位置、读取通讯录、使用摄像头、启用录音等提供服务必要的辅助功能。
</p>
<p class="p">
4、
吉吉小卖部有权根据实际情况,在法律规定范围内自行决定单个用户在本网站及服务中数据的最长储存期限以及用户日志的储存期限,并在服务器上为其分配数据最大存储空间等。
</p>
<p class="p"></p>
<h3 class="h3">六、用户行为规范</h3>
<h4 class="h4">(一) 您同意严格遵守法律法规规章规定,依法遵守以下义务:</h4>
<p class="p">
1、
不得制作、传输或发表以下违法信息资料:反对宪法所确定的基本原则,煽动抗拒、破坏宪法和法律法规实施的;危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的,煽动推翻社会主义制度的;损害国家荣誉和利益的;歪曲、丑化、亵渎、否定英雄烈士事迹和精神,侵害英雄烈士的姓名、肖像、名誉、荣誉的;宣扬或煽动实施恐怖主义、极端主义及其活动的;煽动民族仇恨、民族歧视、破坏民族团结的言论;破坏国家宗教政策,宣扬邪教和封建迷信的;散布谣言,扰乱经济秩序和社会秩序的;散布淫秽、色情、暴力或者教唆犯罪的;侮辱或者诽谤他人,侵害他人名誉、隐私和其他合法权益的;法律、行政法规禁止的其他内容。
</p>
<p class="p">
2、
防范和抵制制作、复制、发布含有下列内容的不良信息资料:标题严重夸张,发表内容与标题严重不符的;不当评述自然灾害、重大事故等灾难的;煽动人群歧视、地域歧视等的;宣扬低俗、庸俗、媚俗内容的;违反社会公德行为的;侵犯未成年人合法权益的;其他对网络生态造成不良影响的内容。
</p>
<h4 class="h4">(二) 本协议依据国家相关法律法规规章制定,您亦同意严格遵守以下义务:</h4>
<p class="p">1、 从中国大陆向境外传输资料信息时必须符合中国有关法规;</p>
<p class="p">2、 不得利用本网站从事洗钱、窃取商业秘密、窃取个人信息等违法犯罪活动;</p>
<p class="p">
3、
不得企图干扰、破坏吉吉小卖部系统或网站的正常运转,故意传播恶意程序或病毒以及其他破坏干扰正常网络信息服务的行为;
</p>
<p class="p">
4、
不得传输或发表任何违法犯罪的、骚扰性的、中伤他人的、辱骂性的、恐吓性的、伤害性的、庸俗的、不文明的等信息资料;
</p>
<p class="p">5、 不得教唆他人从事违法违规或本协议、平台规则所禁止的行为;</p>
<p class="p">
6、
不得利用在本网站注册的账户进行不当利益获取、恶意攻击、违法经营活动、传播不良内容等违反法律法规的行为;
</p>
<p class="p">7、 不得发布任何侵犯他人个人信息、著作权、商标权等知识产权或合法权利的内容;</p>
<p class="p">
8、
不得冒充他人或利用他人的名义使用吉吉小卖部软件服务或传播任何信息,恶意使用注册账号导致其他用户误认的;
</p>
<p class="p">
9、
不得存在可能破坏、篡改、删除、影响吉吉小卖部平台任何系统正常运行或未经授权秘密获取吉吉小卖部平台及其他用户的数据、个人资料的病毒、木马、爬虫等恶意软件、程序代码的;
</p>
<p class="p">10、不得恶意注册吉吉小卖部账号,包括但不限于频繁、批量注册账号、注销账号;</p>
<p class="p">11、不得发布其他对网络生态造成不良影响的内容,及法律、行政法规禁止的其他内容。</p>
<h4 class="h4">(三)您须对自己在网上的言论和行为承担法律责任</h4>
<p class="p">
您若在本网站上散布和传播反动、色情或其它违反国家法律的信息,本网站的系统记录有可能作为您违反法律的证据。吉吉小卖部可对您发布的信息依法或依本协议进行删除或屏蔽。
</p>
<h4 class="h4">(四) 除非法律允许或吉吉小卖部书面许可,您使用本网站过程中不得从事下列行为:</h4>
<p class="p">1、 删除本网站及其副本上关于著作权的信息;</p>
<p class="p">
2、 对本网站进行反向工程、反向汇编、反向编译,或者以其他方式尝试发现本网站的源代码;
</p>
<p class="p">
3、
对吉吉小卖部拥有知识产权的内容进行使用、出租、出借、复制、修改、链接、转载、汇编、发表、出版、建立镜像站点等;
</p>
<p class="p">
4、
对本网站或者本网站运行过程中释放到任何终端内存中的数据、网站运行过程中客户端与服务器端的交互数据,以及本网站运行所必需的系统数据,进行复制、修改、增加、删除、挂接运行或创作任何衍生作品,形式包括但不限于使用插件、外挂或非经吉吉小卖部授权的第三方工具/服务接入本网站和相关系统;
</p>
<p class="p">
5、
通过修改或伪造网站运行中的指令、数据,增加、删减、变动网站的功能或运行效果,或者将用于上述用途的软件、方法进行运营或向公众传播,无论这些行为是否为商业目的;
</p>
<p class="p">
6、
通过非吉吉小卖部开发、授权的第三方软件、插件、外挂、系统,登录或使用本网站及服务,或制作、发布、传播上述工具;
</p>
<p class="p">7、 自行或者授权他人、第三方软件对本网站及其组件、模块、数据进行干扰。</p>
<h3 class="h3">七、不可抗力或其他免责事由</h3>
<h4 class="h4">
(一)
您理解并同意,在使用本服务的过程中,可能会遇到不可抗力等风险因素,使本服务协议下的服务发生中断或终止。
</h4>
<p class="p">
不可抗力是指不能预见、不能克服并不能避免且对一方或双方造成重大影响的客观事件,包括但不限于信息网络设备维护、信息网络连接故障、电脑、通讯或其他系统的故障、电力故障、罢工、劳动争议、暴乱、起义、骚乱、生产力或生产资料不足、火灾、洪水、风暴、爆炸、战争、政府行为、法律法规变动、司法行政机关的命令、其他不可抗力或第三方的不作为而造成的不能服务或延迟服务等行为。出现上述情况时,吉吉小卖部将努力在第一时间与相关部门配合,及时进行修复,但是由此给您造成的损失,吉吉小卖部在法律允许的范围内免责。
</p>
<h4 class="h4">
(二) 您理解并同意,在法律允许的范围内,吉吉小卖部对以下事由所导致的服务中断或终止不承担责任:
</h4>
<p class="p">1、 受到计算机病毒、木马或其他恶意程序、黑客攻击的破坏;</p>
<p class="p">2、 用户或吉吉小卖部的电脑软件、系统、硬件和通信线路出现故障;</p>
<p class="p">3、 用户操作不当;</p>
<p class="p">4、 用户通过非吉吉小卖部授权的方式使用本服务;</p>
<p class="p">5、 其他吉吉小卖部无法控制或合理预见的情形。</p>
</article>
</template>
<style lang="scss" scoped>
.article {
padding: 1em;
}
.h1,
.h2,
.h3,
.h4,
.h5,
.h6 {
font-weight: bold;
margin: 0.5em 0;
}
.h1 {
font-size: 1.3em;
}
.h2 {
font-size: 1.2em;
}
.h3 {
font-size: 1.1em;
}
.p {
text-indent: 2em;
line-height: 1.5;
}
.strong {
font-weight: bold;
display: inline;
text-decoration: underline;
}
.tac {
text-align: center;
text-indent: 0;
}
</style>
4、uniapp配置分包
1)在项目根目录下设置分包根目录
2)在根目录中创建首页地址
3)在pages.json中配置分包
// 分包加载规则
"subPackages": [{
// 子包的根目录
"root": "pagesMember",
// 页面路径和窗口表现
"pages": [{
"path": "settings/settings",
"style": {
"navigationBarTitleText": "设置"
}
}]
}],
// 分包预下载规则
"preloadRule": {
"pages/my/my": {
"network": "all",
"packages": ["pagesMember"]
}
}
5、返回首页的时候修改跳转路径,底部导航栏自动切换到首页
const goBackHome = () => {
useStore.setTabbarActive(0)
uni.reLaunch({
url: '../pageHome/pageHome' // 替换为你的首页路径
});
}
:::info
v-if标签中根据变量来判断值直接写变量的名称即可
:::
<up-icon v-if="valueToPass != '设置中心'" name="home"/>
6、添加设置中心
创建settings组件
<script setup lang="ts">
import navbar from '../../pages/narBar/narBar.vue'
import { ref } from 'vue';
const name = ref('设置中心');
const pageTo = () => {
uni.navigateTo({
url: '/pagesMember/address/address'
});
}
const about = () => {
uni.navigateTo({
url: ''
});
}
</script>
<template>
<navbar :valueToPass="name"/>
<view class="viewport">
<!-- 列表1 -->
<view v-if="true">
<up-button class="item arrow" @click="pageTo" hover-class="none">我的收货地址 <up-icon name="arrow-right"></up-icon></up-button>
</view>
<!-- 列表2 -->
<view class="action">
<!-- <view class="list1"><button hover-class="none" class="item arrow" open-type="openSetting"><up-icon name="arrow-right"/></button></view>
<button hover-class="none" class="item arrow" open-type="feedback">问题反馈</button>
<button hover-class="none" class="item arrow" open-type="contact">联系我们</button> -->
<up-button class="item arrow" open-type="openSetting">权限管理 <up-icon name="arrow-right"></up-icon></up-button>
<up-button class="item arrow" open-type="feedback">问题反馈 <up-icon name="arrow-right"></up-icon></up-button>
<up-button class="item arrow" open-type="contact">联系我们 <up-icon name="arrow-right"></up-icon></up-button>
</view>
<!-- 列表3 -->
<view>
<up-button class="item arrow" @click="about" hover-class="none">关于小兔鲜儿 <up-icon name="arrow-right"></up-icon></up-button>
</view>
<!-- 操作按钮 -->
<view class="action">
<view class="button">退出登录</view>
</view>
</view>
</template>
<style lang="scss">
page {
background-color: #f4f4f4;
}
.viewport {
padding: 20rpx;
}
.list1 {
display: flex;
}
/* 列表 */
.list {
padding: 0 20rpx;
background-color: #fff;
margin-bottom: 20rpx;
border-radius: 10rpx;
// width: 400px;
.item {
line-height: 90rpx;
padding-left: 10rpx;
font-size: 30rpx;
color: #333;
border-top: 1rpx solid #ddd;
position: relative;
text-align: left;
border-radius: 0;
background-color: #fff;
// &::after {
// width: auto;
// height: auto;
// left: auto;
// border: none;
// }
&:first-child {
border: none;
}
&::after {
right: 5rpx;
}
}
.arrow::after {
content: '\e6c2';
position: absolute;
top: 50%;
color: #ccc;
font-family: 'erabbit' !important;
font-size: 32rpx;
transform: translateY(-50%);
}
}
/* 操作按钮 */
.action {
text-align: center;
line-height: 90rpx;
margin-top: 40rpx;
font-size: 32rpx;
color: #333;
.button {
background-color: #fff;
margin-bottom: 20rpx;
border-radius: 10rpx;
}
}
</style>
十二、添加收货地址
1、创建地址管理页面
<script setup lang="ts">
import narBar from '../../pages/narBar/narBar.vue'
import { ref } from 'vue';
const name = ref('收货地址');
</script>
<template>
<narBarVue :valueToPass="name"></narBarVue>
<view class="viewport">
<!-- 地址列表 -->
<scroll-view class="scroll-view" scroll-y>
<view v-if="true" class="address">
<view class="address-list">
<!-- 收货地址项 -->
<view class="item">
<view class="item-content">
<view class="user">
黑马小王子
<text class="contact">13111111111</text>
<text v-if="true" class="badge">默认</text>
</view>
<view class="locate">广东省 广州市 天河区 黑马程序员</view>
<navigator class="edit" hover-class="none"
:url="`/pagesMember/address-form/address-form?id=1`">
修改
</navigator>
</view>
</view>
<!-- 收货地址项 -->
<view class="item">
<view class="item-content">
<view class="user">
黑马小公主
<text class="contact">13222222222</text>
<text v-if="false" class="badge">默认</text>
</view>
<view class="locate">北京市 北京市 顺义区 黑马程序员</view>
<navigator class="edit" hover-class="none"
:url="`/pagesMember/address-form/address-form?id=2`">
修改
</navigator>
</view>
</view>
</view>
</view>
<view v-else class="blank">暂无收货地址</view>
</scroll-view>
<!-- 添加按钮 -->
<view class="add-btn">
<navigator hover-class="none" url="/pagesMember/address-form/address-form">
新建地址
</navigator>
</view>
</view>
</template>
<style lang="scss">
page {
height: 100%;
overflow: hidden;
}
/* 删除按钮 */
.delete-button {
display: flex;
justify-content: center;
align-items: center;
width: 50px;
height: 100%;
font-size: 28rpx;
color: #fff;
border-radius: 0;
padding: 0;
background-color: #cf4444;
}
.viewport {
display: flex;
flex-direction: column;
height: 100%;
background-color: #f4f4f4;
.scroll-view {
padding-top: 20rpx;
}
}
.address {
padding: 0 20rpx;
margin: 0 20rpx;
border-radius: 10rpx;
background-color: #fff;
.item-content {
line-height: 1;
padding: 40rpx 10rpx 38rpx;
border-bottom: 1rpx solid #ddd;
position: relative;
.edit {
position: absolute;
top: 36rpx;
right: 30rpx;
padding: 2rpx 0 2rpx 20rpx;
border-left: 1rpx solid #666;
font-size: 26rpx;
color: #666;
line-height: 1;
}
}
.item:last-child .item-content {
border: none;
}
.user {
font-size: 28rpx;
margin-bottom: 20rpx;
color: #333;
.contact {
color: #666;
}
.badge {
display: inline-block;
padding: 4rpx 10rpx 2rpx 14rpx;
margin: 2rpx 0 0 10rpx;
font-size: 26rpx;
color: #2979ff;
border-radius: 6rpx;
border: 1rpx solid #2979ff;
}
}
.locate {
line-height: 1.6;
font-size: 26rpx;
color: #333;
}
}
.blank {
margin-top: 300rpx;
text-align: center;
font-size: 32rpx;
color: #888;
}
.add-btn {
height: 80rpx;
text-align: center;
line-height: 80rpx;
margin: 30rpx 20rpx;
color: #fff;
border-radius: 80rpx;
font-size: 30rpx;
background-color: #2979ff;
}
</style>
添加右侧图标问题
<template #right>
<up-icon
name="arrow-right"
></up-icon>
</template>
2、创建地址新增页面
<script setup lang="ts">
// 表单数据
const form = ref({
receiver: '', // 收货人
contact: '', // 联系方式
fullLocation: '', // 省市区(前端展示)
provinceCode: '', // 省份编码(后端参数)
cityCode: '', // 城市编码(后端参数)
countyCode: '', // 区/县编码(后端参数)
address: '', // 详细地址
isDefault: 0, // 默认地址,1为是,0为否
});
const rules = {
'form.receiver': [
{
required: true,
message: "请填写收货人姓名",
trigger: "blur",
},
],
'form.contact': [
{
required: true,
message: "请填写收货人手机号码",
trigger: "blur",
},
],
'form.fullLocation': [
{
required: true,
message: "请选择省",
trigger: "change",
},
],
'form.provinceCode': [
{
required: true,
message: "请选择省市",
trigger: "change",
},
],
'form.cityCode': [
{
required: true,
message: "请选择区(县)",
trigger: "change",
},
],
'form.address': [
{
required: true,
message: "请输入街道、楼牌号等详细信息",
trigger: "blur",
},
],
'form.isDefault': [
{
required: true,
message: "请选择房号",
trigger: "change",
},
],
};
</script>
<template>
<view class="content">
<form>
<!-- 表单内容 -->
<view class="form-item">
<text class="label">收货人</text>
<input class="input" placeholder="请填写收货人姓名" value="" />
</view>
<view class="form-item">
<text class="label">手机号码</text>
<input class="input" placeholder="请填写收货人手机号码" value="" />
</view>
<view class="form-item">
<text class="label">所在地区</text>
<picker class="picker" mode="region" value="">
<view v-if="false">广东省 广州市 天河区</view>
<view v-else class="placeholder">请选择省/市/区(县)</view>
</picker>
</view>
<view class="form-item">
<text class="label">详细地址</text>
<input class="input" placeholder="街道、楼牌号等信息" value="" />
</view>
<view class="form-item">
<label class="label">设为默认地址</label>
<switch class="switch" color="#2979ff" :checked="true" />
</view>
</form>
</view>
<!-- 提交按钮 -->
<button class="button">保存并使用</button>
</template>
<style lang="scss">
page {
background-color: #f4f4f4;
}
.content {
margin: 5rpx 20rpx 0;
padding: 0 20rpx;
border-radius: 10rpx;
background-color: #fff;
.form-item,
.uni-forms-item {
display: flex;
align-items: center;
min-height: 96rpx;
padding: 15rpx 10rpx 40rpx;
background-color: #fff;
font-size: 28rpx;
border-bottom: 1rpx solid #ddd;
position: relative;
margin-bottom: 0;
// 调整 uni-forms 样式
.uni-forms-item__content {
display: flex;
}
.uni-forms-item__error {
margin-left: 200rpx;
}
&:last-child {
border: none;
}
.label {
width: 200rpx;
color: #333;
}
.input {
flex: 1;
display: block;
height: 26rpx;
}
.switch {
position: absolute;
right: -20rpx;
transform: scale(0.8);
}
.picker {
flex: 1;
}
.placeholder {
color: #808080;
}
}
}
.button {
height: 80rpx;
margin: 30rpx 20rpx;
color: #fff;
border-radius: 80rpx;
font-size: 30rpx;
background-color: #2979ff;
}
</style>