Compare commits

..

No commits in common. 'main' and 'qjj' have entirely different histories.
main ... qjj

@ -1,138 +1,2 @@
# 编写的原因
# mall4
写在前面,很多加入我们群里的人,都会问我们源码在哪里,现在仔细回答一下
1. 我们已经声明了这是一个前后端分离的商城而且分离的很彻底java的后台管理系统不是jsp使用vue写的所以商城的后台管理在vue这个项目 https://gitee.com/gz-yami/mall4v ,启动完java不要访问java的端口启动vue访问vue的端口页面就能看到后台管理就能上商品了
2. 和上面一样的,很多人问,前端的浏览商品的页面在哪里,其实就在这里 https://gitee.com/gz-yami/mall4m
3. 有人会问你是不是将上面两个链接藏起来了上面两个项目的链接都在readme里面写着可是很多人都不读。
4. swagger文档怎么访问不了其实路径是/doc.html
5. 我们开源的刚上线直接申请通过了推荐项目第一天就有100个星星一个月就有1k的星星大家都不是傻的这代码是能用的拜托...后来没怎么维护(整个流程都是好的,整个功能都是好的,都不知道维护啥),现在又开始维护了,星星也没了(如果你悄悄拿去做外包项目,觉得这个项目对你有用,就给个星星呗)
## 先确定我们下载的项目有几个项目
- mall4jj代表javajava项目这里面包含了小程序/后台vue连接需要的接口。
- mall4vv代表vue项目是后台管理员界面使用的前端项目因为前后端分离的
- mall4mm代表mini小程序项目这里的项目是小程序的项目
- mall4uniuni代表uniappH5项目这里的项目是H5的项目
- jvmjava虚拟机啦~
## 1.java开发环境安装
### 1.1开发环境
以下版本是最低要求的!!! 提问问题前请注意开发环境!!
| 工具 | 版本 |
|---------|-------|
| jdk | 17 |
| mysql | 5.7+ |
| redis | 4.0+ |
| nodejs | 14-16 |
| xxl-job | 2.4.0 |
### 1.2 安装jdk + mysql + redis + maven
如果不了解怎么安装jdk的可以参考 [菜鸟教程的java相关](https://www.runoob.com/java/java-environment-setup.html)
- 教程展示的是oracle需要自行搜索openjdk的下载链接下载jdk17版本
如果不了解怎么安装mysql的可以参考 [菜鸟教程的mysql相关](https://www.runoob.com/mysql/mysql-install.html)
如果不了解怎么安装maven的可以参考 [菜鸟教程的maven相关]( https://www.runoob.com/maven/maven-setup.html )
如果对于redis的安装并不了解的可以参考 [菜鸟教程的redis相关](https://www.runoob.com/redis/redis-install.html)
安装相对简单网上也有很多教程这里就不多讲述。安装完按需对redis进行配置后启动redis服务即可。
### 2.启动
- 推荐使用idea安装lombok插件后使用idea导入maven项目
- 将yami_shop.sql导入到mysql中修改`application-dev.yml`更改 datasource.url、user、password
- 通过修改`shop.properties` 修改七牛云、阿里大鱼等信息
- 修改`api.properties` 修改当前接口所在域名,用于支付回调
- 启动redis端口6379
- 通过`WebApplication`启动项目后台接口,`ApiApplication` 启动项目前端接口
- xxl-job定时任务通过github或者gitee下载xxl-job的已经打包好的源码把`XxlJobConfig.class`这个文件的代码注释打开配置yml文件中相关xxl-job配置即可使用
## 3.vue开发环境安装
这是一套正常的vue启动流程。如果你无法理解可能要先学习一下vue...
#### 3.1 安装nodejs
[NodeJS](https://nodejs.org) 项目要求最低 18.12.0,推荐 20.9.0
如果不了解怎么安装nodejs的可以参考 [菜鸟教程的nodejs相关](https://www.runoob.com/nodejs/nodejs-install-setup.html)
#### 3.2 安装依赖启动项目
项目要求使用 [pnpm](https://www.pnpm.cn/) 包管理工具
使用编辑器打开项目,在根目录执行以下命令安装依赖
```bash
pnpm i
```
如果不想使用 pnpm请删除 `package.json` 文件中 `preinstall` 脚本后再进行安装
```json
{
"scripts" : {
"preinstall": "npx only-allow pnpm" // 使用其他包管理工具npm、yarn、cnpm等请删除此命令
}
}
```
H5端和平台端修改文件`.env.production`(生产环境)/ `.env.development`(开发环境)
里面的`VITE_APP_BASE_API`为api接口请求地址 `VITE_APP_RESOURCES_URL`为静态资源文件url
```json
// api接口请求地址
VITE_APP_BASE_API = 'http://127.0.0.1:8085'
// 静态资源文件url
VITE_APP_RESOURCES_URL = 'https://img.mall4j.com/'
```
mall4m小程序端修改文件`utils\config.js`,里面的`domain`为api接口请求地址
注意如果启动uni项目或者小程序默认后台api服务端口号为8086
如果启动后台项目默认后台admin服务端口号为8085请对照仔细填写后再启动如遇401状态码仔细检查端口号是否配置正确
如果后台启动后图形验证码显示“接口验证失败数过多请稍后再试”请F12打开network确定连接的admin服务端口号是否正确ip或域名是否正确
如果有配置nginx还要确认下项目访问路径是否正确可以通过地址+/doc.html来访问接口文档确定是否正确访问到admin服务
运行dev环境
```bash
npm run dev
```
运行dev环境(H5)
```bash
npm run dev:h5
```
## 4.文档
这代码有没有文档呀?
当然有啦你已经下载了在doc这个文件夹上实在不知道我就给链接出来咯
### [https://gitee.com/gz-yami/mall4j/tree/master/doc](https://gitee.com/gz-yami/mall4j/tree/master/doc)

@ -1,4 +1,4 @@
# 编写原因
# 编写原因
写在前面,很多加入我们群里的人,都会问我们源码在哪里,现在仔细回答一下

@ -1,6 +1,6 @@
## 商品分组
####商城应用
#### 商城应用
在mall4j精选商城首页中可以看到有`每日上新`、`商城热卖`、`更多商品`等标签栏,在每一栏位中用来展示特定的商品列表,如下图:。

@ -2,7 +2,7 @@
在看具体的数据库实体设计之前,我们先一起了解下**电商的名词定义**
##1.1 名词定义
## 1.1 名词定义
参考 [《产品 SKU 是什么意思?与之相关的还有哪些?》](https://www.zhihu.com/question/19841574) 整理。

@ -2,7 +2,7 @@
##PageAdapter
## PageAdapter
使用分页时,前端传入的数据统一格式为`current`当前页,`size`每页大小。而我们在数据库中要将这两个数据变更为从第几行到第几行,所以我们需要简单的适配一下:

@ -1,6 +1,6 @@
在小程序登陆的时候,在`MiniAppAuthenticationProvider`中我们看到这样一行代码
``java
```java
yamiUserDetailsService.insertUserIfNecessary(appConnect);
```

@ -2,7 +2,7 @@
网上有很多说解决xss攻击的方法有很多都是和前端有关而实际上在后台这最后一个防御当中是最为重要的。
在mall4这个项目里面使用了一个过滤器 `XssFilter`
在mall4j这个项目里面,使用了一个过滤器 `XssFilter`
```
public class XssFilter implements Filter {

@ -7,7 +7,7 @@
- 多图片上传:`src\components\mul-pic-upload`
- 文件上传:`src\components\file-upload`
上述这些文件上传,都是基于`el-upload`进封装
上述这些文件上传,都是基于`el-upload`进封装

@ -1,6 +1,6 @@
## 权限控制
### 前端权限控制
#### 前端权限控制
在商城运营时,我们可能是多个人员共同操作我们的系统,但是每个操作人员所具备的权限应该不同,权限的不同主要表现在两个部分,即导航菜单的查看权限和页面增删改操作按钮的操作权限。我们的把页面导航菜单查看权限和页面操作按钮统一存储在菜单数据库表中,菜单类型页面资源的类型。类型包括目录 、菜单 、按钮。

@ -1,4 +1,4 @@
# 后台异常处理
## 后台异常处理
在开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的`try {...} catch {...} finally {...}` 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类`YamiShopBindException` 与异常管理类 `DefaultExceptionHandlerConfig`

@ -2,7 +2,7 @@
利用`spring`框架中`aop`,我们可以实现业务代码与系统级服务进行解耦,例如日志记录、事务及其他安全业务等,可以使得我们的工程更加容易维护、优雅。如何在系统中添加相应的日志呢?
#### 添加依赖
##### 添加依赖
```
<dependency>

@ -1,4 +1,4 @@
我们后使用`spring` 为我们提供好的统一校验的工具`spring-boot-starter-validation`对请求进行校验。
我们后使用`spring` 为我们提供好的统一校验的工具`spring-boot-starter-validation`对请求进行校验。
```xml
<dependency>

@ -1,4 +1,4 @@
# 通用分页表格实现
## 通用分页表格实现
前端基于VUE的轻量级表格插件 `avue`
后端分页组件使用Mybatis分页插件 `MybatisPlus`

@ -2,7 +2,7 @@
~~~
yami-shops
├──mall4m -- 小程序代码
├── mall4m -- 小程序代码
├── mall4v -- 后台vue代码
├── yami-shop-admin -- 后台vue接口工程[8085]
├── yami-shop-api -- 前端(小程序)接口工程[8086]

@ -1,4 +1,4 @@
里整理了一些经常会被问到的问题:
里整理了一些经常会被问到的问题:
1. 为什么vue打包之后或者修改url之后无法登录
你用chrome按f12看看console提示的信息如`Access-Control-Allow-Origin` 那就是跨域了再看看network的请求方法是不是`options`但是返回不是200这也是跨域了。

@ -13,7 +13,7 @@
我们先来看下是如何获取商品信息的
``java
```java
@PostMapping("/info")
@Operation(summary = "获取用户购物车信息" , description = "获取用户购物车信息,参数为用户选中的活动项数组,以购物车id为key")
public ServerResponseEntity<List<ShopCartDto>> info(@RequestBody Map<Long, ShopCartParam> basketIdShopCartParamMap) {

@ -1,4 +1,4 @@
下单简单的分成几个步骤
下单简单的分成几个步骤
1. 用户点击“立即购买”或“购物车-结算”进入到“确认订单”页面
2. 在“确认订单”页面选择收货地址,优惠券等,重新计算运费、订单价格
@ -7,7 +7,7 @@
# 第一步:
## 第一步:
1. 用户点击“立即购买”或“购物车-结算”进入到“确认订单”页面相关url`/p/order/confirm`

@ -8,7 +8,7 @@
我们返回确认订单的接口,看到这样一行代码:
``java
```java
@Operation(summary = "结算,生成订单信息" , description = "传入下单所需要的参数进行下单")
public ServerResponseEntity<ShopCartOrderMergerDto> confirm(@Valid @RequestBody OrderParam orderParam) {
orderService.putConfirmOrderCache(userId,shopCartOrderMergerDto);

@ -1,6 +1,6 @@
> 我们的支付时不允许在订单的支付接口传订单金额的,所以我们采用了订单号进行支付的形式
# 支付
## 支付
我们来到`PayController` ,这里就是统一支付的接口,当然这里的统一支付采用的是模拟支付。

@ -1,3 +1,3 @@
这里只有几点说明:
1 这里写的是接口设计如果你整个接口的接口文档只需要启动api这个项目然后访问 http://localhost:8086/doc.html
1. 这里写的是接口设计如果你整个接口的接口文档只需要启动api这个项目然后访问 http://localhost:8086/doc.html

@ -3,6 +3,6 @@
安装JDK,如果没有java-17-openjdk-devel就没有javac命令
```bash
yu install java-17-openjdk java-17-openjdk-devel
yum install java-17-openjdk java-17-openjdk-devel
```

@ -14,7 +14,7 @@ docker-compose version 1.17.1, build 6d101fb
Linux 系统请使用以下介绍的方法安装。
# 安装方法一:二进制包
## 安装方法一:二进制包
在 Linux 上的也安装十分简单,从 [官方 GitHub Release](https://github.com/docker/compose/releases) 处直接下载编译好的二进制文件即可。

@ -1,4 +1,4 @@
# 安装 Docker
## 安装 Docker
从 2017 年 3 月开始 docker 在原来的基础上分为两个分支版本: Docker CE 和 Docker EE。
Docker CE 即社区免费版Docker EE 即企业版,强调安全,但需付费使用。

@ -9,7 +9,7 @@ docker pull [OPTIONS] NAME[:TAG|@DIGEST]
```
体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。
体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。
- Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
- 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub如果不给出用户名则默认为 library也就是官方镜像。

@ -4,7 +4,7 @@
从 Docker 镜像仓库获取镜像的命令是 `docker pull`。其命令格式为:
```
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
# docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
```

@ -2,7 +2,7 @@
*如果无法理解我们所编写的 `Dockerfile`强烈的不推荐使用docker进行生产环境部署**
**如果无法理解我们所编写的 `Dockerfile`强烈的不推荐使用docker进行生产环境部署**
0. 将整个项目上传到centos中进入到项目根目录
1. 安装 `docker` (参考《docker centos 安装》)

@ -1,6 +1,6 @@
安装maven的前提是安装jdk参考《linux jdk安装》
``bash
```bash
// 使用配置工具配置第三方epel源仓库
yum-config-manager --add-repo http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo
yum-config-manager --enable epel-apache-maven

@ -2,7 +2,7 @@
Nginx官方提供了Yum源
# 1、安装nginx
## 1、安装nginx
```shell
yum install -y nginx

@ -1,6 +1,6 @@
本文为大家介绍了*CentOS* 7 64位 安装 *MySQL5.7* 的详细步骤
# 1、配置YUM源
## 1、配置YUM源
在[MySQL]官网中下载YUM源rpm安装包http://dev.mysql.com/downloads/repo/yum/

@ -1,7 +1,7 @@
## 安装redis
```
安装tcl redis需要
#安装tcl redis需要
wget http://downloads.sourceforge.net/tcl/tcl8.6.8-src.tar.gz
tar xzvf tcl8.6.8-src.tar.gz -C /usr/local/
cd /usr/local/tcl8.6.8/unix/

@ -1,6 +1,6 @@
## 安装jdk
安JDK,如果没有java-17-openjdk-devel就没有javac命令
JDK,如果没有java-17-openjdk-devel就没有javac命令
```bash
yum install java-17-openjdk java-17-openjdk-devel

@ -1,32 +1,25 @@
// app.js 这是小程序应用的入口文件,用于定义整个小程序的全局配置、生命周期函数以及共享的数据等
var http = require("utils/http.js"); // 引入名为http.js的工具模块可能用于处理网络请求等相关功能
//app.js
var http = require("utils/http.js");
App({
// onLaunch是小程序的生命周期函数在小程序初始化完成时触发
onLaunch: function () {
console.log('mall4j.v230313'); // 在控制台打印出一个标识字符串,可能用于版本之类的标记
// http.getToken(); // 原本可能是用于获取令牌token的调用目前被注释掉了也许暂时不需要在启动时获取或者功能还未完善
// 以下是获取用户授权设置信息的代码块
wx.getSetting({
success(res) {
// 判断用户是否已经授权了'scope.userInfo'(用户信息相关权限),如果没有授权
if (!res.authSetting['scope.userInfo']) {
// 则跳转到名为'/pages/login/login'的页面,引导用户进行登录授权操作
wx.navigateTo({
url: '/pages/login/login',
})
}
}
})
},
globalData: {
// 定义全局请求队列,可用于存放待处理的请求等相关操作,初始化为一个空数组
requestQueue: [],
// 用于标记是否正在进行登录操作初始值设为true后续可根据实际登录状态进行修改
isLanding: true,
// 用于记录购物车中的商品总数量初始值设为0在购物车相关操作时可更新这个值
totalCartCount: 0
}
onLaunch: function () {
console.log('mall4j.v230313')
// http.getToken();
// wx.getSetting({
// success(res) {
// if (!res.authSetting['scope.userInfo']) {
// wx.navigateTo({
// url: '/pages/login/login',
// })
// }
// }
// })
},
globalData: {
// 定义全局请求队列
requestQueue: [],
// 是否正在进行登陆
isLanding: true,
// 购物车商品数量
totalCartCount: 0
}
})

@ -1,92 +1,63 @@
var http = require('../../utils/http.js');
// 定义一个微信小程序的组件
Component({
/**
* 组件的属性列表
* 用于接收外部传入的各种数据来控制组件的展示和行为等
*/
properties: {
// 优惠券相关信息的对象包含如couponId等具体详情数据
item: Object,
// 数字类型,可能用于区分优惠券的类型等,具体用途需结合业务场景判断
type: Number,
// 布尔类型,可能用于判断是否处于某种订单相关的场景等,具体由外部传入决定
order: Boolean,
// 布尔类型,用于判断优惠券是否可使用,外部传入设置该状态
canUse: Boolean,
// 数字类型,可能表示优惠券的序号或者索引等,具体依业务而定
index: Number,
// 数字类型,也许和展示时间相关的类型区分有关,具体功能要看业务逻辑
showTimeType: Number
},
/**
* 组件的初始数据
* 在这里初始化组件内部使用的数据状态
*/
data: {
// 初始设置stsType的值为4具体含义可能和后续的业务逻辑相关比如商品分类筛选等
stsType: 4
},
// 生命周期函数可以为函数或一个在methods段中定义的方法名
attached: function () {
// 打印当前组件中data里的item数据方便调试查看传入的优惠券相关信息可根据实际需求决定是否保留该语句
// console.log(this.data.item);
attached: function() {
//console.log(this.data.item);
},
/**
* 组件的方法列表
* 定义了组件内可调用的各种方法用于实现不同的业务功能如操作优惠券等
*/
methods: {
// 接收优惠券的方法
receiveCoupon() {
// 获取当前组件中data里item对象的couponId属性值作为要接收的优惠券的ID
var couponId = this.data.item.couponId;
// 使用引入的http模块发起一个POST请求向服务端请求接收优惠券
http.request({
// 请求的接口地址,用于接收优惠券的服务端接口
url: "/p/myCoupon/receive",
// 请求方法为POST
method: "POST",
// 将获取到的优惠券ID作为请求的数据发送给服务端此处可能需根据服务端要求调整格式比如包装成对象等
data: couponId,
callBack: () => {
// 获取当前组件中的优惠券对象(此处可能是为了更新其相关状态)
var coupon = this.data.item;
// 将优惠券的可接收状态设置为false表示已接收
coupon.canReceive = false;
// 通过setData更新组件中data里的item数据从而更新组件的展示状态等
this.setData({
item: coupon
})
}
})
},
// 查看优惠券的方法,会触发一个名为'checkCoupon'的自定义事件,并传递相关数据
checkCoupon(e) {
// 触发名为'checkCoupon'的自定义事件并传递当前组件的索引之前定义的index属性该语句被注释掉了可能有其他考虑比如更换了传递数据的方式
// this.triggerEvent('checkCoupon', this.data.index);
// 触发名为'checkCoupon'的自定义事件传递包含当前点击元素的优惠券ID从事件对象的dataset中获取的对象用于在外部处理查看优惠券的相关逻辑
this.triggerEvent('checkCoupon', {
couponId: e.currentTarget.dataset.couponid
});
},
/**
* 立即使用优惠券的方法
* 用于导航到相应的商品分类页面并携带优惠券相关信息作为参数以展示可使用优惠券的商品等
* 立即使用
*/
useCoupon() {
// 构建要导航到的页面地址先设置基础的商品分类页面路径并添加stsType参数初始值为4有其特定业务含义
var url = '/pages/prod-classify/prod-classify?sts=' + this.data.stsType;
// 获取当前组件中data里item对象的couponId属性值作为要传递的优惠券ID
var id = this.data.item.couponId;
// 设置页面标题为"优惠券活动商品",用于在导航到的页面展示等
var title = "优惠券活动商品";
// 如果获取到了优惠券ID则将其和标题作为参数添加到页面地址后面方便目标页面根据这些参数展示相应内容
if (id) {
url += "&tagid=" + id + "&title=" + title;
}
// 使用微信小程序的导航API跳转到构建好的页面地址对应的页面
wx.navigateTo({
url: url
})

@ -1,49 +1,30 @@
<!-- 这是一个优惠券展示的视图组件根据canUse属性的值来决定是否添加'gray'类名,从而控制优惠券的样式(比如是否为灰色不可用状态) -->
<view class="coupon-item {{canUse?'':'gray'}}">
<!-- 优惠券左边部分的视图容器,用于展示优惠券金额、折扣、使用条件等信息 -->
<view class='left'>
<!-- 当优惠券类型couponType为1时展示固定金额优惠的相关信息 -->
<view class="num" wx:if="{{item.couponType == 1}}">
<!-- 先展示人民币符号 -->
<!-- 展示具体的优惠金额金额数据从item对象的reduceAmount属性获取 -->
<text class="coupon-price">{{item.reduceAmount}}</text>
</view>
<!-- 当优惠券类型couponType为2时展示折扣优惠的相关信息 -->
<view class="num" wx:if="{{item.couponType == 2}}">
<!-- 展示具体的折扣数值折扣数据从item对象的couponDiscount属性获取 -->
<text class="coupon-price">{{item.couponDiscount}}</text>折
</view>
<!-- 展示优惠券的使用条件满多少金额可用金额数据从item对象的cashCondition属性获取 -->
<view class='condition'>
满{{item.cashCondition}}元可用
</view>
<view class='left'>
<view class="num" wx:if="{{item.couponType == 1}}">
<text class="coupon-price">{{item.reduceAmount}}</text>
</view>
<!-- 优惠券右边部分的视图容器,用于展示优惠券适用商品类型、有效期、领取/使用按钮等信息 -->
<view class='right'>
<!-- 展示优惠券适用商品类型相关的描述信息 -->
<view class="c-des">
<!-- 根据suitableProdType的值判断是通用还是指定商品可用并展示相应文字描述 -->
<text class="c-type">{{item.suitableProdType==0?'通用':'商品'}}</text> {{item.suitableProdType==0?'全场通用':'指定商品可用'}}
</view>
<!-- 展示优惠券有效期相关的信息 -->
<view class="c-date">
<!-- 当showTimeType为1且优惠券类型couponType为2时展示领券后多少天失效的信息天数从item对象的validDays属性获取 -->
<text wx:if="{{showTimeType==1 && item.couponType==2}}" class="c-data-info">领券{{item.validDays}}天后失效</text>
<!-- 其他情况展示具体的开始时间到结束时间的有效期范围时间数据分别从item对象的startTime和endTime属性获取 -->
<text wx:else class="c-data-info">{{item.startTime}}~{{item.endTime}}</text>
<!-- 当优惠券可领取canReceive为true且不在订单场景!order为true展示“立即领取”按钮并绑定点击事件'receiveCoupon',点击可触发领取优惠券的操作 -->
<text class="c-btn" wx:if="{{item.canReceive &&!order}}" bindtap='receiveCoupon'>立即领取</text>
<!-- 当优惠券不可领取(!item.canReceive为true且不在订单场景!order为true展示“立即使用”按钮并绑定点击事件'useCoupon',点击可触发使用优惠券的操作 -->
<text class="c-btn get-btn" wx:if="{{!item.canReceive &&!order}}" bindtap='useCoupon'>立即使用</text>
</view>
<!-- 当处于订单场景order为true且优惠券可用canUse为true展示选择按钮checkbox用于选择该优惠券同时绑定点击事件'checkCoupon'并传递优惠券ID从item对象的couponId属性获取等相关数据 -->
<view wx:if="{{order && canUse}}" class="sel-btn">
<checkbox color="#eb2444" data-couponid="{{item.couponId}}" checked="{{item.choose}}" bindtap="checkCoupon"></checkbox>
</view>
<view class="num" wx:if="{{item.couponType == 2}}">
<text class="coupon-price">{{item.couponDiscount}}</text>折
</view>
<!-- 当优惠券类型type为1时展示对应的标签图片已使用的优惠券图标 -->
<image class="tag-img" src="../../images/icon/coupon-used.png" wx:if="{{type==1}}"></image>
<!-- 当优惠券类型type为2时展示对应的标签图片其他类型的优惠券图标具体需看实际图标含义 -->
<image class="tag-img" src="../../images/icon/coupon-ot.png" wx:if="{{type==2}}"></image>
<view class='condition'>
满{{item.cashCondition}}元可用
</view>
</view>
<view class='right'>
<view class="c-des">
<text class="c-type">{{item.suitableProdType==0?'通用':'商品'}}</text> {{item.suitableProdType==0?'全场通用':'指定商品可用'}}
</view>
<view class="c-date">
<text wx:if="{{showTimeType==1 && item.couponType==2}}" class="c-data-info">领券{{item.validDays}}天后失效</text>
<text wx:else class="c-data-info">{{item.startTime}}~{{item.endTime}}</text>
<text class="c-btn" wx:if="{{item.canReceive && !order}}" bindtap='receiveCoupon'>立即领取</text>
<text class="c-btn get-btn" wx:if="{{!item.canReceive && !order}}" bindtap='useCoupon'>立即使用</text>
</view>
<view wx:if="{{order && canUse}}" class="sel-btn">
<checkbox color="#eb2444" data-couponid="{{item.couponId}}" checked="{{item.choose}}" bindtap="checkCoupon"></checkbox>
</view>
</view>
<image class="tag-img" src="../../images/icon/coupon-used.png" wx:if="{{type==1}}"></image>
<image class="tag-img" src="../../images/icon/coupon-ot.png" wx:if="{{type==2}}"></image>
</view>

@ -1,220 +1,118 @@
/* 优惠券项目整体的样式类,用于设置优惠券的整体外观和布局等相关样式 */
.coupon-item {
/* 设置外边距上下外边距为15px左右外边距为0 */
.coupon-item{
margin: 15px 0;
/* 设置相对定位,方便其内部元素基于它进行定位 */
position: relative;
/* 添加阴影效果水平和垂直方向偏移1px模糊半径3px颜色透明度为0.15的黑色阴影 */
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.15);
/* 设置高度为95px */
box-shadow: 1px 1px 3px rgba(0,0,0,0.15);
height: 95px;
/* 设置背景颜色为白色 */
background: #fff;
}
/* 优惠券项目中左边部分的样式类,用于设置优惠券左边区域的样式,比如颜色、布局等 */
.coupon-item.left {
/* 设置左浮动,使其在水平方向上向左排列 */
float: left;
/* 设置文字颜色为白色 */
.coupon-item .left{
float: left;
color: #fff;
/* 设置文本居中对齐 */
text-align: center;
/* 添加左边框为1px的虚线颜色为白色 */
border-left: 1px dashed #fff;
/* 设置上下内边距为20px左右内边距为0 */
padding: 20px 0;
/* 设置背景渐变,从#F45C43到#eb2444的线性渐变webkit内核浏览器兼容写法 */
background: -webkit-gradient(linear, left top, right top, from(#F45C43), to(#eb2444));
/* 设置背景渐变,从#F45C43到#eb2444的线性渐变opera浏览器兼容写法 */
background: -o-linear-gradient(left, #F45C43, #eb2444);
/* 设置背景渐变,从#F45C43到#eb2444的线性渐变标准写法 */
background: linear-gradient(left, #F45C43, #eb2444);
/* 设置背景渐变,从#F45C43到#eb2444的线性渐变webkit内核浏览器另一种兼容写法 */
background: -webkit-linear-gradient(left, #F45C43, #eb2444);
/* 设置宽度为260rpxrpx是微信小程序中的自适应单位会根据屏幕宽度自动换算 */
background: -webkit-gradient(linear,left top,right top,from(#F45C43),to(#eb2444));
background: -o-linear-gradient(left,#F45C43,#eb2444);
background: linear-gradient(left,#F45C43,#eb2444);
background: -webkit-linear-gradient(left,#F45C43,#eb2444);
width: 260rpx;
/* 设置高度为55px */
height: 55px;
}
/* 优惠券项目左边部分中金额数字的样式类,用于设置金额数字相关的样式,比如字体、大小、行高等 */
.coupon-item.left.num {
/* 设置字体加粗权重为600 */
font-weight: 600;
/* 设置字体大小为36rpx */
font-size: 36rpx;
/* 设置高度为70rpx */
height: 70rpx;
/* 设置行高为70rpx使文字垂直居中 */
line-height: 70rpx;
/* 设置字体为arial字体 */
font-family: arial;
.coupon-item .left .num{
font-weight:600;
font-size:36rpx;
height:70rpx;
line-height:70rpx;
font-family:arial;
}
/* 优惠券项目左边部分中金额数字里优惠券价格的样式类,用于对优惠券价格的字体等样式进行单独设置 */
.coupon-item.left.num.coupon-price {
/* 设置字体大小为72rpx */
font-size: 72rpx;
/* 设置行高为72rpx */
.coupon-item .left .num .coupon-price{
font-size: 72rpx;
line-height: 72rpx;
/* 设置为行内块元素,方便在同一行内进行布局调整等 */
display: inline-block;
/* 设置字体为arial字体 */
display: inline-block;
font-family: arial;
}
/* 优惠券项目左边部分中使用条件的样式类,用于设置优惠券使用条件相关的文本样式,比如字体、大小、溢出处理等 */
.coupon-item.left.condition {
/* 设置字体大小为28rpx */
.coupon-item .left .condition{
font-size: 28rpx;
/* 设置行高为28rpx */
line-height: 28rpx;
/* 设置为块级元素 */
display: block;
/* 文本超出一行时不换行 */
white-space: nowrap;
/* 超出部分隐藏 */
overflow: hidden;
/* 超出部分用省略号显示 */
text-overflow: ellipsis;
/* 设置左右内边距各2px */
padding: 0 2px;
/* 设置字体为arial字体 */
font-family: arial;
}
/* 优惠券项目中右边部分的样式类,用于设置优惠券右边区域的样式,比如边距、布局等 */
.coupon-item.right {
/* 设置左边距为280rpx使其与左边部分隔开一定距离 */
margin-left: 280rpx;
/* 设置上下左右内边距为5px */
.coupon-item .right{
margin-left: 280rpx;
padding: 5px;
/* 设置相对定位,方便其内部元素基于它进行定位 */
position: relative;
}
/* 优惠券项目右边部分中描述信息的样式类,用于设置描述信息相关的样式,比如高度、字体、溢出处理等 */
.coupon-item.right.c-des {
/* 设置高度为30px */
height: 30px;
/* 设置字体大小为26rpx */
.coupon-item .right .c-des{
height: 30px;
font-size: 26rpx;
/* 设置行高为30px */
line-height: 30px;
/* 超出部分隐藏 */
overflow: hidden;
/* 设置字体加粗权重为600 */
font-weight: 600;
}
/* 优惠券项目右边部分中描述信息里类型的样式类,用于设置优惠券类型相关的样式,比如字体、背景色、圆角等 */
.coupon-item.right.c-des.c-type {
/* 设置字体大小为24rpx */
font-size: 24rpx;
/* 设置背景颜色为淡红色系,用于突出显示类型 */
.coupon-item .right .c-des .c-type{
font-size: 24rpx;
background: #fdf0f0;
/* 设置文字颜色为红色系,与背景形成对比 */
color: #eb2444;
/* 设置圆角半径为8px使元素看起来更圆润 */
border-radius: 8px;
/* 设置上下左右内边距分别为3px和10px */
padding: 3px 10px;
padding:3px 10px;
}
/* 优惠券项目右边部分中日期信息的样式类,用于设置日期相关的样式,比如字体、外边距等 */
.coupon-item.right.c-date {
/* 设置字体大小为24rpx */
font-size: 24rpx;
/* 设置上边距为25px使其与上面的描述信息隔开一定距离 */
margin-top: 25px;
.coupon-item .right .c-date{
font-size: 24rpx;
margin-top:25px;
}
/* 优惠券项目右边部分中日期信息里具体数据信息的样式类,用于设置日期具体数据相关的样式,比如字体 */
.coupon-item.right.c-date.c-data-info {
/* 设置字体为arial字体 */
font-family: arial;
.coupon-item .right .c-date .c-data-info{
font-family: arial;
}
/* 优惠券项目右边部分中日期信息里按钮的样式类,用于设置按钮的通用样式,比如位置、字体、圆角、背景等 */
.coupon-item.right.c-date.c-btn {
/* 设置绝对定位,使其可以精确地定位在父元素内的某个位置 */
.coupon-item .right .c-date .c-btn{
position: absolute;
/* 定位到底部距离底部0距离 */
bottom: 0;
/* 定位到右边距离右边10px距离 */
right: 10px;
/* 设置文字颜色为白色 */
bottom:0;
right:10px;
color: #fff;
/* 设置字体大小为24rpx */
font-size: 24rpx;
/* 设置字体为arial字体 */
font-family: arial;
/* 设置圆角半径为14px使按钮看起来更圆润 */
border-radius: 14px;
/* 设置上下左右内边距分别为3px和7px */
padding: 3px 7px;
/* 设置背景渐变,从#6c96da到#6b83d7的线性渐变webkit内核浏览器兼容写法这里被注释掉了可能有其他背景设置需求 */
/* background: -webkit-gradient(linear, left top, right top, from(#6c96da), to(#6b83d7));
background: -o-linear-gradient(left, #6c96da, #6b83d7);
background: linear-gradient(left, #6c96da, #6b83d7);
background: -webkit-linear-gradient(left, #6c96da, #6b83d7); */
/* 设置背景颜色为红色系 */
padding:3px 7px;
/* background: -webkit-gradient(linear,left top,right top,from(#6c96da),to(#6b83d7));
background: -o-linear-gradient(left,#6c96da,#6b83d7);
background: linear-gradient(left,#6c96da,#6b83d7);
background: -webkit-linear-gradient(left,#6c96da,#6b83d7); */
background: #eb2444;
/* 设置边框为1px的实线颜色为红色系 */
border: 1px solid #eb2444;
}
/* 优惠券项目右边部分中日期信息里获取按钮的样式类,用于对获取按钮这种特定按钮进行样式覆盖,比如背景、文字颜色等 */
.coupon-item.right.c-date.c-btn.get-btn {
/* 设置背景颜色为白色 */
background: #fff;
/* 设置边框为1px的实线颜色为红色系 */
border: 1px solid #eb2444;
/* 设置文字颜色为红色系 */
color: #eb2444;
.coupon-item .right .c-date .c-btn.get-btn{
background: #fff;
border: 1px solid #eb2444;
color:#eb2444;
}
/* 优惠券项目处于灰色状态时左边部分的样式类,用于对灰色状态下左边部分的背景等样式进行覆盖 */
.coupon-item.gray.left {
/* 设置背景颜色为灰色 */
background: #bbb;
.coupon-item.gray .left{
background: #bbb;
}
/* 优惠券项目处于灰色状态时右边部分中描述信息里类型的样式类,用于对灰色状态下类型的背景、文字颜色等样式进行覆盖 */
.coupon-item.gray.right.c-des.c-type {
/* 设置背景颜色为灰色 */
background: #bbb;
/* 设置文字颜色为白色 */
.coupon-item.gray .right .c-des .c-type{
background: #bbb;
color: #fff;
}
/* 优惠券项目处于灰色状态时右边部分中日期信息里按钮的样式类,用于隐藏灰色状态下的按钮 */
.coupon-item.gray.right.c-date.c-btn {
/* 设置为不显示 */
display: none;
.coupon-item.gray .right .c-date .c-btn{
display: none;
}
/* 优惠券项目中标签图片的样式类,用于设置标签图片的位置、大小等样式 */
.coupon-item.tag-img {
/* 设置绝对定位,方便精确放置在父元素内的某个位置 */
position: absolute;
/* 定位到顶部距离顶部0距离 */
top: 0;
/* 定位到右边距离右边0距离 */
right: 0;
/* 设置宽度为120rpx */
width: 120rpx;
/* 设置高度为120rpx */
height: 120rpx;
.coupon-item .tag-img{
position: absolute;
top:0;
right:0;
width:120rpx;
height:120rpx;
}
/* 优惠券项目中选择按钮的样式类,用于设置选择按钮的位置等样式 */
.coupon-item.sel-btn {
/* 设置绝对定位,方便精确放置在父元素内的某个位置 */
position: absolute;
/* 定位到右边距离右边10px距离 */
right: 10px;
/* 定位到顶部距离顶部35px距离 */
top: 35px;
}
.coupon-item .sel-btn{
position:absolute;
right:10px;
top:35px;
}

@ -1,40 +1,30 @@
// components/production/production.js
// 定义一个小程序组件使用Component函数来创建组件在小程序中是可复用的独立模块有自己的属性、数据和方法等
Component({
/**
* 组件的属性列表
* 这里定义了组件外部可以传入的属性相当于组件的输入参数外部使用者可以通过设置这些属性来影响组件的展示或行为等
*/
properties: {
// item属性类型为Object对象具体的对象结构和用途应该由组件的使用场景决定外部可以传入一个对象数据给组件
item: Object,
// sts属性类型为Number数字同样其具体含义取决于组件的业务逻辑外部传入一个数字值给组件
sts: Number,
},
/**
* 组件的属性列表
*/
properties: {
item:Object,
sts:Number,
},
/**
* 组件的初始数据
* 这里定义组件内部私有的数据在组件的生命周期内可以对这些数据进行修改操作初始时可以为空对象后续可根据业务逻辑添加相应的数据
*/
data: {
/**
* 组件的初始数据
*/
data: {
},
},
/**
* 组件的方法列表
* 这里定义了组件内部可以调用的方法用于实现各种功能比如响应事件进行数据处理等
*/
methods: {
// toProdPage方法用于处理跳转到产品详情页面的功能通常是在某个用户交互事件触发时调用比如点击某个元素等情况。
toProdPage: function (e) {
// 从事件对象e的currentTarget.dataset中获取名为prodid的数据这个数据应该是在页面元素上通过自定义数据属性data-*绑定的产品ID相关信息
var prodid = e.currentTarget.dataset.prodid;
// 使用wx.navigateTo API进行页面跳转跳转到名为'/pages/prod/prod'的页面并将获取到的产品IDprodid作为查询参数传递过去
// 这样目标页面可以根据这个参数获取并展示对应的产品详情信息。
wx.navigateTo({
url: '/pages/prod/prod?prodid=' + prodid,
})
},
}
})
/**
* 组件的方法列表
*/
methods: {
toProdPage: function (e) {
var prodid = e.currentTarget.dataset.prodid;
wx.navigateTo({
url: '/pages/prod/prod?prodid=' + prodid,
})
},
}
})

@ -1,32 +1,19 @@
<!-- 这是一个商品展示的视图组件,绑定了点击事件'toProdPage'点击该组件会触发跳转到商品详情页面的操作同时传递商品ID从item对象的prodId属性获取作为参数 -->
<view class='prod-items' bindtap='toProdPage' data-prodid="{{item.prodId}}">
<!-- 用于展示商品图片的容器视图 -->
<view class='hot-imagecont'>
<!-- 展示商品图片图片的路径从item对象的pic属性获取设置了对应的图片样式类'hotsaleimg' -->
<view class='prod-items' bindtap='toProdPage' data-prodid="{{item.prodId}}">
<view class='hot-imagecont'>
<image src='{{item.pic}}' class='hotsaleimg' ></image>
</view>
<!-- 用于展示商品相关文本信息的容器视图 -->
<view class='hot-text'>
<!-- 展示商品名称商品名称数据从item对象的prodName属性获取 -->
</view>
<view class='hot-text'>
<view class='hotprod-text'>{{item.prodName}}</view>
<!-- 当sts的值等于6时展示商品的评价数量以及好评率相关信息数据分别从item对象的prodCommNumber和positiveRating属性获取 -->
<view class='prod-info' wx:if='{{sts==6}}'>{{item.prodCommNumber}}评价 {{item.positiveRating}}%好评</view>
<!-- 用于展示商品价格等文本信息的容器视图 -->
<view class='prod-text-info'>
<!-- 用于展示价格相关内容的容器视图 -->
<view class='price'>
<!-- 当sts的值等于2时展示“限时价”的提示文本 -->
<text wx:if='{{sts==2}}' class='deadline-price'>限时价</text>
<!-- 展示人民币符号 -->
<text class='symbol'>¥</text>
<!-- 展示商品价格的整数部分这里调用了wxs模块中的parsePrice函数对item对象的price属性进行处理取处理结果的第一个元素作为整数部分展示 -->
<text class='big-num'>{{wxs.parsePrice(item.price)[0]}}</text>
<!-- 展示商品价格的小数部分同样通过wxs模块的parsePrice函数取第二个元素来展示 -->
<text class='small-num'>.{{wxs.parsePrice(item.price)[1]}}</text>
</view>
<view class='price'>
<text wx:if='{{sts==2}}' class='deadline-price'>限时价</text>
<text class='symbol'>¥</text>
<text class='big-num'>{{wxs.parsePrice(item.price)[0]}}</text>
<text class='small-num'>.{{wxs.parsePrice(item.price)[1]}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 引入wxs模块模块名为'wxs'对应的wxs代码文件路径为'../../wxs/number.wxs'这个模块可能包含了一些对数据进行处理的函数比如此处用到的parsePrice函数 -->
<wxs module="wxs" src="../../wxs/number.wxs" />

@ -1,112 +1,66 @@
/* 导入上级目录下的 app.wxss 文件,用于引入一些全局样式或基础样式设置,使当前样式文件可以复用其中的样式规则 */
@import "../../app.wxss";
/* 商品展示项prod-items的样式类用于设置商品展示区域整体的样式如宽度、布局、背景色、内边距等 */
.prod-items {
/* 设置宽度为 375rpxrpx 是微信小程序中的自适应单位,会根据屏幕宽度自动换算) */
width: 375rpx;
/* 设置左浮动,使其在水平方向上按照浮动规则排列 */
float: left;
/* 设置背景颜色为白色 */
background: #fff;
/* 设置底部内边距为 20rpx用于和内部元素隔开一定距离等 */
padding-bottom: 20rpx;
/* 设置盒模型为 border-box这样设置内边距和边框不会增加元素的整体宽度 */
box-sizing: border-box;
width: 375rpx;
float: left;
background: #fff;
padding-bottom: 20rpx;
box-sizing: border-box;
}
/* 选择奇数位置2n - 1的 prod 元素下的 prod-items 元素的样式规则,用于对奇数位置商品展示项设置不同的内边距样式 */
prod:nth-child(2n - 1).prod-items {
/* 设置上、右、下、左的内边距分别为 20rpx、10rpx、10rpx、20rpx以调整内部元素布局和间距 */
padding: 20rpx 10rpx 10rpx 20rpx;
prod:nth-child(2n-1) .prod-items {
padding: 20rpx 10rpx 10rpx 20rpx;
}
/* 选择偶数位置2n的 prod 元素下的 prod-items 元素的样式规则,用于对偶数位置商品展示项设置不同的内边距样式 */
prod:nth-child(2n).prod-items {
/* 设置上、右、下、左的内边距分别为 20rpx、20rpx、10rpx、10rpx以调整内部元素布局和间距 */
padding: 20rpx 20rpx 10rpx 10rpx;
prod:nth-child(2n) .prod-items {
padding: 20rpx 20rpx 10rpx 10rpx;
}
/* 商品展示项中热门图片容器hot-imagecont里图片hotsaleimg的样式类用于设置商品图片的尺寸大小 */
.hot-imagecont.hotsaleimg {
/* 设置图片宽度为 345rpx */
width: 345rpx;
/* 设置图片高度为 345rpx */
height: 345rpx;
}
.hot-imagecont .hotsaleimg {
width:345rpx;
height:345rpx;
/* 商品展示项中热门文本hot-text里商品名称hotprod-text的样式类用于设置商品名称相关的文本样式如高度、字体大小、文本溢出处理等 */
.hot-text.hotprod-text {
/* 设置高度为 76rpx */
height: 76rpx;
/* 设置字体大小为 28rpx */
font-size: 28rpx;
/* 使用-webkit-box 布局方式,这是一种弹性盒子布局的旧浏览器兼容写法,用于实现多行文本溢出省略显示的效果 */
display: -webkit-box;
/* 允许单词在边界处断开换行,避免文本过长出现显示问题 */
word-break: break-all;
/* 超出部分隐藏 */
overflow: hidden;
/* 超出部分用省略号显示 */
text-overflow: ellipsis;
/* 再次声明使用-webkit-box 布局方式,这是兼容写法的一部分 */
display: -webkit-box;
/* 设置显示的行数为 2 行,结合前面的属性实现文本最多显示 2 行并溢出省略的效果 */
-webkit-line-clamp: 2;
/* 设置盒子的排列方向为垂直方向,用于多行文本的布局控制 */
-webkit-box-orient: vertical;
/* 设置文本颜色为黑色 */
color: #000;
}
/* 商品展示项中热门图片容器hot-imagecont的样式类用于设置图片容器的样式如圆角、文本对齐、字体大小等 */
.prod-items.hot-imagecont {
/* 设置圆角半径为 8rpx使图片容器看起来更圆润 */
border-radius: 8rpx;
/* 设置文本居中对齐,对于图片容器内可能有的文本元素生效 */
text-align: center;
/* 设置字体大小为 0通常用于消除图片与其他内联元素之间的默认间距问题等 */
font-size: 0;
.hot-text .hotprod-text {
height: 76rpx;
font-size: 28rpx;
display: -webkit-box;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: #000;
}
/* 商品展示项中热门文本hot-text的样式类用于设置热门文本区域整体的外边距等样式使其与上面的图片容器隔开一定距离 */
.prod-items.hot-text {
/* 设置上边距为 20rpx与上面的图片容器拉开距离 */
margin-top: 20rpx;
.prod-items .hot-imagecont {
border-radius: 8rpx;
text-align: center;
font-size: 0;
}
/* 商品展示项中热门文本hot-text里商品评价信息prod-info的样式类用于设置商品评价相关的文本样式如字体大小、颜色、外边距等 */
.prod-items.hot-text.prod-info {
/* 设置字体大小为 20rpx */
font-size: 20rpx;
/* 设置文本颜色为灰色系,用于和其他主要信息区分开来,体现辅助信息的感觉 */
color: #777;
/* 设置上边距为 8rpx与上面的商品名称等信息隔开一定距离 */
margin-top: 8rpx;
.prod-items .hot-text {
margin-top: 20rpx;
}
/* 商品展示项中热门文本hot-text里商品价格信息prod-text-info的样式类用于设置商品价格信息区域整体的样式如位置、高度、行高等 */
.prod-items.hot-text.prod-text-info {
/* 设置相对定位,方便其内部元素基于它进行定位 */
position: relative;
/* 设置高度为 50rpx */
height: 50rpx;
/* 设置行高为 70rpx用于调整文本在垂直方向上的位置等 */
line-height: 70rpx;
/* 设置字体为 Arial 字体 */
font-family: Arial;
.prod-items .hot-text .prod-info {
font-size: 20rpx;
color: #777;
margin-top: 8rpx;
}
/* 商品展示项中热门文本hot-text里商品价格信息prod-text-info中价格price的样式类用于设置价格文本的颜色使其突出显示 */
.prod-items.hot-text.prod-text-info.price {
/* 设置文本颜色为红色系,通常用于醒目地展示价格信息 */
color: #eb2444;
.prod-items .hot-text .prod-text-info {
position: relative;
height: 50rpx;
line-height: 70rpx;
font-family: Arial;
}
/* 商品展示项中限时价deadline-price的样式类用于设置限时价文本的字体大小、外边距等样式 */
.deadline-price {
/* 设置字体大小为 22rpx */
font-size: 22rpx;
/* 设置右边距为 5rpx使其与旁边的价格等文本隔开一定距离 */
margin-right: 5rpx;
.prod-items .hot-text .prod-text-info .price {
color: #eb2444;
}
.deadline-price{
font-size: 22rpx;
margin-right: 5rpx;
}

@ -1,345 +1,242 @@
// 模块导出的自执行函数,用于定义模块的导出内容以及处理模块的依赖加载等逻辑
module.exports = (function () {
var __MODS__ = {};
// 定义模块的函数,用于创建一个模块的相关信息对象,包括模块状态、执行函数、依赖等
var __DEFINE__ = function (modId, func, req) {
var m = { exports: {}, _tempexports: {} };
__MODS__[modId] = { status: 0, func: func, req: req, m: m };
};
// 用于加载模块的函数如果模块未加载则通过原生的require加载若已定义但未执行则执行模块函数并返回其导出内容
var __REQUIRE__ = function (modId, source) {
if (!__MODS__[modId]) return require(source);
if (!__MODS__[modId].status) {
var m = __MODS__[modId].m;
m._exports = m._tempexports;
var desp = Object.getOwnPropertyDescriptor(m, "exports");
if (desp && desp.configurable)
Object.defineProperty(m, "exports", {
set: function (val) {
if (typeof val === "object" && val!== m._exports) {
m._exports.__proto__ = val.__proto__;
Object.keys(val).forEach(function (k) {
m._exports[k] = val[k];
});
}
m._tempexports = val;
},
get: function () {
return m._tempexports;
}
});
__MODS__[modId].status = 1;
__MODS__[modId].func(__MODS__[modId].req, m, m.exports);
}
return __MODS__[modId].m.exports;
};
// 处理通配符导入的模块如果模块是ES模块则直接返回否则创建一个新对象并将原对象的属性复制过去同时添加默认属性指向原对象
var __REQUIRE_WILDCARD__ = function (obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj!= null) {
for (var k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k];
}
}
newObj.default = obj;
return newObj;
}
};
// 获取模块默认导出的函数如果模块是ES模块则返回其default属性否则直接返回模块对象
var __REQUIRE_DEFAULT__ = function (obj) {
return obj && obj.__esModule? obj.default : obj;
};
module.exports = (function() {
var __MODS__ = {};
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
__DEFINE__(1648886413910, function(require, module, exports) {
;(function (root, factory, undef) {
if (typeof exports === "object") {
// CommonJS
module.exports = exports = factory(require("./core"), require("./x64-core"), require("./lib-typedarrays"), require("./enc-utf16"), require("./enc-base64"), require("./enc-base64url"), require("./md5"), require("./sha1"), require("./sha256"), require("./sha224"), require("./sha512"), require("./sha384"), require("./sha3"), require("./ripemd160"), require("./hmac"), require("./pbkdf2"), require("./evpkdf"), require("./cipher-core"), require("./mode-cfb"), require("./mode-ctr"), require("./mode-ctr-gladman"), require("./mode-ofb"), require("./mode-ecb"), require("./pad-ansix923"), require("./pad-iso10126"), require("./pad-iso97971"), require("./pad-zeropadding"), require("./pad-nopadding"), require("./format-hex"), require("./aes"), require("./tripledes"), require("./rc4"), require("./rabbit"), require("./rabbit-legacy"));
}
else if (typeof define === "function" && define.amd) {
// AMD
define(["./core", "./x64-core", "./lib-typedarrays", "./enc-utf16", "./enc-base64", "./enc-base64url", "./md5", "./sha1", "./sha256", "./sha224", "./sha512", "./sha384", "./sha3", "./ripemd160", "./hmac", "./pbkdf2", "./evpkdf", "./cipher-core", "./mode-cfb", "./mode-ctr", "./mode-ctr-gladman", "./mode-ofb", "./mode-ecb", "./pad-ansix923", "./pad-iso10126", "./pad-iso97971", "./pad-zeropadding", "./pad-nopadding", "./format-hex", "./aes", "./tripledes", "./rc4", "./rabbit", "./rabbit-legacy"], factory);
}
else {
// Global (browser)
root.CryptoJS = factory(root.CryptoJS);
}
}(this, function (CryptoJS) {
// 定义模块ID为1648886413910的模块它主要处理不同环境下CommonJS、AMD、全局浏览器环境CryptoJS的导出逻辑
__DEFINE__(1648886413910, function (require, module, exports) {
// 立即执行函数根据不同的模块环境CommonJS、AMD、全局浏览器环境来设置CryptoJS的导出方式
(function (root, factory, undef) {
if (typeof exports === "object") {
// CommonJS环境下将factory函数执行的结果赋值给module.exports和exportsfactory函数需要传入多个依赖模块
module.exports = exports = factory(
require("./core"),
require("./x64-core"),
require("./lib-typedarrays"),
require("./enc-utf16"),
require("./enc-base64"),
require("./enc-base64url"),
require("./md5"),
require("./sha1"),
require("./sha256"),
require("./sha224"),
require("./sha512"),
require("./sha384"),
require("./sha3"),
require("./ripemd160"),
require("./hmac"),
require("./pbkdf2"),
require("./evpkdf"),
require("./cipher-core"),
require("./mode-cfb"),
require("./mode-ctr"),
require("./mode-ctr-gladman"),
require("./mode-ofb"),
require("./mode-ecb"),
require("./pad-ansix923"),
require("./pad-iso10126"),
require("./pad-iso97971"),
require("./pad-zeropadding"),
require("./pad-nopadding"),
require("./format-hex"),
require("./aes"),
require("./tripledes"),
require("./rc4"),
require("./rabbit"),
require("./rabbit-legacy")
);
} else if (typeof define === "function" && define.amd) {
// AMD环境下使用define函数定义模块传入依赖数组和factory函数
define([
"./core",
"./x64-core",
"./lib-typedarrays",
"./enc-utf16",
"./enc-base64",
"./enc-base64url",
"./md5",
"./sha1",
"./sha256",
"./sha224",
"./sha512",
"./sha384",
"./sha3",
"./ripemd160",
"./hmac",
"./pbkdf2",
"./evpkdf",
"./cipher-core",
"./mode-cfb",
"./mode-ctr",
"./mode-ctr-gladman",
"./mode-ofb",
"./mode-ecb",
"./pad-ansix923",
"./pad-iso10126",
"./pad-iso97971",
"./pad-zeropadding",
"./pad-nopadding",
"./format-hex",
"./aes",
"./tripledes",
"./rc4",
"./rabbit",
"./rabbit-legacy"
], factory);
} else {
// 全局浏览器环境下将factory函数传入全局的CryptoJS如果存在执行并将结果赋值给全局的CryptoJS
root.CryptoJS = factory(root.CryptoJS);
}
}(this, function (CryptoJS) {
return CryptoJS;
}));
}, function (modId) {
var map = {
"./core": 1648886413911,
"./x64-core": 1648886413912,
"./lib-typedarrays": 1648886413913,
"./enc-utf16": 1648886413914,
"./enc-base64": 1648886413915,
"./enc-base64url": 1648886413916,
"./md5": 1648886413917,
"./sha1": 1648886413918,
"./sha256": 1648886413919,
"./sha224": 1648886413920,
"./sha512": 1648886413921,
"./sha384": 1648886413922,
"./sha3": 1648886413923,
"./ripemd160": 1648886413924,
"./hmac": 1648886413925,
"./pbkdf2": 1648886413926,
"./evpkdf": 1648886413927,
"./cipher-core": 1648886413928,
"./mode-cfb": 1648886413929,
"./mode-ctr": 1648886413930,
"./mode-ctr-gladman": 1648886413931,
"./mode-ofb": 1648886413932,
"./mode-ecb": 1648886413933,
"./pad-ansix923": 1648886413934,
"./pad-iso10126": 1648886413935,
"./pad-iso97971": 1648886413936,
"./pad-zeropadding": 1648886413937,
"./pad-nopadding": 1648886413938,
"./format-hex": 1648886413939,
"./aes": 1648886413940,
"./tripledes": 1648886413941,
"./rc4": 1648886413942,
"./rabbit": 1648886413943,
"./rabbit-legacy": 1648886413944
};
return __REQUIRE__(map[modId], modId);
});
// 定义模块ID为1648886413911的模块主要处理CryptoJS核心部分在不同环境下的导出逻辑
__DEFINE__(1648886413911, function (require, module, exports) {
// 立即执行函数根据不同的模块环境CommonJS、AMD、全局浏览器环境来设置CryptoJS核心部分的导出方式
(function (root, factory) {
if (typeof exports === "object") {
// CommonJS环境下将factory函数执行的结果赋值给module.exports和exports
module.exports = exports = factory();
} else if (typeof define === "function" && define.amd) {
// AMD环境下使用define函数定义模块传入空的依赖数组和factory函数
define([], factory);
} else {
// 全局浏览器环境下将factory函数传入全局的CryptoJS如果存在执行并将结果赋值给全局的CryptoJS
root.CryptoJS = factory();
}
}(this, function () {
/*globals window, global, require*/
/**
* CryptoJS核心组件相关代码
*/
var CryptoJS = CryptoJS || (function (Math, undefined) {
var crypto;
// 尝试从浏览器的window对象获取crypto属性作为加密相关的原生对象适用于普通浏览器环境
if (typeof window!== 'undefined' && window.crypto) {
crypto = window.crypto;
}
// 尝试从浏览器的web worker的self对象获取crypto属性作为加密相关的原生对象适用于web worker环境
if (typeof self!== 'undefined' && self.crypto) {
crypto = self.crypto;
}
// 尝试从全局的globalThis对象获取crypto属性作为加密相关的原生对象适用于新的全局环境规范
if (typeof globalThis!== 'undefined' && globalThis.crypto) {
crypto = globalThis.crypto;
}
// 尝试从浏览器的window对象获取msCrypto属性作为加密相关的原生对象适用于IE 11等旧浏览器的实验性支持
if (!crypto && typeof window!== 'undefined' && window.msCrypto) {
crypto = window.msCrypto;
}
// 尝试从NodeJS的global对象获取crypto属性作为加密相关的原生对象适用于NodeJS环境
if (!crypto && typeof global!== 'undefined' && global.crypto) {
crypto = global.crypto;
}
// 尝试通过NodeJS的require函数导入'crypto'模块作为加密相关的原生对象适用于NodeJS环境
if (!crypto && typeof require === 'function') {
try {
crypto = require('crypto');
} catch (err) {}
}
return CryptoJS;
/*
* 生成密码学安全的伪随机数的函数
* 由于Math.random()在密码学上不安全所以需要使用原生加密模块提供的方法来生成
*/
var cryptoSecureRandomInt = function () {
if (crypto) {
// 如果存在crypto对象且有getRandomValues方法适用于浏览器环境尝试使用该方法生成随机数
if (typeof crypto.getRandomValues === 'function') {
try {
return crypto.getRandomValues(new Uint32Array(1))[0];
} catch (err) {}
}
// 如果存在crypto对象且有randomBytes方法适用于NodeJS环境尝试使用该方法生成随机数
if (typeof crypto.randomBytes === 'function') {
try {
return crypto.randomBytes(4).readInt32LE();
} catch (err) {}
}
}
// 如果无法通过上述方式获取安全的随机数,则抛出错误
throw new Error('Native crypto module could not be used to get secure random number.');
};
// 本地对Object.create的 polyfill实现如果原生不存在Object.create方法则使用以下函数替代
var create = Object.create || (function () {
function F() {}
return function (obj) {
var subtype;
F.prototype = obj;
subtype = new F();
F.prototype = null;
return subtype;
};
}());
/**
* CryptoJS的命名空间对象
*/
var C = {};
/**
* 库的命名空间对象用于存放库相关的属性和方法等
*/
var C_lib = C.lib = {};
/**
* 作为原型继承的基础对象提供了一系列用于创建扩展初始化混合属性以及克隆对象等方法
*/
var Base = C_lib.Base = (function () {
return {
/**
* 创建一个继承自当前对象的新对象并可传入要覆盖或添加的属性对象
* @param {Object} overrides 要复制到新对象的属性对象
* @return {Object} 新创建的继承了当前对象的对象
* @example
* var MyType = CryptoJS.lib.Base.extend({
* field: 'value',
* method: function () {}
* });
*/
extend: function (overrides) {
// 创建一个继承自当前对象的新对象通过Object.create或者polyfill的方式
var subtype = create(this);
// 如果有要覆盖或添加的属性,将其混入新对象
if (overrides) {
subtype.mixIn(overrides);
}
// 如果新对象没有自定义的init方法或者其init方法与父对象的init方法相同则创建一个默认的init方法用于调用父对象的init方法
if (!subtype.hasOwnProperty('init') || this.init === subtype.init) {
subtype.init = function () {
subtype.$super.init.apply(this, arguments);
};
}
// 设置新对象的init方法的原型为新对象本身以便在init方法中可以正确访问到新对象的属性等
subtype.init.prototype = subtype;
// 保存对父对象的引用,方便在新对象中调用父对象的方法等
subtype.$super = this;
return subtype;
},
/**
* 扩展当前对象并执行init方法传入的参数会传递给init方法返回新创建的对象
* @return {Object} 新创建并初始化后的对象
* @example
* var instance = MyType.create();
*/
create: function () {
var instance = this.extend();
instance.init.apply(instance, arguments);
return instance;
},
/**
* 初始化新创建的对象可在子类中重写此方法来添加对象创建时的逻辑
* @example
* var MyType = CryptoJS.lib.Base.extend({
* init: function () {
* //...
* }
* });
*/
init: function () { },
/**
* 将传入的属性对象的属性复制到当前对象中
* @param {Object} properties 要混入的属性对象
* @example
* MyType.mixIn({
* field: 'value'
* });
*/
mixIn: function (properties) {
for (var propertyName in properties) {
if (properties.hasOwnProperty(propertyName)) {
this[propertyName] = properties[propertyName];
}
}
}));
}, function(modId) {var map = {"./core":1648886413911,"./x64-core":1648886413912,"./lib-typedarrays":1648886413913,"./enc-utf16":1648886413914,"./enc-base64":1648886413915,"./enc-base64url":1648886413916,"./md5":1648886413917,"./sha1":1648886413918,"./sha256":1648886413919,"./sha224":1648886413920,"./sha512":1648886413921,"./sha384":1648886413922,"./sha3":1648886413923,"./ripemd160":1648886413924,"./hmac":1648886413925,"./pbkdf2":1648886413926,"./evpkdf":1648886413927,"./cipher-core":1648886413928,"./mode-cfb":1648886413929,"./mode-ctr":1648886413930,"./mode-ctr-gladman":1648886413931,"./mode-ofb":1648886413932,"./mode-ecb":1648886413933,"./pad-ansix923":1648886413934,"./pad-iso10126":1648886413935,"./pad-iso97971":1648886413936,"./pad-zeropadding":1648886413937,"./pad-nopadding":1648886413938,"./format-hex":1648886413939,"./aes":1648886413940,"./tripledes":1648886413941,"./rc4":1648886413942,"./rabbit":1648886413943,"./rabbit-legacy":1648886413944}; return __REQUIRE__(map[modId], modId); })
__DEFINE__(1648886413911, function(require, module, exports) {
;(function (root, factory) {
if (typeof exports === "object") {
// CommonJS
module.exports = exports = factory();
}
else if (typeof define === "function" && define.amd) {
// AMD
define([], factory);
}
else {
// Global (browser)
root.CryptoJS = factory();
}
}(this, function () {
/*globals window, global, require*/
/**
* CryptoJS core components.
*/
var CryptoJS = CryptoJS || (function (Math, undefined) {
var crypto;
// Native crypto from window (Browser)
if (typeof window !== 'undefined' && window.crypto) {
crypto = window.crypto;
}
// Native crypto in web worker (Browser)
if (typeof self !== 'undefined' && self.crypto) {
crypto = self.crypto;
}
// Native crypto from worker
if (typeof globalThis !== 'undefined' && globalThis.crypto) {
crypto = globalThis.crypto;
}
// Native (experimental IE 11) crypto from window (Browser)
if (!crypto && typeof window !== 'undefined' && window.msCrypto) {
crypto = window.msCrypto;
}
// Native crypto from global (NodeJS)
if (!crypto && typeof global !== 'undefined' && global.crypto) {
crypto = global.crypto;
}
// Native crypto import via require (NodeJS)
if (!crypto && typeof require === 'function') {
try {
crypto = require('crypto');
} catch (err) {}
}
/*
* Cryptographically secure pseudorandom number generator
*
* As Math.random() is cryptographically not safe to use
*/
var cryptoSecureRandomInt = function () {
if (crypto) {
// Use getRandomValues method (Browser)
if (typeof crypto.getRandomValues === 'function') {
try {
return crypto.getRandomValues(new Uint32Array(1))[0];
} catch (err) {}
}
// Use randomBytes method (NodeJS)
if (typeof crypto.randomBytes === 'function') {
try {
return crypto.randomBytes(4).readInt32LE();
} catch (err) {}
}
}
throw new Error('Native crypto module could not be used to get secure random number.');
};
/*
* Local polyfill of Object.create
*/
var create = Object.create || (function () {
function F() {}
return function (obj) {
var subtype;
F.prototype = obj;
subtype = new F();
F.prototype = null;
return subtype;
};
}());
/**
* CryptoJS namespace.
*/
var C = {};
/**
* Library namespace.
*/
var C_lib = C.lib = {};
/**
* Base object for prototypal inheritance.
*/
var Base = C_lib.Base = (function () {
return {
/**
* Creates a new object that inherits from this object.
*
* @param {Object} overrides Properties to copy into the new object.
*
* @return {Object} The new object.
*
* @static
*
* @example
*
* var MyType = CryptoJS.lib.Base.extend({
* field: 'value',
*
* method: function () {
* }
* });
*/
extend: function (overrides) {
// Spawn
var subtype = create(this);
// Augment
if (overrides) {
subtype.mixIn(overrides);
}
// Create default initializer
if (!subtype.hasOwnProperty('init') || this.init === subtype.init) {
subtype.init = function () {
subtype.$super.init.apply(this, arguments);
};
}
// Initializer's prototype is the subtype object
subtype.init.prototype = subtype;
// Reference supertype
subtype.$super = this;
return subtype;
},
/**
* Extends this object and runs the init method.
* Arguments to create() will be passed to init().
*
* @return {Object} The new object.
*
* @static
*
* @example
*
* var instance = MyType.create();
*/
create: function () {
var instance = this.extend();
instance.init.apply(instance, arguments);
return instance;
},
/**
* Initializes a newly created object.
* Override this method to add some logic when your objects are created.
*
* @example
*
* var MyType = CryptoJS.lib.Base.extend({
* init: function () {
* // ...
* }
* });
*/
init: function () {
},
/**
* Copies properties into this object.
*
* @param {Object} properties The properties to mix in.
*
* @example
*
* MyType.mixIn({
* field: 'value'
* });
*/
mixIn: function (properties) {
for (var propertyName in properties) {
if (properties.hasOwnProperty(propertyName)) {
this[propertyName] = properties[propertyName];
}
}
// IE won't copy toString using the loop above
if (properties.hasOwnProperty('toString')) {

@ -1,63 +1,39 @@
// 这个函数用于将给定的日期对象格式化为特定的字符串格式,格式为 "年/月/日 时:分:秒"
const formatTime = date => {
// 获取日期对象中的年份信息
const year = date.getFullYear();
// 获取日期对象中的月份信息需要注意的是JavaScript中月份是从0开始计数的所以这里要加1得到实际的月份值
const month = date.getMonth() + 1;
// 获取日期对象中的日信息
const day = date.getDate();
// 获取日期对象中的小时信息
const hour = date.getHours();
// 获取日期对象中的分钟信息
const minute = date.getMinutes();
// 获取日期对象中的秒信息
const second = date.getSeconds();
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
// 先将年、月、日组成的数组中的每个元素通过formatNumber函数进行格式化处理然后使用"/"将它们连接起来;
// 再将小时、分钟、秒组成的数组中的每个元素通过formatNumber函数进行格式化处理然后使用":"将它们连接起来;
// 最后将这两部分用空格连接起来,形成最终的格式化后的时间字符串并返回
return [year, month, day].map(formatNumber).join('/') +'' + [hour, minute, second].map(formatNumber).join(':');
return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}
// 这个函数用于将数字格式化为固定长度的字符串格式,如果数字是个位数,则在前面添加"0"
const formatNumber = n => {
// 将传入的参数转换为字符串类型
n = n.toString();
// 如果字符串长度大于1说明不是个位数直接返回原字符串否则在字符串前面添加"0"后返回
return n[1]? n : '0' + n;
n = n.toString()
return n[1] ? n : '0' + n
}
// 这个函数用于对HTML内容字符串进行样式相关的格式化处理主要是调整图片和表格单元格的样式
const formatHtml = content => {
// 查找HTML内容中所有的<img>标签并给它们添加特定的内联样式设置宽度为100%、高度自适应、外边距为0以及以flex布局显示
content = content.replace(/\<img/gi, '<img style="width:100%!important;height:auto!important;margin:0;display:flex;" ');
// 查找HTML内容中所有的<td>标签,并给它们添加特定的内联样式,设置单元格间距、内边距、边框等样式属性,使其更符合特定的布局需求
content = content.replace(/\<img/gi, '<img style="width:100% !important;height:auto !important;margin:0;display:flex;" ');
content = content.replace(/\<td/gi, '<td cellspacing="0" cellpadding="0" border="0" style="display:block;vertical-align:top;margin: 0px; padding: 0px; border: 0px;outline-width:0px;" ');
// 将HTML内容中所有的width=属性替换为sss=,这里可能是为了暂时去除原有的宽度设置,后续再统一处理或者有其他特殊用途
content = content.replace(/width=/gi, 'sss=');
// 将HTML内容中所有的height=属性替换为sss=作用类似于对width属性的处理可能是为了后续统一管理高度相关样式
content = content.replace(/height=/gi, 'sss=');
// 查找HTML内容中所有的结束标签 />并给对应的标签添加特定的内联样式设置最大宽度为100%、高度自适应、外边距为0以及以块级元素显示然后替换原内容中的结束标签部分
content = content.replace(/ \/\>/gi, ' style="max-width:100%!important;height:auto!important;margin:0;display:block;" \/\>');
// 返回处理后的HTML内容字符串
content = content.replace(/ \/\>/gi, ' style="max-width:100% !important;height:auto !important;margin:0;display:block;" \/\>');
return content;
}
// 这个函数的作用是移除购物车Tabbar上显示的数字可能是未读消息数量等提示信息通过调用微信小程序的API来实现
// 这里假设使用的是微信小程序的开发环境wx是小程序的全局对象removeTabBarBadge是其提供的用于移除Tabbar角标数字提示的方法
/**
* 移除购物车Tabbar的数字
*/
const removeTabBadge = () => {
wx.removeTabBarBadge({
// 指定要移除角标的Tabbar选项卡的索引这里的2表示购物车对应的选项卡索引具体索引值可能根据小程序的实际布局而定
index: 2
})
wx.removeTabBarBadge({
index: 2
})
}
// 将上述定义的几个函数作为模块的属性进行导出,方便其他模块引入并使用这些功能函数
module.exports = {
formatTime: formatTime,
formatHtml: formatHtml,
removeTabBadge: removeTabBadge
}
removeTabBadge: removeTabBadge
}

@ -1,10 +1,5 @@
// 从指定路径 '../mixins/basic' 导入名为 'basic' 的模块(这里假设是一个混入对象,用于扩展组件的功能等)
import { basic } from '../mixins/basic';
// 从 '../mixins/observer/index' 路径导入名为 'observe' 的模块(可能用于实现数据观察等相关功能)
import { observe } from '../mixins/observer/index';
// 这个函数用于将源对象source中的某些属性按照给定的映射关系map复制到目标对象target
// 具体来说遍历映射关系中的每个键key如果源对象中存在对应的属性则将该属性值复制到目标对象的对应映射键名下
function mapKeys(source, target, map) {
Object.keys(map).forEach(key => {
if (source[key]) {
@ -12,14 +7,8 @@ function mapKeys(source, target, map) {
}
});
}
// 定义名为 'VantComponent' 的函数,用于创建一个 Vant 风格的组件(可能是基于微信小程序等框架),接收一个配置对象 'vantOptions'(有默认值为空对象)
function VantComponent(vantOptions = {}) {
// 创建一个空的对象 'options',用于存储经过处理后的组件配置选项,后续会逐步往里面添加和调整各项配置
const options = {};
// 使用'mapKeys' 函数将 'vantOptions' 中的部分属性按照特定的映射规则复制到 'options' 对象中
// 例如,将 'data' 属性映射为 'properties'(可能是适配不同框架对于数据定义的要求)等,实现属性名称的转换和整理
mapKeys(vantOptions, options, {
data: 'data',
props: 'properties',
@ -32,45 +21,28 @@ function VantComponent(vantOptions = {}) {
destroyed: 'detached',
classes: 'externalClasses'
});
// 从 'vantOptions' 中获取'relation' 属性(可能用于定义组件间的关系等)
const { relation } = vantOptions;
if (relation) {
// 如果存在'relation' 属性,则将其合并到 'options.relations' 中,构建组件关系配置
// 这里将组件关系配置以特定的格式(路径和关系对象的形式)添加到'relations' 属性中,用于后续处理组件间的关联逻辑
options.relations = Object.assign(options.relations || {}, {
[`../${relation.name}/index`]: relation
});
}
// 如果 'options.externalClasses' 不存在(为假值),则初始化为一个空数组,用于存储组件外部可传入的类名相关配置
// add default externalClasses
options.externalClasses = options.externalClasses || [];
// 向 'options.externalClasses' 数组中添加一个默认的类名 'custom-class',方便外部对组件样式进行定制
options.externalClasses.push('custom-class');
// 如果 'options.behaviors' 不存在(为假值),则初始化为一个空数组,'behaviors' 通常用于添加混入的功能模块等
// add default behaviors
options.behaviors = options.behaviors || [];
// 将之前导入的 'basic' 混入对象添加到 'options.behaviors' 数组中,为组件添加基础的通用功能扩展
options.behaviors.push(basic);
// 如果 'vantOptions' 中存在 'field' 属性(可能表示组件与表单字段相关的某种标识)
// map field to form-field behavior
if (vantOptions.field) {
// 则向 'options.behaviors' 数组中添加 'wx://form-field'(可能是指向一个表单字段相关的行为模块,用于实现表单相关功能)
options.behaviors.push('wx://form-field');
}
// 为组件配置添加默认的通用选项配置
// add default options
options.options = {
multipleSlots: true, // 允许组件使用多个插槽,方便更灵活的内容布局和定制
addGlobalClass: true // 允许添加全局类名,增强样式定制的灵活性
multipleSlots: true,
addGlobalClass: true
};
// 使用导入的 'observe' 模块(函数),根据 'vantOptions' 来对 'options' 进行一些数据观察等相关的处理(具体功能由 'observe' 函数实现)
observe(vantOptions, options);
// 调用 'Component' 函数(这里假设是所在框架提供的用于注册组件的函数,比如微信小程序的组件注册机制),传入处理好的 'options' 配置对象来注册组件
Component(options);
}
// 将 'VantComponent' 函数作为模块的导出项,以便其他模块可以引入并使用该函数来创建 Vant 风格的组件
export { VantComponent };

@ -1,29 +1,14 @@
// 函数 isDef 用于判断传入的值是否为已定义(即不是 undefined 也不是 null
// 返回一个布尔值,如果值既不是 undefined 也不是 null则返回 true否则返回 false
function isDef(value) {
return value!== undefined && value!== null;
return value !== undefined && value !== null;
}
// 函数 isObj 用于判断传入的参数是否为对象类型(包括普通对象、函数等符合 JavaScript 中对象定义的情况,但要排除 null
// 首先获取参数的类型(通过 typeof 操作符),然后判断参数不为 null 且类型是 'object' 或者 'function' 时,返回 true否则返回 false
function isObj(x) {
const type = typeof x;
return x!== null && (type === 'object' || type === 'function');
return x !== null && (type === 'object' || type === 'function');
}
// 函数 isNumber 用于简单判断传入的字符串是否表示一个纯数字(只包含数字字符)
// 通过正则表达式 /^\d+$/ 来测试传入的字符串,如果匹配成功,表示字符串只由数字组成,返回 true否则返回 false
// 注意:该函数对于如 "12.3" 这样的浮点数字符串会判断为 false有一定的局限性仅适用于判断正整数形式的字符串
function isNumber(value) {
return /^\d+$/.test(value);
}
// 函数 range 用于将给定的数字 num 限定在指定的最小值 min 和最大值 max 范围内
// 先通过 Math.max 取 num 和 min 中的较大值,确保返回值不会小于 min再通过 Math.min 取上述较大值与 max 中的较小值,确保返回值不会大于 max
// 最终返回限定在 [min, max] 范围内的值
function range(num, min, max) {
return Math.min(Math.max(num, min), max);
}
// 将 isObj、isDef、isNumber、range 这四个函数作为模块的导出项,以便其他模块可以引入并使用这些函数
export { isObj, isDef, isNumber, range };
export { isObj, isDef, isNumber, range };

@ -1,33 +1,19 @@
// 从相对路径 '../common/component' 导入名为 'VantComponent' 的模块,这里假设 'VantComponent' 是一个用于创建特定组件的函数(可能遵循某种组件化规范)
import { VantComponent } from '../common/component';
// 调用 'VantComponent' 函数来创建一个组件,传入一个配置对象,用于定义组件的各种属性、方法等相关信息
VantComponent({
// 'props' 属性用于定义组件可接收的外部属性(类似 React 中的 props 概念),是一个对象,对象的每个键值对表示一个属性的定义
props: {
// 'info' 属性,初始值设置为 null类型未明确限定可接收各种类型的值传入
info: null,
// 'name' 属性限定接收的值类型为字符串类型String
name: String,
//'size' 属性限定接收的值类型为字符串类型String
size: String,
// 'color' 属性限定接收的值类型为字符串类型String
color: String,
// 'customStyle' 属性限定接收的值类型为字符串类型String
customStyle: String,
// 'classPrefix' 属性详细定义了其属性类型为字符串类型type: String并且设置了默认值为 'van-icon'
classPrefix: {
type: String,
value: 'van-icon'
}
},
//'methods' 属性用于定义组件内部的方法,是一个对象,对象的每个键对应一个方法名,值为对应的方法函数
methods: {
// 定义名为 'onClick' 的方法,当组件被点击时可能会触发该方法
onClick() {
// 通过 '$emit' 方法(这里假设是基于某种组件框架提供的事件触发机制,比如 Vue 中的自定义事件触发方式)向外触发一个名为 'click' 的自定义事件
// 这样外部使用该组件的地方可以监听这个 'click' 事件并执行相应的逻辑
this.$emit('click');
}
}
});
});

@ -1,13 +1,7 @@
// 从相对路径 '../common/component' 导入名为 'VantComponent' 的模块。通常情况下,'VantComponent' 应该是一个用于创建特定组件的函数,遵循着特定的组件构建规则或框架规范
import { VantComponent } from '../common/component';
// 调用 'VantComponent' 函数来创建一个组件,向其传入一个配置对象,该配置对象用于定义组件的相关属性、行为等信息。
VantComponent({
// 'props' 是配置对象中的一个属性,用于定义组件可接收的外部属性(类似于其他框架中的 props 概念),这里它是一个对象,对象内的每个键值对代表一个具体的外部属性定义。
props: {
// 'info' 属性的定义,其初始值被设置为 null这意味着它可以接收任意类型的值外部使用组件时可以传入各种类型的数据给这个属性且若不传值则默认为 null。
info: null,
// 'customStyle' 属性的定义指定其接收的值类型为字符串类型String意味着外部向组件传入该属性时必须传入符合字符串格式的数据用于可能的样式定制等功能。
customStyle: String
}
});
});

@ -1,35 +1,22 @@
// 使用 `export` 关键字将名为 `basic` 的变量作为模块的导出项,以便其他模块可以引入并使用它。
// `basic` 被赋值为通过 `Behavior` 函数创建的一个行为对象(在微信小程序等框架中,行为可以被组件引入来复用其中的属性、方法等功能)。
export const basic = Behavior({
// `methods` 属性用于定义在该行为对象中包含的一系列方法,这些方法可以被使用了这个行为的组件所调用。
methods: {
// 定义名为 `$emit` 的方法,其功能是调用 `this.triggerEvent` 方法,并将接收到的所有参数原封不动地传递给 `triggerEvent` 方法。
// 这里的 `$emit` 可能是仿照一些框架(如 Vue 等)中触发自定义事件的方式来命名,用于在组件内部触发自定义事件,方便组件间通信等操作。
$emit() {
this.triggerEvent.apply(this, arguments);
},
// 定义名为 `getRect` 的方法,该方法用于获取页面中指定选择器对应的元素的布局信息(边界矩形信息等),返回一个 `Promise` 对象。
getRect(selector, all) {
return new Promise(resolve => {
// 使用微信小程序提供的 `wx.createSelectorQuery` 方法创建一个选择器查询对象,用于在页面中查找元素。
wx.createSelectorQuery()
// 通过 `.in(this)` 将选择器查询的作用范围限定在当前组件实例内(如果有组件层级等情况时很有用)。
// 根据 `all` 参数的值来决定调用 `selectAll` 还是 `select` 方法,`select` 用于获取单个元素,`selectAll` 用于获取所有匹配选择器的元素。
.in(this)[all?'selectAll' :'select'](selector)
// 调用 `boundingClientRect` 方法来获取元素的边界矩形信息(如位置、大小等),传入一个回调函数,在获取到信息后进行处理。
.in(this)[all ? 'selectAll' : 'select'](selector)
.boundingClientRect(rect => {
// 如果 `all` 参数为 `true`,并且获取到的 `rect` 是数组且长度大于 0说明获取到了多个元素的矩形信息此时通过 `resolve` 方法将 `rect` 数组传递出去,用于后续处理(在 `Promise` 中表示成功获取到了期望的数据)。
if (all && Array.isArray(rect) && rect.length) {
resolve(rect);
}
// 如果 `all` 参数为 `false`,并且获取到了单个元素的 `rect` 信息(即 `rect` 不为空),同样通过 `resolve` 方法将 `rect` 传递出去,表示成功获取到了单个元素的矩形信息。
if (!all && rect) {
resolve(rect);
}
})
// 最后调用 `exec` 方法执行整个查询操作,触发实际的查找和获取元素信息的流程。
if (all && Array.isArray(rect) && rect.length) {
resolve(rect);
}
if (!all && rect) {
resolve(rect);
}
})
.exec();
});
}
}
});
});

@ -1,34 +1,18 @@
// 使用 `export` 关键字将名为 `button` 的变量作为模块的导出项,这样其他模块就能引入并使用它。
// `button` 被赋值为通过 `Behavior` 函数创建的一个行为对象(在微信小程序等框架里,行为对象可以被组件引入,以复用其中定义的各类属性、方法以及外部类名等内容)。
export const button = Behavior({
// `externalClasses` 属性用于定义该行为对象外部可传入的类名列表,这里定义了一个名为 `hover-class` 的外部类名。
// 外部类名意味着使用了这个行为的组件,在外部使用时可以通过这个类名来自定义相应的样式,增强组件样式的可定制性。
externalClasses: ['hover-class'],
// `properties` 属性用于定义该行为对象包含的一系列属性,这些属性可以被使用了这个行为的组件所接收和使用,相当于组件对外暴露的可配置属性。
properties: {
// `id` 属性,其值被限定为字符串类型(`String`),外部使用组件时传入的 `id` 值需要符合字符串的格式要求,可用于唯一标识组件等用途。
id: String,
// `lang` 属性,详细定义了它的类型为字符串类型(`type: String`),并且设置了默认值为 `'en'`。意味着如果外部没有传入该属性值,组件内部会默认使用 `'en'` 这个值,常用于设置语言相关的配置。
lang: {
type: String,
value: 'en'
},
// `businessId` 属性,限定接收的值类型为数字类型(`Number`),用于接收和处理与业务相关的数字标识等信息。
businessId: Number,
// `sessionFrom` 属性,其值限定为字符串类型(`String`),可用于表示会话来源等相关信息,外部使用组件时需要传入符合字符串格式的数据。
sessionFrom: String,
// `sendMessageTitle` 属性,限定为字符串类型(`String`),可能用于设置发送消息时的标题内容等,外部传入对应字符串来配置此功能相关信息。
sendMessageTitle: String,
// `sendMessagePath` 属性,同样限定为字符串类型(`String`),也许用于指定发送消息的路径相关信息,由外部传入合适的字符串值进行配置。
sendMessagePath: String,
// `sendMessageImg` 属性,也是字符串类型(`String`),大概用于配置发送消息时所附带的图片相关信息,外部传入对应字符串进行定制。
sendMessageImg: String,
// `showMessageCard` 属性,其类型被定义为布尔类型(`Boolean`),用于控制是否显示消息卡片等相关功能的显示与否,外部传入 `true` 或 `false` 来进行相应设置。
showMessageCard: Boolean,
// `appParameter` 属性,限定为字符串类型(`String`),可用于传递应用相关的参数信息,外部传入对应字符串来配置相应功能。
appParameter: String,
// `ariaLabel` 属性,同样是字符串类型(`String`),在无障碍访问相关的场景下,用于设置元素的可访问性标签等信息,方便屏幕阅读器等辅助工具使用。
ariaLabel: String
}
});
});

@ -1,30 +1,17 @@
// 使用 `export` 关键字将名为 `link` 的变量作为模块的导出项,这样其他模块就可以引入并使用它。
// `link` 在这里被赋值为通过 `Behavior` 函数创建的一个行为对象,在微信小程序等框架中,行为对象能够被组件引入,以此复用其中定义的属性、方法等内容,来扩展组件的功能。
export const link = Behavior({
// `properties` 属性用于定义该行为对象所包含的组件可接收的外部属性,相当于对外暴露的配置项,外部使用组件时可以传入对应的值来配置组件的行为和状态。
properties: {
// `url` 属性,其值被限定为字符串类型(`String`),外部使用组件时需要传入符合字符串格式的数据,通常用于指定链接的地址,比如页面跳转的目标地址等。
url: String,
// `linkType` 属性,详细定义了它的类型为字符串类型(`type: String`),并且设置了默认值为 `'navigateTo'`。
// 这个属性大概率用于指定链接跳转的方式,默认是 `'navigateTo'`,表示以小程序中对应的页面跳转方式(类似 `wx.navigateTo`)进行跳转,外部也可以传入其他符合要求的跳转方式字符串来改变默认行为。
linkType: {
type: String,
value: 'navigateTo'
}
},
// `methods` 属性用于定义该行为对象中包含的方法,这些方法可以被使用了这个行为的组件所调用,用于实现特定的功能逻辑。
methods: {
// 定义名为 `jumpLink` 的方法,该方法用于触发链接跳转的操作,接收一个可选参数 `urlKey`,默认值为 `'url'`。
jumpLink(urlKey = 'url') {
// 从组件实例的数据(`this.data`)中获取由 `urlKey` 指定的属性值,并赋值给变量 `url`。
// 通常情况下,如果 `urlKey` 使用默认值 `'url'`,就是获取之前定义的 `url` 属性所对应的值,也就是目标跳转地址。
const url = this.data[urlKey];
// 判断获取到的 `url` 是否存在(不为空字符串、`null`、`undefined` 等假值情况),如果存在,则执行页面跳转操作。
if (url) {
// 通过 `wx` 对象(微信小程序提供的全局 API 对象),使用 `this.data.linkType` 指定的跳转方式来进行页面跳转,将获取到的 `url` 作为参数传递给对应的跳转方法。
// 例如,如果 `linkType` 的值为 `'navigateTo'`,就相当于执行 `wx.navigateTo({ url })`,实现跳转到指定的页面地址。
wx[this.data.linkType]({ url });
}
}
}
});
});

@ -1,37 +1,25 @@
// 使用 `export` 关键字将名为 `openType` 的变量作为模块的导出项,使得其他模块能够引入并使用它。
// `openType` 被赋值为通过 `Behavior` 函数创建的一个行为对象,在微信小程序的开发框架中,行为对象可供组件引入,以复用其中定义好的属性、方法等内容,从而扩展组件的功能。
export const openType = Behavior({
// `properties` 属性用于定义该行为对象所关联组件能够接收的外部属性,也就是组件对外暴露的可配置项,外部在使用组件时可以传入相应的值来设定组件的相关特性。
properties: {
// `openType` 属性,其值被限定为字符串类型(`String`),外部使用组件时需传入符合字符串格式的数据,该属性通常用于指定某种开放类型相关的设置,具体含义会依据微信小程序不同的开放能力场景而定。
openType: String
},
// `methods` 属性用于定义行为对象中包含的一系列方法,这些方法可以被引入该行为的组件所调用,用于实现特定的业务逻辑以及与外部的交互操作。
methods: {
// 定义名为 `bindGetUserInfo` 的方法,它接收一个 `event` 参数,该参数通常包含了微信小程序触发对应事件时传递过来的相关信息。
// 此方法的作用是,当触发获取用户信息的相关事件时,通过 `this.$emit` 向外(可能是组件的父组件等监听者)触发一个名为 `getuserinfo` 的自定义事件,并将 `event.detail`(即包含用户信息详情的数据)作为参数传递出去,方便外部进行相应的处理,比如获取到用户信息后进行展示或者存储等操作。
bindGetUserInfo(event) {
this.$emit('getuserinfo', event.detail);
},
// 定义名为 `bindContact` 的方法,同样接收 `event` 参数,当触发与联系人相关的事件时,通过 `this.$emit` 向外触发名为 `contact` 的自定义事件,并传递 `event.detail`(包含联系人相关详细信息的数据),使得外部可以处理联系人相关的业务逻辑,例如更新联系人列表等操作。
bindContact(event) {
this.$emit('contact', event.detail);
},
// 定义名为 `bindGetPhoneNumber` 的方法,接收 `event` 参数,在触发获取手机号码相关事件时,使用 `this.$emit` 向外触发 `getphonenumber` 自定义事件,同时将 `event.detail`(包含手机号码相关信息的数据)传递出去,便于外部获取并使用该手机号码信息,比如进行验证或者存储等处理。
bindGetPhoneNumber(event) {
this.$emit('getphonenumber', event.detail);
},
// 定义名为 `bindError` 的方法,接收 `event` 参数,当出现错误相关的事件触发时,通过 `this.$emit` 向外触发 `error` 自定义事件,将 `event.detail`(包含错误详细信息的数据)传递出去,方便外部根据错误信息进行相应的错误处理,例如提示用户或者记录日志等操作。
bindError(event) {
this.$emit('error', event.detail);
},
// 定义名为 `bindLaunchApp` 的方法,接收 `event` 参数,在触发启动应用相关的事件时,利用 `this.$emit` 向外触发 `launchapp` 自定义事件,把 `event.detail`(包含启动应用相关详细信息的数据)传递出去,使得外部可以根据启动情况做后续处理,比如判断启动是否成功等操作。
bindLaunchApp(event) {
this.$emit('launchapp', event.detail);
},
// 定义名为 `bindOpenSetting` 的方法,接收 `event` 参数,当触发打开设置相关的事件时,通过 `this.$emit` 向外触发 `opensetting` 自定义事件,同时传递 `event.detail`(包含打开设置相关详细信息的数据),方便外部知晓设置页面的打开情况以及进行相应的后续操作,例如判断用户是否进行了相关设置调整等操作。
bindOpenSetting(event) {
this.$emit('opensetting', event.detail);
}
},
}
});
});

@ -1,57 +1,39 @@
// 定义一个全局变量 `cache`,用于缓存获取到的安全区域相关信息,初始值设置为 `null`。
let cache = null;
// 定义 `getSafeArea` 函数,该函数用于获取设备的安全区域相关信息,返回一个 `Promise` 对象,方便进行异步操作的处理。
function getSafeArea() {
return new Promise((resolve, reject) => {
// 首先检查 `cache` 是否已经有值(不为 `null`),如果已经缓存了安全区域信息,则直接通过 `resolve` 将缓存的信息传递出去,避免重复获取。
if (cache!= null) {
if (cache != null) {
resolve(cache);
}
// 如果 `cache` 为 `null`,说明还没有获取过安全区域信息,需要调用微信小程序的 `wx.getSystemInfo` 方法来获取系统相关信息。
else {
wx.getSystemInfo({
// `success` 回调函数会在成功获取到系统信息后被调用,它接收一个包含系统信息的对象作为参数,解构出其中需要的属性(`model` 表示设备型号,`screenHeight` 表示屏幕高度,`statusBarHeight` 表示状态栏高度)。
success: ({ model, screenHeight, statusBarHeight }) => {
// 通过正则表达式判断设备型号是否匹配 `iphone x`(不区分大小写),用于识别是否为 iPhone X 系列设备,将结果赋值给 `iphoneX` 变量。
const iphoneX = /iphone x/i.test(model);
// 判断设备型号是否匹配 `iPhone11`(不区分大小写)并且屏幕高度是否为 `812`,用于识别特定的 iPhone 11 相关机型,将结果赋值给 `iphoneNew` 变量。
const iphoneNew = /iPhone11/i.test(model) && screenHeight === 812;
// 将安全区域相关信息缓存到 `cache` 变量中,包括是否为 iPhone X 系列或特定 iPhone 11 机型(通过 `iphoneX` 或 `iphoneNew` 判断)以及状态栏高度信息。
cache = {
isIPhoneX: iphoneX || iphoneNew,
statusBarHeight
};
// 通过 `resolve` 将缓存好的安全区域信息传递出去,用于后续的处理。
resolve(cache);
},
// 如果获取系统信息失败,调用 `reject`,将错误信息传递出去,由调用 `getSafeArea` 函数的地方进行相应的错误处理。
fail: reject
});
}
});
}
// 使用 `export` 关键字导出一个名为 `safeArea` 的函数,该函数返回一个通过 `Behavior` 创建的行为对象,用于在微信小程序组件中实现与安全区域相关的功能和属性配置。
export const safeArea = ({ safeAreaInsetBottom = true, safeAreaInsetTop = false } = {}) => Behavior({
// `properties` 属性用于定义行为对象关联的组件可接收的外部属性,这里定义了两个与安全区域插入相关的布尔类型属性。
properties: {
// `safeAreaInsetTop` 属性,其类型为布尔类型(`Boolean`),默认值由传入 `safeArea` 函数的参数 `safeAreaInsetTop` 决定(如果未传则默认为 `false`),用于控制是否在顶部插入安全区域相关的样式或布局调整等。
safeAreaInsetTop: {
type: Boolean,
value: safeAreaInsetTop
},
// `safeAreaInsetBottom` 属性,同样是布尔类型(`Boolean`),默认值由传入 `safeArea` 函数的参数 `safeAreaInsetBottom` 决定(如果未传则默认为 `true`),用于控制是否在底部插入安全区域相关的样式或布局调整等。
safeAreaInsetBottom: {
type: Boolean,
value: safeAreaInsetBottom
}
},
// `created` 生命周期钩子函数,在组件被创建时会被调用,这里用于获取安全区域信息并设置到组件的 `data` 中,以便组件后续使用这些信息进行相应的布局或样式处理。
created() {
// 调用 `getSafeArea` 函数获取安全区域信息,当获取成功后(`Promise` 状态变为 `resolved`),解构出 `isIPhoneX`(是否为 iPhone X 系列等相关机型)和 `statusBarHeight`(状态栏高度)信息,并通过 `this.set`(这里假设是微信小程序组件中用于设置数据的方法)将这些信息设置到组件的数据中。
getSafeArea().then(({ isIPhoneX, statusBarHeight }) => {
this.set({ isIPhoneX, statusBarHeight });
});
}
});
});

@ -1,50 +1,27 @@
// 使用 `export` 关键字将名为 `touch` 的变量作为模块的导出项,这样其他模块就能引入并使用它。
// `touch` 被赋值为通过 `Behavior` 函数创建的一个行为对象,在微信小程序等框架里,行为对象可被组件引入,以复用其中定义的方法等内容,进而实现相应的触摸交互相关功能。
export const touch = Behavior({
// `methods` 属性用于定义该行为对象中包含的一系列方法,这些方法可被使用了这个行为的组件所调用,用于处理触摸相关的事件逻辑。
methods: {
// 定义名为 `touchStart` 的方法,它接收一个 `event` 参数,该参数包含了触摸开始事件触发时微信小程序传递过来的相关信息。
// 这个方法通常在触摸操作刚开始时被调用,用于初始化一些与触摸操作相关的变量,以便后续在触摸移动等过程中进行计算和判断。
touchStart(event) {
// 从 `event.touches` 数组中获取第一个触摸点的信息(一般在单指触摸情况下就是当前触摸点),并赋值给 `touch` 变量,方便后续获取触摸点的坐标等属性。
const touch = event.touches[0];
// 初始化 `direction` 属性为空字符串,表示触摸滑动方向尚未确定,后续会根据触摸移动情况来更新这个值,用于判断是水平滑动还是垂直滑动等情况。
this.direction = '';
// 将 `deltaX`(触摸点在 X 轴方向的偏移量)初始化为 0后续在触摸移动过程中会根据触摸点位置变化来更新该值。
this.deltaX = 0;
// 将 `deltaY`(触摸点在 Y 轴方向的偏移量)初始化为 0同样会在触摸移动时根据实际情况进行更新。
this.deltaY = 0;
// 将 `offsetX`(触摸点在 X 轴方向相对起始位置的偏移绝对值)初始化为 0用于记录触摸移动过程中的偏移情况方便后续判断滑动方向等操作。
this.offsetX = 0;
// 将 `offsetY`(触摸点在 Y 轴方向相对起始位置的偏移绝对值)初始化为 0作用与 `offsetX` 类似,用于触摸移动相关的逻辑处理。
this.offsetY = 0;
// 获取触摸开始时触摸点的 X 轴坐标(相对于浏览器客户端窗口左上角的位置),并赋值给 `startX` 属性,用于后续计算触摸点在 X 轴方向的偏移量等操作。
this.startX = touch.clientX;
// 获取触摸开始时触摸点的 Y 轴坐标(相对于浏览器客户端窗口左上角的位置),并赋值给 `startY` 属性,用于后续计算触摸点在 Y 轴方向的偏移量等操作。
this.startY = touch.clientY;
},
// 定义名为 `touchMove` 的方法,同样接收 `event` 参数,该方法会在触摸点在屏幕上移动时被触发,用于实时更新与触摸移动相关的变量,以判断触摸滑动的方向等信息。
touchMove(event) {
// 从 `event.touches` 数组中获取当前第一个触摸点的信息(单指触摸时的当前触摸点),赋值给 `touch` 变量,以便获取当前触摸点的坐标。
const touch = event.touches[0];
// 计算触摸点在 X 轴方向相对于触摸开始时的偏移量,通过当前触摸点的 X 轴坐标减去触摸开始时的 `startX` 值来得到,并更新 `deltaX` 属性。
this.deltaX = touch.clientX - this.startX;
// 计算触摸点在 Y 轴方向相对于触摸开始时的偏移量,用当前触摸点的 Y 轴坐标减去触摸开始时的 `startY` 值,然后更新 `deltaY` 属性。
this.deltaY = touch.clientY - this.startY;
// 计算触摸点在 X 轴方向相对于起始位置的偏移绝对值,使用 `Math.abs` 函数获取 `deltaX` 的绝对值,并赋值给 `offsetX` 属性,这有助于后续比较 X 轴和 Y 轴方向的偏移情况来判断滑动方向。
this.offsetX = Math.abs(this.deltaX);
// 计算触摸点在 Y 轴方向相对于起始位置的偏移绝对值,通过 `Math.abs` 函数获取 `deltaY` 的绝对值,将结果赋值给 `offsetY` 属性,同样用于滑动方向的判断等逻辑。
this.offsetY = Math.abs(this.deltaY);
// 根据 `offsetX` 和 `offsetY` 的大小关系来确定触摸滑动的方向,将结果赋值给 `direction` 属性。
// 如果 `offsetX`X 轴方向偏移绝对值)大于 `offsetY`Y 轴方向偏移绝对值),说明触摸滑动更偏向水平方向,将 `direction` 设置为 `'horizontal'`
// 如果 `offsetX` 小于 `offsetY`,表明触摸滑动更偏向垂直方向,把 `direction` 设置为 `'vertical'`
// 如果 `offsetX` 和 `offsetY` 相等,意味着滑动方向不太明确或者没有明显的水平或垂直偏向,将 `direction` 设置为空字符串 `''`。
this.direction =
this.offsetX > this.offsetY
? 'horizontal'
: this.offsetX < this.offsetY
? 'vertical'
: '';
? 'vertical'
: '';
}
}
});
});

@ -1,126 +1,87 @@
// 从相对路径 '../common/utils' 导入名为 'isObj' 的函数,该函数可能用于判断传入的值是否为对象类型,以便后续在代码中进行相关类型判断的逻辑处理。
import { isObj } from '../common/utils';
// 定义一个函数 'getClassNames',它接收一个参数 'name',用于根据给定的名称生成一系列与过渡效果相关的类名字符串。
// 返回一个对象,对象的每个属性对应不同过渡阶段的类名组合,方便后续在组件样式相关的逻辑中使用这些类名来实现动画效果。
const getClassNames = (name) => ({
// 'enter' 阶段的类名组合,用于表示元素进入时的过渡状态,包含了多个类名,可能会在 CSS 中定义相应的样式来实现动画效果。
enter: `van-${name}-enter van-${name}-enter-active enter-class enter-active-class`,
// 'enter-to' 阶段的类名组合,通常用于表示元素进入过渡的目标状态,同样结合多个类名用于样式定义和动画实现。
'enter-to': `van-${name}-enter-to van-${name}-enter-active enter-to-class enter-active-class`,
// 'leave' 阶段的类名组合,对应元素离开时的过渡起始状态相关类名,通过这些类名关联的 CSS 样式来展示离开的动画效果。
leave: `van-${name}-leave van-${name}-leave-active leave-class leave-active-class`,
// 'leave-to' 阶段的类名组合,用于表示元素离开过渡的目标状态,配合 CSS 可以呈现完整的离开动画效果。
'leave-to': `van-${name}-leave-to van-${name}-leave-active leave-to-class leave-active-class`
});
// 定义 'nextTick' 函数,它返回一个 Promise用于模拟下一次事件循环的执行时机通过设置一个短暂的定时器这里设置为每 20 分之一秒,即 50 毫秒,对应帧率 20fps来实现类似浏览器的下一次重绘时机的效果常用于等待 DOM 更新等异步操作场景。
const nextTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 20));
// 定义 'transition' 函数,它返回一个通过 'Behavior' 创建的行为对象,该行为对象用于在微信小程序组件等环境中实现过渡动画相关的功能逻辑,并且可以接收一个参数'showDefaultValue' 用于设置默认显示状态。
export const transition = function (showDefaultValue) {
return Behavior({
// 'properties' 属性用于定义该行为对象所关联组件能够接收的外部属性,也就是组件对外暴露的可配置项,外部使用组件时可以传入相应的值来控制组件的行为和外观等方面。
properties: {
// 'customStyle' 属性,其值被限定为字符串类型(`String`),外部可传入符合字符串格式的数据,用于自定义组件的样式,提供额外的样式定制灵活性。
customStyle: String,
//'show' 属性,定义其类型为布尔类型(`Boolean`),默认值由传入 'transition' 函数的'showDefaultValue' 参数决定,并且设置了一个名为 'observer' 的回调函数 'observeShow',意味着当该属性的值发生变化时,会自动触发 'observeShow' 函数进行相应的处理,该属性通常用于控制组件的显示与隐藏状态。
show: {
type: Boolean,
value: showDefaultValue,
observer: 'observeShow'
},
// 'duration' 属性,其类型可以是数字类型(`Number`)或者对象类型(`Object`),默认值为 300同样设置了 'observer' 回调函数 'observeDuration',用于在属性值变化时执行相应逻辑,该属性可能用于控制过渡动画的时长相关设置。
duration: {
type: [Number, Object],
value: 300,
observer: 'observeDuration'
},
// 'name' 属性,类型为字符串类型(`String`),默认值为 'fade',并且设置了 'observer' 回调函数 'updateClasses',当该属性值改变时会触发 'updateClasses' 函数,该属性大概率用于指定过渡动画的名称或者类型,不同名称可能对应不同的样式和动画效果。
name: {
type: String,
value: 'fade',
observer: 'updateClasses'
}
},
// 'data' 属性用于定义组件内部的数据状态,这些数据可以在组件的各个方法中使用和修改,用于记录组件的一些状态信息以及与动画相关的一些临时数据等。
data: {
// 'type' 用于记录当前过渡的类型相关信息,初始值为空字符串,后续可能根据不同的操作(如显示、隐藏操作)来更新其值以表示相应的过渡状态。
type: '',
// 'inited' 用于标记组件是否已经完成初始化,初始值为 false在组件进行一些初始化操作如首次显示时会将其设置为 true。
inited: false,
// 'display' 用于控制组件是否显示,初始值为 false根据'show' 属性以及过渡动画的执行情况来动态改变其值,以实现组件的显示隐藏效果。
display: false,
// 'classNames' 通过调用 'getClassNames' 函数,传入默认的 'fade' 作为参数来获取初始的与过渡相关的类名对象,后续会根据 'name' 属性的变化来更新这些类名,用于应用相应的动画样式。
classNames: getClassNames('fade')
},
// 'attached' 是微信小程序组件生命周期中的一个钩子函数,会在组件被添加到页面时执行,在这里用于判断组件初始状态是否为显示状态,如果是则调用'show' 方法来展示组件并触发相应的过渡动画效果。
attached() {
if (this.data.show) {
this.show();
}
},
//'methods' 属性用于定义该行为对象中包含的一系列方法,这些方法可以被使用了这个行为的组件所调用,用于实现各种与过渡动画相关的具体功能逻辑,如显示、隐藏操作以及过渡状态变化的处理等。
methods: {
// 'observeShow' 方法是'show' 属性的观察者函数,当'show' 属性的值发生变化时会被调用,根据新传入的 'value'(即'show' 属性的新值)来决定是显示组件(调用'show' 方法)还是隐藏组件(调用 'leave' 方法)。
observeShow(value) {
if (value) {
this.show();
} else {
}
else {
this.leave();
}
},
// 'updateClasses' 方法是 'name' 属性的观察者函数,当 'name' 属性的值改变时会被调用,通过调用 'getClassNames' 函数并传入新的 'name' 值来获取对应的类名对象,然后使用'set' 方法(这里假设是微信小程序组件中用于更新数据的方法)更新组件内部的 'classNames' 数据,以应用新的过渡动画相关类名样式。
updateClasses(name) {
this.set({
classNames: getClassNames(name)
});
},
//'show' 方法用于处理组件显示的逻辑,实现显示时的过渡动画效果,涉及到更新组件的数据状态以及按顺序执行多个异步操作来逐步应用不同阶段的动画类名,从而实现平滑的过渡动画展示。
show() {
// 解构出组件数据中的 'classNames'(过渡相关类名对象)和 'duration'(过渡时长相关数据)属性,方便后续使用。
const { classNames, duration } = this.data;
// 根据 'duration' 的类型判断当前过渡时长,如果 'duration' 是对象类型,则取其 'leave' 属性的值作为过渡时长,否则直接使用 'duration' 的值作为过渡时长,这样可以支持更灵活的时长设置方式(比如针对进入和离开动画设置不同时长)。
const currentDuration = isObj(duration)? duration.leave : duration;
// 创建一个立即resolved的Promise用于开始链式调用后续的异步操作确保按顺序执行动画相关的状态更新步骤。
const currentDuration = isObj(duration) ? duration.leave : duration;
Promise.resolve()
// 调用 'nextTick' 函数,等待下一次事件循环时机,模拟浏览器下一次重绘之前的操作,常用于确保在 DOM 更新后进行下一步操作,在这里是过渡动画的一个时间节点控制。
.then(nextTick)
// 使用'set' 方法更新组件的多个数据属性,包括将 'inited' 设置为 true表示组件已初始化'display' 设置为 true显示组件'classes' 设置为 'classNames.enter'(应用进入动画的起始类名),以及更新 'currentDuration'(过渡时长),实现显示动画的第一步状态更新。
.then(() => this.set({
inited: true,
display: true,
classes: classNames.enter,
currentDuration
}))
// 再次调用 'nextTick' 函数,等待下一个合适的时间节点,继续过渡动画的下一步操作。
inited: true,
display: true,
classes: classNames.enter,
currentDuration
}))
.then(nextTick)
// 使用'set' 方法更新组件的 'classes' 属性为 'classNames['enter-to']',应用进入动画的目标类名,完成进入动画的完整过渡过程。
.then(() => this.set({
classes: classNames['enter-to']
}));
classes: classNames['enter-to']
}));
},
// 'leave' 方法用于处理组件隐藏的逻辑,实现隐藏时的过渡动画效果,同样涉及到更新组件数据状态以及通过多个异步操作配合定时器等方式来逐步应用不同阶段的动画类名,以实现平滑的过渡动画隐藏效果。
leave() {
const { classNames, duration } = this.data;
const currentDuration = isObj(duration)? duration.leave : duration;
const currentDuration = isObj(duration) ? duration.leave : duration;
Promise.resolve()
.then(nextTick)
// 使用'set' 方法更新组件的 'classes' 属性为 'classNames.leave'(应用离开动画的起始类名),同时更新 'currentDuration'(过渡时长),开启隐藏动画的第一步操作。
.then(() => this.set({
classes: classNames.leave,
currentDuration
}))
// 设置一个定时器,在 'currentDuration'(过渡时长)之后调用 'onTransitionEnd' 方法,用于在动画结束后执行一些收尾操作,比如完全隐藏组件等。
classes: classNames.leave,
currentDuration
}))
.then(() => setTimeout(() => this.onTransitionEnd(), currentDuration))
// 再次调用 'nextTick' 函数,等待下一个合适的时间节点,进行隐藏动画后续的类名更新操作。
.then(nextTick)
// 使用'set' 方法更新组件的 'classes' 属性为 'classNames['leave-to']'(应用离开动画的目标类名),完成隐藏动画的完整过渡过程。
.then(() => this.set({
classes: classNames['leave-to']
}));
classes: classNames['leave-to']
}));
},
// 'onTransitionEnd' 方法在过渡动画结束时被调用,用于处理动画结束后的相关逻辑,比如根据组件当前的'show' 属性值决定是否彻底隐藏组件(将 'display' 属性设置为 false并且通过 '$emit' 方法(这里假设是微信小程序组件中用于触发自定义事件的方式)向外触发一个名为 'transitionEnd' 的自定义事件,方便外部组件监听该事件并进行相应的后续操作。
onTransitionEnd() {
if (!this.data.show) {
this.set({ display: false });
@ -129,4 +90,4 @@ export const transition = function (showDefaultValue) {
}
}
});
};
};

@ -1,108 +1,74 @@
// 从相对路径 '../common/component' 导入名为 'VantComponent' 的模块,这里假设 'VantComponent' 是一个用于创建特定组件的函数,遵循着相应的组件构建规范或框架约定。
import { VantComponent } from '../common/component';
// 调用 'VantComponent' 函数来创建一个组件,传入一个配置对象,该对象用于定义组件的各种属性、数据、监听以及方法等相关信息,以此来定制组件的行为和功能。
VantComponent({
// 'field' 属性设置为 true这里可能暗示该组件与表单字段相关联或许会参与表单相关的交互逻辑比如在表单提交时的验证、取值等操作具体功能依赖于所在框架的实现方式。
field: true,
// 'classes' 属性定义了一个外部可传入的类名列表,这里包含了 'icon-class',意味着外部在使用这个组件时,可以通过传入这个类名来定制组件中与图标相关的样式,增强组件样式的可定制性。
classes: ['icon-class'],
// 'props' 属性用于定义组件可接收的外部属性(类似于其他框架中的 props 概念),是一个对象,对象中的每个键值对代表一个具体的外部属性及其相关配置,包括类型、默认值等信息。
props: {
// 'value' 属性,其值被限定为数字类型(`Number`),外部使用组件时需要传入符合数字格式的数据,该属性通常用于存储组件的一个数值相关的值,具体含义根据组件功能而定,可能是评分值之类的关键数据。
value: Number,
//'readonly' 属性,类型为布尔类型(`Boolean`),用于控制组件是否处于只读状态,外部传入 `true` 或 `false` 来决定用户是否能够对组件进行操作,例如在展示评分但不允许修改的场景下可以设置为 `true`。
readonly: Boolean,
// 'disabled' 属性,同样是布尔类型(`Boolean`),用于设置组件是否处于禁用状态,当设置为 `true` 时,用户通常不能与组件进行交互操作,常用于根据业务逻辑限制组件可用性的情况。
disabled: Boolean,
// 'allowHalf' 属性,布尔类型(`Boolean`),可能用于控制组件是否允许出现半分值之类的情况,比如评分组件中是否支持打半分,具体功能取决于组件的业务逻辑设计。
allowHalf: Boolean,
//'size' 属性,详细定义了它的类型为数字类型(`type: Number`),并且设置了默认值为 `20`,大概率用于控制组件中某个元素(可能是图标等)的尺寸大小,外部也可以传入其他数字来改变默认的尺寸设置。
size: {
type: Number,
value: 20
},
// 'icon' 属性,限定接收的值类型为字符串类型(`String`),默认值为 `'star'`,很可能用于指定组件中表示某种状态(如选中状态)的图标名称,方便通过图标来展示组件的相关状态,外部可传入其他字符串来更换图标。
icon: {
type: String,
value: 'star'
},
// 'voidIcon' 属性,也是字符串类型(`String`),默认值为 `'star-o'`,可能用于表示组件中与 'icon' 相对应的另一种状态(如未选中状态)的图标名称,同样可由外部传入不同字符串进行定制。
voidIcon: {
type: String,
value: 'star-o'
},
// 'color' 属性,类型为字符串类型(`String`),默认值为 `'#ffd21e'`,通常用于设置组件中某个元素(比如选中状态图标)的颜色,外部可以传入符合颜色表示规范的字符串来改变默认颜色。
color: {
type: String,
value: '#ffd21e'
},
// 'voidColor' 属性,同样是字符串类型(`String`),默认值为 `'#c7c7c7'`,大概率用于设置未选中状态相关元素的颜色,可通过外部传入字符串来进行颜色定制。
voidColor: {
type: String,
value: '#c7c7c7'
},
// 'disabledColor' 属性,类型为字符串类型(`String`),默认值为 `'#bdbdbd'`,用于设定组件处于禁用状态时相关元素呈现的颜色,外部可传入合适字符串改变此颜色设置。
disabledColor: {
type: String,
value: '#bdbdbd'
},
// 'count' 属性,其类型为数字类型(`Number`),默认值为 `5`,很可能用于表示组件中某种元素的数量,比如评分组件中星星图标的数量,外部可以传入不同数字来调整数量。
count: {
type: Number,
value: 5
}
},
// 'data' 属性用于定义组件内部的数据状态,这些数据可以在组件的各个方法中使用和修改,这里定义了 'innerValue' 并初始化为 `0`,可能用于在组件内部临时存储和处理一些与外部 'value' 属性相关的值,或者用于记录组件的中间状态等。
data: {
innerValue: 0
},
// 'watch' 属性用于定义对组件数据的监听逻辑,这里监听了 'value' 属性的变化,当 'value' 的值发生改变时,会执行对应的回调函数。
watch: {
value(value) {
// 在 'value' 的新值与组件内部的 'innerValue' 不相等时,说明 'value' 属性发生了实际的变化,此时通过 'this.set' 方法(这里假设是所在框架用于更新组件数据的方法)更新 'innerValue' 的值,使其与 'value' 保持一致,确保组件内部状态与外部传入的属性值同步。
if (value!== this.data.innerValue) {
if (value !== this.data.innerValue) {
this.set({ innerValue: value });
}
}
},
//'methods' 属性用于定义组件内部可调用的方法,这些方法实现了组件的各种交互逻辑和业务功能,比如响应用户的操作等。
methods: {
// 定义名为 'onSelect' 的方法,该方法可能在用户进行选择操作(比如点击某个图标来选择评分等情况)时被触发,接收一个 'event' 参数,包含了触发事件时的相关信息。
onSelect(event) {
// 解构出组件内部的数据对象,方便后续访问组件的各个数据属性,例如判断组件是否处于禁用或只读状态等。
const { data } = this;
// 从触发事件的目标元素(`event.currentTarget`)的数据集(`dataset`)中获取名为'score' 的数据,这里的'score' 可能代表用户选择的某个分值相关的数据,具体含义取决于组件的业务逻辑设计。
const { score } = event.currentTarget.dataset;
// 判断组件当前既不处于禁用状态(`!data.disabled`)也不处于只读状态(`!data.readonly`),即用户可以进行操作的情况下,执行以下逻辑。
if (!data.disabled &&!data.readonly) {
// 通过 'this.set' 方法更新组件内部的 'innerValue' 属性为用户选择的分值加 `1`,这里加 `1` 的操作可能与组件具体的分值计算逻辑有关,比如索引从 `0` 开始但分值从 `1` 开始计数等情况。
if (!data.disabled && !data.readonly) {
this.set({ innerValue: score + 1 });
// 使用 '$emit' 方法(这里假设是所在框架用于触发自定义事件的机制)向外触发一个名为 'input' 的自定义事件,并将用户选择的分值加 `1` 作为参数传递出去,方便外部组件监听该事件获取用户选择的分值,常用于与表单等相关的双向数据绑定场景。
this.$emit('input', score + 1);
// 同样使用 '$emit' 方法向外触发一个名为 'change' 的自定义事件,也将用户选择的分值加 `1` 作为参数传递出去,外部可以监听该事件来执行相应的业务逻辑,比如更新页面其他部分显示的总分等情况。
this.$emit('change', score + 1);
}
},
// 定义名为 'onTouchMove' 的方法,该方法可能在用户触摸屏幕并移动手指(比如在移动端进行滑动操作)时被触发,接收 'event' 参数,包含触摸移动事件相关的信息。
onTouchMove(event) {
// 从触摸事件对象的第一个触摸点(`event.touches[0]`)中解构出 'clientX'(触摸点的横坐标,相对于浏览器窗口左上角的位置)和 'clientY'(触摸点的纵坐标)属性,方便后续判断触摸点所在的位置。
const { clientX, clientY } = event.touches[0];
// 调用组件内部的 'getRect' 方法(这里假设该方法用于获取指定选择器对应的元素的布局矩形信息,可能是基于所在框架提供的相关 API 实现),传入选择器 '.van-rate__icon' 并设置 `true` 表示获取所有匹配的元素,返回一个 `Promise`,在获取到元素信息后进行后续处理。
this.getRect('.van-rate__icon', true).then((list) => {
// 对获取到的元素列表(`list`)进行排序操作,排序规则是根据每个元素的右边界(`right`)减去左边界(`left`)的值来升序排列,这样可以方便后续按照从左到右的顺序查找触摸点所在的元素。
const target = list
.sort(item => item.right - item.left)
// 使用 `find` 方法在排好序的元素列表中查找符合条件的元素,条件是触摸点的横坐标(`clientX`)在元素的左边界(`left`)和右边界(`right`)之间,并且纵坐标(`clientY`)在元素的上边界(`top`)和下边界(`bottom`)之间,找到的元素即为触摸点所在的目标元素。
.find(item => clientX >= item.left &&
clientX <= item.right &&
clientY >= item.top &&
clientY <= item.bottom);
// 如果找到了目标元素(`target` 不为 `null`),则调用 'onSelect' 方法,模拟用户点击该目标元素的操作逻辑,传入一个经过处理的事件对象(将原事件对象和目标元素合并,模拟点击事件中目标元素的相关信息),以便执行相应的选择和事件触发逻辑。
if (target!= null) {
clientX <= item.right &&
clientY >= item.top &&
clientY <= item.bottom);
if (target != null) {
this.onSelect(Object.assign({}, event, { currentTarget: target }));
}
});
}
}
});
});

@ -1,34 +1,32 @@
module.exports = {
env: {
browser: true, // 启用浏览器环境,提供全局变量如 `window` 和 `document`
es2021: true, // 启用 ES2021 语法支持
node: true // 启用 Node.js 环境,提供全局变量如 `require` 和 `module`
browser: true,
es2021: true,
node: true
},
globals: {
uni: 'readonly', // 将 `uni` 定义为只读全局变量,适用于 Uni-App 框架
getApp: 'readonly', // 将 `getApp` 定义为只读全局函数,用于获取应用实例
wx: 'readonly', // 将 `wx` 定义为只读全局对象,适用于微信小程序
getCurrentPages: 'readonly', // 将 `getCurrentPages` 定义为只读全局函数,用于获取当前页面栈
plus: 'readonly' // 将 `plus` 定义为只读全局对象,适用于 5+AppHTML5+
uni: 'readonly',
getApp: 'readonly',
wx: 'readonly',
getCurrentPages: 'readonly',
plus: 'readonly'
},
extends: [
'standard', // 继承 Standard 代码风格规则集
'./.eslintrc-auto-import.json', // 继承自定义的自动导入配置文件
'plugin:vue/vue3-recommended', // 继承 Vue 3 的推荐规则集
'plugin:vue-scoped-css/vue3-recommended' // 继承 Vue 3 scoped CSS 的推荐规则集
'standard',
'./.eslintrc-auto-import.json',
'plugin:vue/vue3-recommended',
'plugin:vue-scoped-css/vue3-recommended'
],
overrides: [
// 用于针对特定文件或文件模式进行规则覆盖,当前为空
],
parserOptions: {
ecmaVersion: 'latest', // 使用最新的 ECMAScript 版本解析代码
sourceType: 'module' // 将代码视为 ES 模块
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: [
'vue' // 使用 `eslint-plugin-vue` 插件来支持 Vue 文件的 linting
]
};
'vue'
],
rules: {
// Possible Errors
// 要求使用 let 或 const 而不是 var

@ -1,23 +1,4 @@
# 启用严格的引擎检查
# 当设置为 true 时npm 会严格检查项目的 package.json 文件中的 engines 字段,
# 确保当前环境的 Node.js 和 npm 版本符合指定的要求。如果不符合npm 将抛出错误并阻止安装依赖。
engine-strict = true
# 启用“羞耻地提升”shameful hoisting
# 在 yarn 中,默认情况下,依赖项会被提升到顶层 node_modules 目录中,以减少嵌套层级。
# 但是某些情况下yarn 会避免提升一些依赖项,以防止潜在的冲突。
# 设置为 true 时yarn 会忽略这些规则,尽可能地将所有依赖项提升到顶层,即使这样做可能会导致冲突。
# 这种做法被称为“羞耻地提升”,因为它可能会引入不稳定的依赖关系。
shamefully-hoist = true
# 禁用严格的 peer 依赖检查
# 当设置为 false 时npm 或 yarn 不会因为 peer 依赖版本不匹配而抛出错误或警告。
# peer 依赖通常用于插件系统,确保插件和主库的版本兼容性。
# 设置为 false 可以避免因 peer 依赖版本不匹配而导致的安装失败,但可能会引入潜在的兼容性问题。
strict-peer-dependencies = false
# 指定 npm 或 yarn 使用的注册表registry
# 默认情况下npm 和 yarn 使用官方的 npm 注册表 (https://registry.npmjs.org)。
# 通过设置此选项,可以切换到其他镜像源,例如国内的 npmmirror以加快包的下载速度。
# 这里指定了使用 https://registry.npmmirror.com 作为注册表,适用于中国大陆用户。
registry = https://registry.npmmirror.com

@ -2,33 +2,20 @@
<html>
<head>
<meta charset="UTF-8" />
<!-- 设置文档字符编码为 UTF-8确保页面正确显示中文等多语言字符 -->
<meta name="renderer" content="webkit">
<!-- 强制使用 WebKit 渲染引擎,适用于某些浏览器(如 QQ 浏览器)以确保更好的兼容性 -->
<title>mall4uni-pro</title>
<!-- 页面标题,显示在浏览器标签上 -->
<script>
// 检查浏览器是否支持 CSS 的 `env()` 和 `constant()` 函数
// 这些函数用于适配 iPhone X 及以上机型的“安全区域”(如底部导航栏)
let coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'));
// 动态生成 viewport 元标签,设置页面的视口属性
// `viewport-fit=cover` 用于确保页面内容完全覆盖整个屏幕,避免出现空白区域
let coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />'
);
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<!-- 预加载链接(preload-links),通常用于预加载关键资源,提升页面加载速度 -->
<!--preload-links-->
<!--app-context-->
<!-- 此处可能是动态插入的应用上下文信息例如通过服务器端渲染SSR注入的数据 -->
</head>
<body>
<div id="app"><!--app-html--></div>
<!-- 主应用容器Vue 或其他前端框架的根元素将挂载在这里 -->
<script type="module" src="/src/main.js"></script>
<!-- 引入主 JavaScript 文件,使用 ES6 模块语法 (`type="module"`),确保代码模块化和更好的性能 -->
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

@ -1,9 +1,9 @@
{
"compilerOptions": {
"types": [
"@dcloudio/types", // DCloud Uni-App
"miniprogram-api-typings", // API TypeScript API
"mini-types" // TypeScript
"@dcloudio/types",
"miniprogram-api-typings",
"mini-types"
]
},
"allowJs": true // js

@ -1,176 +1,108 @@
/** app.wxss **/
/*
*
* 100%
* 使 border-box 使 padding border
* #333
*/
/**app.wxss**/
.container {
height: 100%;
box-sizing: border-box;
color: #333;
font-family: helvetica, 'Heiti SC', PingFangSC-Light;
font-family: helvetica,'Heiti SC',PingFangSC-Light;
}
/*
*
* Arial (#eb2444)
* 使
*/
.price {
.price{
font-family: Arial;
display: inline-block;
color: #eb2444;
padding-bottom: 10rpx;
padding-left: 10rpx;
padding-bottom:10rpx;
padding-left:10rpx;
}
/*
* ¥$
* 24rpx
*/
/* 价格数字显示不同大小 */
.symbol {
font-size: 24rpx;
}
/*
*
* 32rpx
*/
.big-num {
font-size: 32rpx;
}
/*
*
* 24rpx
*/
.small-num {
font-size: 24rpx;
}
/*
* checkbox
*
*/
/*
* checkbox
* checkbox 100%
*/
*checkbox
*
*/
/* reg */
uni-checkbox-group {
width: 100% !important;
}
/*
* checkbox
* checkbox 33%使使
*
*/
uni-checkbox-group uni-label {
width: 33% !important;
display: inline-flex;
margin-bottom: 20rpx;
}
/*
* checkbox
* checkbox 38rpxborder-radius: 50%
*/
uni-checkbox .uni-checkbox-input {
uni-checkbox-group uni-label{
width: 33% !important;
display: inline-flex;
margin-bottom: 20rpx;
}
/*checkbox 选项框大小 */
uni-checkbox .uni-checkbox-input{
width: 38rpx !important;
height: 38rpx !important;
border-radius: 50% !important;
border-radius: 50%!important;
}
/*checkbox选中后样式 */
uni-checkbox .uni-checkbox-input.uni-checkbox-input-checked{
background: #e43130;
border: 1px solid transparent !important;
}
/*checkbox选中后图标样式 */
uni-checkbox .uni-checkbox-input.uni-checkbox-input-checked::before{
display: inline-block;
width: 20rpx;
height: 20rpx;
line-height: 20rpx;
text-align: center;
font-size: 18rpx;
color: #fff;
background: transparent;
transform: translate(-60%, -50%) scale(1);
-webkit-transform: translate(-60%, -50%) scale(1);
}
/*
* checkbox
* checkbox (#e43130)
*/
uni-checkbox .uni-checkbox-input.uni-checkbox-input-checked {
background: #e43130;
border: 1px solid transparent !important;
}
/*
* checkbox
* checkbox
* 使 transform
*/
uni-checkbox .uni-checkbox-input.uni-checkbox-input-checked::before {
display: inline-block;
width: 20rpx;
height: 20rpx;
line-height: 20rpx;
text-align: center;
font-size: 18rpx;
color: #fff;
background: transparent;
transform: translate(-60%, -50%) scale(1);
-webkit-transform: translate(-60%, -50%) scale(1);
}
/*
* radio
*
*/
/*
* radio
* radio 36rpx
*/
uni-radio .uni-radio-input {
*radio
*
*/
/* 未选中的 背景样式 */
uni-radio .uni-radio-input{
height: 36rpx;
width: 36rpx;
border-radius: 50%;
background: transparent;
box-sizing: border-box;
}
/* 选中后的 背景样式 */
uni-radio .uni-radio-input.uni-radio-input-checked{
border: none !important;
background: #e43130 !important;
}
/* 选中后的 对勾样式 */
uni-radio .uni-radio-input.uni-radio-input-checked::before{
border-radius: 50%;
width: 32rpx;
height: 32rpx;
line-height: 32rpx;
text-align: center;
font-size: 20rpx;
color:#fff;
background: #e43130;
border-radius: 50%;
transform: translate(-50%, -50%) scale(1);
-webkit-transform: translate(-50%, -50%) scale(1);
}
/*
* radio
* radio (#e43130)
*/
uni-radio .uni-radio-input.uni-radio-input-checked {
border: none !important;
background: #e43130 !important;
}
/*
* radio
* radio
* 使 transform
*/
uni-radio .uni-radio-input.uni-radio-input-checked::before {
border-radius: 50%;
width: 32rpx;
height: 32rpx;
line-height: 32rpx;
text-align: center;
font-size: 20rpx;
color: #fff;
background: #e43130;
border-radius: 50%;
transform: translate(-50%, -50%) scale(1);
-webkit-transform: translate(-50%, -50%) scale(1);
}
/*
* iPhone X
* iPhone X
*/
@media screen and (width: 375px) and (height: 812px) {
/* 底部按钮兼容 iPhone X以上 */
@media screen and (width: 375px) and (height: 812px){
.container {
padding-bottom: 70px;
}
}
/*
* iPhone 8 Plus
* iPhone 8 Plus
*/
@media screen and (width: 414px) and (height: 736px) {
@media screen and (width: 414px) and (height: 736px){
.container {
padding-bottom: 70px;
}

@ -1,94 +1,79 @@
<template>
<!-- 根据条件判断显示不同的图片元素 -->
<!-- 如果不存在错误isError为false且图片路径imgPath有值则显示此图片元素 -->
<image v-if="!isError && imgPath"
:src="imgPath"
:style="imgStyle"
:class="classList"
:mode="imgMode"
@error="imgError"
@load="imgLoad"
@tap="handleTap" />
<!-- 如果存在错误或者图片路径无值则显示默认图片元素 -->
<image v-else
:src="defaultImgPath"
:style="imgStyle"
:class="classList"
@tap="handleTap" />
<image
v-if="!isError && imgPath"
:src="imgPath"
:style="imgStyle"
:class="classList"
:mode="imgMode"
@error="imgError"
@load="imgLoad"
@tap="handleTap"
/>
<image
v-else
:src="defaultImgPath"
:style="imgStyle"
:class="classList"
@tap="handleTap"
/>
</template>
<script setup>
// 使 defineProps props
const props = defineProps({
//
src: {
type: String,
default: ''
},
// 'scaleToFill'
imgMode: {
type: String,
default: 'scaleToFill'
},
//
classList: {
type: Array,
default: () => {
return []
}
},
//
imgStyle: {
type: Object,
default: () => {
return {}
}
},
// falsetruefalse
defaultImgType: {
type: Boolean,
default: false
const props = defineProps({
src: {
type: String,
default: ''
},
imgMode: {
type: String,
default: 'scaleToFill'
},
classList: {
type: Array,
default: () => {
return []
}
})
},
imgStyle: {
type: Object,
default: () => {
return {}
}
},
// , false, true
defaultImgType: {
type: Boolean,
default: false
}
})
// imgPath util.checkFileUrl props.src
//
const imgPath = computed(() => {
return util.checkFileUrl(props.src)
})
// defaultImgPath defaultImgType
// defaultImgType true
const defaultImgPath = computed(() => {
if (props.defaultImgType) return '/static/images/icon/head04.png'
return '/static/images/icon/def.png'
})
const imgPath = computed(() => {
return util.checkFileUrl(props.src)
})
const defaultImgPath = computed(() => {
if (props.defaultImgType) return '/static/images/icon/head04.png'
return '/static/images/icon/def.png'
})
// 使 defineEmits
const emit = defineEmits(['imgError', 'imgLoad', 'handleTap'])
// isError false
const isError = ref(false)
//
// isError true 'imgError'
const imgError = () => {
isError.value = true
emit('imgError')
}
const emit = defineEmits(['imgError', 'imgLoad', 'handleTap'])
const isError = ref(false)
const imgError = () => {
isError.value = true
emit('imgError')
}
// 'imgLoad' e
const imgLoad = (e) => {
emit('imgLoad', e)
}
const imgLoad = (e) => {
emit('imgLoad', e)
}
// 'handleTap'
const handleTap = () => {
emit('handleTap')
}
const handleTap = () => {
emit('handleTap')
}
</script>
<style scoped>
/* 为 image 标签设置样式,使其宽度和高度都占满父元素的 100% */
image {
width: 100%;
height: 100%;
}
image {
width: 100%;
height: 100%;
}
</style>

@ -1,86 +1,53 @@
/*
.prod-items
43%40rpx使 border-box使
*/
.prod-items {
width: 43%;
background: #fff;
margin-bottom: 40rpx;
box-sizing: border-box;
/*
.hot-imagecont
0
*/
.hot-imagecont {
.hot-imagecont {
border-radius: 8rpx;
text-align: center;
font-size: 0;
}
.hot-text {
.hot-text {
margin-top: 20rpx;
/*
.prod-info
*/
.prod-info {
.prod-info {
font-size: 20rpx;
color: #777;
padding: 0 20rpx;
margin-top: 8rpx;
}
/*
.prod-text-info
*/
.prod-text-info {
.prod-text-info {
position: relative;
height: 50rpx;
line-height: 70rpx;
font-family: Arial;
/*
.price #eb2444
*/
.price {
.price {
color: #eb2444;
padding-left: 20rpx;
}
}
}
}
/*
prod
:nth-child(2n-1)
*/
prod {
&:nth-child(2n-1) {
.prod-items {
.prod-items {
padding: 20rpx 10rpx 10rpx 20rpx;
}
}
/*
:nth-child(2n)
.prod-items
*/
&:nth-child(2n) {
.prod-items {
.prod-items {
padding: 20rpx 20rpx 10rpx 10rpx;
}
}
}
/*
.hot-imagecont .hotsaleimg 使345rpx
*/
.hot-imagecont {
.hotsaleimg {
.hotsaleimg {
width: 100%;
height: 345rpx;
}
}
/*
.hot-text .hotprod-text
2
*/
.hot-text {
.hotprod-text {
.hotprod-text {
height: 76rpx;
font-size: 28rpx;
display: -webkit-box;
@ -94,9 +61,6 @@ prod {
color: #000;
}
}
/*
.deadline-price
*/
.deadline-price {
font-size: 22rpx;
margin-right: 5rpx;

@ -1,70 +1,39 @@
<template>
<!--
使用view标签创建一个类名为"prod-items"的组件元素通过":data-prodid"绑定了一个名为"item.prodId"的数据属性
并监听了"tap"事件当触发点击时会调用"toProdPage"方法
-->
<view class="prod-items"
:data-prodid="item.prodId"
@tap="toProdPage">
<!--
创建一个类名为"hot-imagecont"的view元素用于包裹商品图片相关内容起到容器的作用
-->
<view
class="prod-items"
:data-prodid="item.prodId"
@tap="toProdPage"
>
<view class="hot-imagecont">
<!--
使用image标签展示商品图片图片的src属性通过":src"绑定了名为"item.pic"的数据
并设置了类名"hotsaleimg"该类名应该在对应的样式文件中有相应的样式定义来控制图片的展示样式
-->
<image :src="item.pic"
class="hotsaleimg" />
<image
:src="item.pic"
class="hotsaleimg"
/>
</view>
<!--
创建一个类名为"hot-text"的view元素用于包裹商品的文本信息部分比如商品名称评价信息价格等内容
-->
<view class="hot-text">
<!--
创建一个类名为"hotprod-text"的view元素用于展示商品名称通过双大括号插值表达式"{{ item.prodName }}"来显示具体的商品名称数据
-->
<view class="hotprod-text">
{{ item.prodName }}
</view>
<!--
根据"sts"的值来决定是否显示该元素"sts === 6"显示这个类名为"prod-info"的view元素
用于展示商品的评价相关信息如评价数量和好评率等同样通过插值表达式来展示具体数据
-->
<view v-if="sts===6"
class="prod-info">
<view
v-if="sts===6"
class="prod-info"
>
{{ item.prodCommNumber }}评价 {{ item.positiveRating }}%好评
</view>
<!--
创建一个类名为"prod-text-info"的view元素用于包裹商品价格相关的展示内容是价格信息的整体容器
-->
<view class="prod-text-info">
<!--
创建一个类名为"price"的view元素用于进一步细分价格展示的不同部分如限时价标识货币符号价格的整数部分和小数部分等
-->
<view class="price">
<!--
根据"sts"的值来决定是否显示这个类名为"deadline-price"的text元素"sts === 2"显示"限时价"文本用于提示限时优惠价格情况
-->
<text v-if="sts===2"
class="deadline-price">
<text
v-if="sts===2"
class="deadline-price"
>
限时价
</text>
<!--
展示货币符号"¥"使用text标签进行文本展示设置了类名"symbol"方便样式控制虽然此处没看到具体样式但可以后续添加样式定义
-->
<text class="symbol">
</text>
<!--
通过调用"wxs.parsePrice(item.price)[0]"方法来获取商品价格的整数部分并展示在页面上类名"big-num"方便后续样式控制比如设置字体大小等
-->
<text class="big-num">
{{ wxs.parsePrice(item.price)[0] }}
</text>
<!--
通过调用"wxs.parsePrice(item.price)[1]"方法来获取商品价格的小数部分并展示在页面上类名"small-num"方便样式处理确保小数部分的显示格式等符合要求
-->
<text class="small-num">
.{{ wxs.parsePrice(item.price)[1] }}
</text>
@ -74,40 +43,31 @@
</view>
</template>
<script setup>
// "wxs""number""number"
const wxs = number()
// "props"使"defineProps"
// eslint使
// eslint-disable-next-line no-unused-vars
const props = defineProps({
// "item"Objectnullnull
item: {
type: Object,
default: () => {
return null
}
},
// "sts"Number22
sts: {
type: Number,
default: () => {
return 2
}
const wxs = number()
// eslint-disable-next-line no-unused-vars
const props = defineProps({
item: {
type: Object,
default: () => {
return null
}
},
sts: {
type: Number,
default: () => {
return 2
}
})
// "toProdPage"
const toProdPage = (e) => {
// "e""data-prodid"idid
const prodid = e.currentTarget.dataset.prodid
// 使uni-app"uni.navigateTo""/pages/prod/prod"id便
uni.navigateTo({
url: '/pages/prod/prod?prodid=' + prodid
})
}
})
const toProdPage = (e) => {
const prodid = e.currentTarget.dataset.prodid
uni.navigateTo({
url: '/pages/prod/prod?prodid=' + prodid
})
}
</script>
<style scoped lang="scss">
// "./production.scss"使scoped
@use './production.scss';
@use './production.scss';
</style>

@ -1,18 +1,9 @@
import {
createSSRApp // 从 Vue 库中导入 createSSRApp 函数,用于创建一个适用于服务器端渲染 (SSR) 的 Vue 应用实例
createSSRApp
} from 'vue'
import App from './App.vue' // 导入应用的根组件
/**
* 创建并返回一个 Vue 应用实例
*
* @returns {Object} - 包含应用实例的对象
*/
export function createApp() {
// 使用 createSSRApp 函数创建一个新的 Vue 应用实例,并传入根组件 App
import App from './App.vue'
export function createApp () {
const app = createSSRApp(App)
// 返回一个对象,包含创建的应用实例
return {
app
}

@ -1,106 +1,99 @@
{
"name": "", //
"appid": "__UNI__2CF44C6", // AppID
"description": "", //
"versionName": "1.0.0", //
"versionCode": "100", //
"transformPx": false, // px rpxfalse
/* 5+App */
"app-plus": {
"compatible": {
"ignoreVersion": true // true HBuilderX 1.9.0
},
"usingComponents": true, // true
"nvueStyleCompiler": "uni-app", // nvue 使 uni-app
"compilerVersion": 3, //
"splashscreen": {
"alwaysShowBeforeRender": true, // true
"waiting": true, // true
"autoclose": true, // true
"delay": 0 // 0
},
/* */
"modules": {
"Camera": {}, // Camera
"LivePusher": {} // LivePusher
},
/* */
"distribute": {
/* android */
"android": {
"permissions": [
"<uses-feature android:name=\"android.hardware.camera\"/>", // 使
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", // 使
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", // 访
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", // 访 Wi-Fi
"<uses-permission android:name=\"android.permission.CAMERA\"/>", // 使
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", //
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", // Wi-Fi
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", // 使
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", //
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", //
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", //
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", //
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>", //
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", // 使
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", //
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" //
],
"abiFilters": [ "armeabi-v7a", "arm64-v8a", "x86" ] // ABI
}
}
},
/* */
"mp-weixin": {
"appid": "wx6fa71e69231a4fa4", // AppID
"setting": {
"urlCheck": false // URL false
},
"usingComponents": true, // true
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于地址信息新增、修改以及获取附近门店" //
}
},
"requiredPrivateInfos": [ "getLocation", "chooseLocation" ] //
},
"h5": {
"title": "", // H5
"domain": "https://mini-h5.mall4j.com", // H5
"router": {
"mode": "history" // 使 history
"name" : "",
"appid" : "__UNI__2CF44C6",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"compatible" : {
"ignoreVersion" : true //trueHBuilderX1.9.0
},
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {
"Camera" : {},
"LivePusher" : {}
},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
],
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
}
}
},
"uniStatistics": {
"enable": false // Uni-App false
/* */
"mp-weixin" : {
"appid" : "wx6fa71e69231a4fa4",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true,
"permission" : {
"scope.userLocation" : {
"desc" : "你的位置信息将用于地址信息新增、修改以及获取附近门店"
}
},
"requiredPrivateInfos" : [ "getLocation", "chooseLocation" ]
},
"optimization": {
"treeShaking": {
"enable": false // Tree Shaking false
}
"h5" : {
"title" : "",
"domain" : "https://mini-h5.mall4j.com",
"router" : {
"mode" : "history"
},
"uniStatistics" : {
"enable" : false
},
"optimization" : {
"treeShaking" : {
"enable" : false
}
},
"template" : "index.html",
"devServer" : {
"disableHostCheck" : true,
"port" : 80
},
"sdkConfigs" : {
"maps" : {
"qqmap" : {
// h5ip
"key" : ""
}
}
}
},
"template": "index.html", // H5
"devServer": {
"disableHostCheck": true, // true
"port": 80 //
"uniStatistics" : {
"enable" : false
},
"sdkConfigs": {
"maps": {
"qqmap": {
"key": "" // QQ API Key H5 IP
}
}
}
},
"uniStatistics": {
"enable": false // Uni-App false
},
"vueVersion": "3" // 使 Vue Vue 3
"vueVersion" : "3"
}

@ -1,200 +1,196 @@
{
"pages": [
{
"path": "pages/index/index", //
"path": "pages/index/index",
"style": {
"backgroundTextStyle": "dark", // "dark"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTextStyle": "black", //
"enablePullDownRefresh": true, // true
"navigationBarTitleText": "mall4j" //
"backgroundTextStyle": "dark",
"navigationBarBackgroundColor": "#fff",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": true,
"navigationBarTitleText": "mall4j"
}
},
{
"path": "pages/user/user", //
"path": "pages/user/user",
"style": {
"navigationBarTitleText": "个人中心" //
"navigationBarTitleText": "个人中心"
}
},
{
"path": "pages/basket/basket", //
"path": "pages/basket/basket",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "购物车", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "购物车",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/category/category", //
"path": "pages/category/category",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "分类商品", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "分类商品",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/sub-category/sub-category" //
"path": "pages/sub-category/sub-category"
},
{
"path": "pages/search-page/search-page", //
"path": "pages/search-page/search-page",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "搜索", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "搜索",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/delivery-address/delivery-address", //
"path": "pages/delivery-address/delivery-address",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "收货地址", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "收货地址",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/editAddress/editAddress", //
"path": "pages/editAddress/editAddress",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "编辑收货地址", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "编辑收货地址",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/orderList/orderList", //
"path": "pages/orderList/orderList",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarTitleText": "订单列表", //
"navigationBarTextStyle": "black", //
"navigationBarBackgroundColor": "#fafafa" //
"backgroundTextStyle": "light",
"navigationBarTitleText": "订单列表",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#fafafa"
}
},
{
"path": "pages/order-detail/order-detail", //
"path": "pages/order-detail/order-detail",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "订单详情", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "订单详情",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/submit-order/submit-order", //
"path": "pages/submit-order/submit-order",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "提交订单", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "提交订单",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/express-delivery/express-delivery", //
"path": "pages/express-delivery/express-delivery",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "物流查询", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "物流查询",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/pay-result/pay-result", //
"path": "pages/pay-result/pay-result",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "支付结果", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "支付结果",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/search-prod-show/search-prod-show", //
"path": "pages/search-prod-show/search-prod-show",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "搜索结果", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "搜索结果",
"navigationBarTextStyle": "black"
}
},
{
"path": "pages/prod/prod", //
"path": "pages/prod/prod",
"style": {
"navigationBarTitleText": "商品详情" //
"navigationBarTitleText": "商品详情"
}
},
{
"path": "pages/prod-classify/prod-classify", //
"path": "pages/prod-classify/prod-classify",
"style": {
"onReachBottomDistance": 0 // 0
"onReachBottomDistance": 0
}
},
{
"path": "pages/recent-news/recent-news", //
"path": "pages/recent-news/recent-news",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarTitleText": "最新公告", //
"navigationBarTextStyle": "black", //
"navigationBarBackgroundColor": "#fafafa" //
"backgroundTextStyle": "light",
"navigationBarTitleText": "最新公告",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#fafafa"
}
},
{
"path": "pages/news-detail/news-detail", //
"path": "pages/news-detail/news-detail",
"style": {
"backgroundTextStyle": "light", // "light"
"navigationBarTitleText": "最新公告", //
"navigationBarTextStyle": "black", //
"navigationBarBackgroundColor": "#fafafa" //
"backgroundTextStyle": "light",
"navigationBarTitleText": "最新公告",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#fafafa"
}
},
{
"path": "pages/accountLogin/accountLogin" //
"path": "pages/accountLogin/accountLogin"
},
{
"path": "pages/register/register" //
"path": "pages/register/register"
}
],
"tabBar": {
"selectedColor": "#3a86b9", //
"color": "#b8b8b8", //
"selectedColor": "#3a86b9",
"color": "#b8b8b8",
"list": [
{
"pagePath": "pages/index/index", //
"text": "首页", //
"iconPath": "/static/images/tabbar/homepage.png", //
"selectedIconPath": "/static/images/tabbar/homepage-sel.png" //
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "/static/images/tabbar/homepage.png",
"selectedIconPath": "/static/images/tabbar/homepage-sel.png"
},
{
"pagePath": "pages/category/category", //
"text": "分类", //
"iconPath": "/static/images/tabbar/category.png", //
"selectedIconPath": "/static/images/tabbar/category-sel.png" //
"pagePath": "pages/category/category",
"text": "分类",
"iconPath": "/static/images/tabbar/category.png",
"selectedIconPath": "/static/images/tabbar/category-sel.png"
},
{
"pagePath": "pages/basket/basket", //
"text": "购物车", //
"iconPath": "/static/images/tabbar/basket.png", //
"selectedIconPath": "/static/images/tabbar/basket-sel.png" //
"pagePath": "pages/basket/basket",
"text": "购物车",
"iconPath": "/static/images/tabbar/basket.png",
"selectedIconPath": "/static/images/tabbar/basket-sel.png"
},
{
"pagePath": "pages/user/user", //
"text": "我的", //
"iconPath": "/static/images/tabbar/user.png", //
"selectedIconPath": "/static/images/tabbar/user-sel.png" //
"pagePath": "pages/user/user",
"text": "我的",
"iconPath": "/static/images/tabbar/user.png",
"selectedIconPath": "/static/images/tabbar/user-sel.png"
}
]
},
"sitemapLocation": "sitemap.json", //
"sitemapLocation": "sitemap.json",
"globalStyle": {
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "WeChat", //
"navigationBarTextStyle": "black" //
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
},
"subPackages": [] //
"subPackages": []
}

@ -1,18 +1,8 @@
/*
.con 100%
50px
*/
.con {
background: #fff;
height: 100%;
margin-top: 50px;
}
/*
image 使便
150rpx margin: auto 50%使
8%
*/
image {
display: block;
width: 150rpx;
@ -23,22 +13,11 @@ image {
height: 150rpx;
margin-bottom: 8%;
}
/*
.login-form 90% margin: 0 auto
20%使
*/
.login-form {
width: 90%;
margin: 0 auto;
margin-bottom: 20%;
}
/*
authorized-btn 90%
绿#0ab9061rpx6rpx26rpx
8rpx80rpx便
*/
.authorized-btn {
width: 90%;
margin: 0 auto;
@ -56,12 +35,6 @@ authorized-btn 类用于设置授权按钮的样式宽度占父元素的90%
padding: 8rpx;
margin-top: 80rpx;
}
/*
to-idx-btn 90%
#eeeeee#3336rpx26rpx8rpx
30rpx
*/
.to-idx-btn {
width: 90%;
margin: 0 auto;
@ -77,11 +50,6 @@ to-idx-btn 类用于设置跳转到首页按钮(推测,根据命名)的样
padding: 8rpx;
margin-top: 30rpx;
}
/*
form-title 100%50rpx
32rpx#00a0e9便
*/
.form-title {
width: 100%;
margin-bottom: 50rpx;
@ -91,22 +59,11 @@ form-title 类用于设置表单标题的样式宽度占满父元素100%
margin-bottom: 50rpx;
font-size: 32rpx;
}
/*
.item 使便30rpx
*/
.item {
display: block;
margin-bottom: 30rpx;
margin-bottom: 30rpx;
}
/*
.account 使flex
#f8f8f815rpx使 border-box26rpx
align-items: center input
*/
.account {
display: flex;
background: #f8f8f8;
@ -114,51 +71,28 @@ form-title 类用于设置表单标题的样式宽度占满父元素100%
box-sizing: border-box;
font-size: 26rpx;
align-items: center;
input {
/*
input 20rpx75%
*/
padding-left: 20rpx;
width: 75%;
padding-left: 20rpx;
}
}
/*
button ::after 0!important
*/
button {
&::after {
border: 0 !important;
}
}
/*
operate 使flex
justify-content: space-between align-items: center
*/
.operate {
display: flex;
justify-content: space-between;
align-items: center;
}
/*
to-register 28rpx#00AAFF
*/
.to-register {
font-size: 28rpx;
color: #00AAFF;
font-size: 28rpx;
}
/*
.error .error-text
*/
.error {
.error-text {
display: block;
@ -169,13 +103,7 @@ to-register 类用于设置跳转到注册页面链接(推测,根据命名
margin-top: 10rpx;
font-size: 28rpx;
margin-top: 10rpx;
.warning-icon {
/*
.warning-icon 使
26rpx26rpx#e4313050%使
12rpx
*/
display: inline-block;
color: #fff;
width: 26rpx;

@ -1,183 +1,172 @@
<template>
<!-- 使用view标签创建一个类名为"con"的容器元素作为整个登录页面内容的包裹元素 -->
<view class="con">
<!-- 展示一个图片元素图片来源为项目中静态资源目录下的"logo.png"文件 -->
<image src="@/static/logo.png" />
<!-- 登录表单部分的整体容器类名为"login-form" -->
<!-- 登录 -->
<view class="login-form">
<!-- 根据"errorTips"的值来动态添加"error"类名用于控制账号输入框相关的错误提示显示与否同时基础类名为"item" -->
<view :class="['item',errorTips==1? 'error':'']">
<!-- 账号输入框所在的容器类名为"account"内部包含账号提示文本和输入框元素 -->
<view class="account">
<!-- 显示"账号"提示文本类名为"input-item" -->
<text class="input-item">
账号
</text>
<!-- 账号输入框类型为文本输入框type="text"绑定了自定义数据属性"data-type""account"
设置了占位符样式类和具体的占位符文本同时监听输入事件当输入时会触发"getInputVal"方法 -->
<input type="text"
data-type="account"
placeholder-class="inp-palcehoder"
placeholder="请输入用户名"
@input="getInputVal">
<input
type="text"
data-type="account"
placeholder-class="inp-palcehoder"
placeholder="请输入用户名"
@input="getInputVal"
>
</view>
<!-- "errorTips"等于1时显示这个错误提示文本元素类名为"error-text"包含一个警告图标和具体的提示信息 -->
<view v-if="errorTips==1"
class="error-text">
<view
v-if="errorTips==1"
class="error-text"
>
<text class="warning-icon">
!
</text>
请输入账号
</view>
</view>
<!-- 与账号输入框部分类似根据"errorTips"的值来动态添加"error"类名用于控制密码输入框相关的错误提示显示与否基础类名为"item" -->
<view :class="['item',errorTips==2? 'error':'']">
<view class="account">
<text class="input-item">
密码
</text>
<input type="password"
data-type="password"
placeholder-class="inp-palcehoder"
placeholder="请输入密码"
@input="getInputVal">
<input
type="password"
data-type="password"
placeholder-class="inp-palcehoder"
placeholder="请输入密码"
@input="getInputVal"
>
</view>
<view v-if="errorTips==2"
class="error-text">
<view
v-if="errorTips==2"
class="error-text"
>
<text class="warning-icon">
!
</text>
请输入密码
</view>
</view>
<!-- 操作相关元素的容器类名为"operate"目前里面包含了跳转到注册页面的链接 -->
<view class="operate">
<view class="to-register"
@tap="toRegitser">
<view
class="to-register"
@tap="toRegitser"
>
还没有账号
<text>去注册></text>
</view>
</view>
</view>
</view>
<!-- 包含登录按钮和回到首页按钮的容器 -->
<view>
<!-- 登录按钮类名为"authorized-btn"点击时会触发"login"方法 -->
<button class="authorized-btn"
@tap="login">
登录
</button>
<!-- 回到首页按钮类名为"to-idx-btn"点击时会触发"toIndex"方法 -->
<button class="to-idx-btn"
@tap="toIndex">
回到首页
</button>
</view>
<view>
<button
class="authorized-btn"
@tap="login"
>
登录
</button>
<button
class="to-idx-btn"
@tap="toIndex"
>
回到首页
</button>
</view>
</view>
</template>
<script setup>
// "@/utils/crypto.js""encrypt"
import { encrypt } from '@/utils/crypto.js'
import { encrypt } from '@/utils/crypto.js'
/**
* 生命周期函数--监听页面显示当页面显示时执行以下代码
* 设置页面头部导航栏的标题为"用户登录"
*/
onShow(() => {
//
uni.setNavigationBarTitle({
title: '用户登录'
})
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
//
uni.setNavigationBarTitle({
title: '用户登录'
})
})
// 使Vueref"principal"
const principal = ref('') //
// 使Vueref"errorTips"0
const errorTips = ref(0) //
// "principal""errorTips"0
watch(
() => principal.value,
() => {
errorTips.value = 0
}
)
// 使Vueref"credentials"
const credentials = ref('') //
const principal = ref('') //
const errorTips = ref(0) //
watch(
() => principal.value,
() => {
errorTips.value = 0
}
)
/**
* 输入框的值发生变化时触发的方法用于获取输入框输入的值并根据输入框的类型账号或密码分别存储到对应的响应式数据中
* @param {Object} e - 输入事件对象包含了输入框相关的各种信息如输入的值绑定的数据属性等
*/
const getInputVal = (e) => {
const type = e.currentTarget.dataset.type
if (type == 'account') {
principal.value = e.detail.value
} else if (type == 'password') {
credentials.value = e.detail.value
}
const credentials = ref('') //
/**
* 输入框的值
*/
const getInputVal = (e) => {
const type = e.currentTarget.dataset.type
if (type == 'account') {
principal.value = e.detail.value
} else if (type == 'password') {
credentials.value = e.detail.value
}
}
/**
* 登录按钮点击时触发的方法用于处理登录逻辑
* 首先判断账号和密码是否为空如果为空则设置相应的错误提示如果都不为空则发起登录请求
* 将加密后的密码和账号信息作为请求数据发送到服务器的"/login"接口根据服务器返回结果进行相应处理
* 登录成功后显示提示信息并在1秒后跳转到首页
*/
const login = () => {
if (principal.value.length == 0) {
errorTips.value = 1
} else if (credentials.value.length == 0) {
errorTips.value = 2
} else {
errorTips.value = 0
http.request({
url: '/login',
method: 'post',
data: {
userName: principal.value,
passWord: encrypt(credentials.value)
}
})
.then(({ data }) => {
http.loginSuccess(data, () => {
uni.showToast({
title: '登录成功',
icon: 'none',
complete: () => {
setTimeout(() => {
wx.switchTab({
url: '/pages/index/index'
})
}, 1000)
}
})
/**
* 登录
*/
const login = () => {
if (principal.value.length == 0) {
errorTips.value = 1
} else if (credentials.value.length == 0) {
errorTips.value = 2
} else {
errorTips.value = 0
http.request({
url: '/login',
method: 'post',
data: {
userName: principal.value,
passWord: encrypt(credentials.value)
}
})
.then(({ data }) => {
http.loginSuccess(data, () => {
uni.showToast({
title: '登录成功',
icon: 'none',
complete: () => {
setTimeout(() => {
wx.switchTab({
url: '/pages/index/index'
})
}, 1000)
}
})
})
}
})
}
}
/**
* 跳转到注册页面的方法使用uni-app的导航函数跳转到"/pages/register/register"页面
*/
const toRegitser = () => {
uni.navigateTo({
url: '/pages/register/register'
})
}
/**
* 去注册
*/
const toRegitser = () => {
uni.navigateTo({
url: '/pages/register/register'
})
}
/**
* 回到首页的方法使用微信小程序相关的切换页面函数这里可能是在uni-app中兼容微信小程序的写法切换到"/pages/index/index"页面
*/
const toIndex = () => {
wx.switchTab({
url: '/pages/index/index'
})
}
/**
* 回到首页
*/
const toIndex = () => {
wx.switchTab({
url: '/pages/index/index'
})
}
</script>
<style scoped lang="scss">
// "./accountLogin.scss"
// "scoped"
@import "./accountLogin.scss";
@import "./accountLogin.scss";
</style>

@ -1,391 +1,365 @@
/* 容器样式 */
.container {
width: 100%; /* 设置容器宽度为100% */
background: #f4f4f4; /* 设置背景颜色 */
min-height: calc(100vh - 118rpx); /* 设置最小高度为视窗高度减去底部导航栏高度 */
width: 100%;
background: #f4f4f4;
min-height: calc(100vh - 118rpx);
}
/* 产品列表样式 */
.prod-list {
padding-bottom: 118rpx; /* 底部填充以防止内容被固定底部遮挡 */
width: 100%; /* 设置宽度为100% */
/* 单个产品块样式 */
padding-bottom: 118rpx;
width: 100%;
.prod-block {
background: #fff; /* 背景颜色 */
margin-top: 15rpx; /* 上边距 */
/* 折扣提示样式 */
background: #fff;
margin-top: 15rpx;
.discount-tips {
padding: 20rpx 0 20rpx 20rpx; /* 内边距 */
border-bottom: 2rpx solid #f4f4f4; /* 下边框 */
height: 40rpx; /* 高度 */
line-height: 40rpx; /* 行高 */
/* 文字块样式 */
padding: 20rpx 0 20rpx 20rpx;
border-bottom: 2rpx solid #f4f4f4;
height: 40rpx;
line-height: 40rpx;
.text-block {
padding: 3rpx 5rpx; /* 内边距 */
border-radius: 8rpx; /* 圆角 */
font-size: 22rpx; /* 字体大小 */
color: #eb2444; /* 文字颜色 */
border: 2rpx solid #eb2444; /* 边框 */
padding: 3rpx 5rpx;
border-radius: 8rpx;
font-size: 22rpx;
color: #eb2444;
border: 2rpx solid #eb2444;
}
/* 文字列表样式 */
.text-list {
font-size: 24rpx; /* 字体大小 */
margin-left: 10rpx; /* 左边距 */
font-size: 24rpx;
margin-left: 10rpx;
}
}
}
/* 产品项样式 */
.item {
background: #fff; /* 背景颜色 */
display: flex; /* 使用弹性布局 */
align-items: center; /* 垂直居中对齐 */
padding: 20rpx; /* 内边距 */
/* 产品信息样式 */
background: #fff;
display: flex;
align-items: center;
padding: 20rpx;
.prodinfo {
position: relative; /* 相对定位 */
color: #999; /* 文字颜色 */
width: 100%; /* 宽度 */
/* 底线伪元素 */
position: relative;
color: #999;
width: 100%;
&::after {
content: ''; /* 伪元素内容为空 */
background-color: #f4f4f4; /* 背景颜色 */
left: 0; /* 左偏移 */
height: 1px; /* 高度 */
transform-origin: 50% 100% 0; /* 变换原点 */
bottom: -20rpx; /* 底部偏移 */
position: absolute; /* 绝对定位 */
display: block; /* 显示为块级元素 */
width: 642rpx; /* 宽度 */
padding-left: 20rpx; /* 左内边距 */
content: '';
background-color: #f4f4f4;
left: 0;
height: 1px;
transform-origin: 50% 100% 0;
bottom: -20rpx;
position: absolute;
display: block;
width: 642rpx;
padding-left: 20rpx;
}
/* 最后一个子元素去除底线 */
&:last-child {
.pic {
text-align: center;
width: 180rpx;
height: 180rpx;
line-height: 180rpx;
font-size: 0;
}
}
&:last-child {
.prodinfo {
&::after {
height: 0; /* 高度设为0 */
height: 0;
}
}
/* 图片容器 */
.pic {
text-align: center; /* 文本居中 */
width: 180rpx; /* 宽度 */
height: 180rpx; /* 高度 */
line-height: 180rpx; /* 行高 */
font-size: 0; /* 字体大小 */
}
}
/* 状态样式 */
.staus {
text-align: center; /* 文本居中 */
background: rgb(196, 192, 192); /* 背景颜色 */
font-size: 20rpx; /* 字体大小 */
width: 50rpx; /* 宽度 */
color: #fff; /* 文字颜色 */
text-align: center;
background: rgb(196, 192, 192);
font-size: 20rpx;
width: 50rpx;
color: #fff;
}
/* 操作选项样式 */
.opt {
font-size: 28rpx; /* 字体大小 */
margin-left: 20rpx; /* 左边距 */
width: 100%; /* 宽度 */
font-size: 28rpx;
margin-left: 20rpx;
width: 100%;
}
/* 图片样式 */
.pic {
image {
max-width: 100%; /* 最大宽度 */
max-height: 100%; /* 最大高度 */
vertical-align: middle; /* 垂直对齐 */
max-width: 100%;
max-height: 100%;
vertical-align: middle;
}
}
}
/* 失效产品样式 */
.lose-efficacy {
.discount-tips {
padding: 20rpx 0; /* 内边距 */
border-bottom: 2rpx solid #ddd; /* 下边框 */
height: 50rpx; /* 高度 */
line-height: 50rpx; /* 行高 */
margin-left: 20rpx; /* 左边距 */
/* 文字列表样式 */
padding: 20rpx 0;
border-bottom: 2rpx solid #ddd;
height: 50rpx;
line-height: 50rpx;
margin-left: 20rpx;
.text-list {
font-size: 30rpx; /* 字体大小 */
margin-left: 10rpx; /* 左边距 */
font-size: 30rpx;
margin-left: 10rpx;
}
}
}
}
/* 产品信息样式 */
.prodinfo {
display: flex; /* 使用弹性布局 */
margin-left: 20rpx; /* 左边距 */
/* 操作选项样式 */
display: flex;
margin-left: 20rpx;
.opt {
.prod-name {
color: #333; /* 文字颜色 */
max-height: 72rpx; /* 最大高度 */
line-height: 36rpx; /* 行高 */
display: -webkit-box; /* WebKit行盒模型 */
word-break: break-all; /* 允许单词内断行 */
overflow: hidden; /* 隐藏溢出内容 */
text-overflow: ellipsis; /* 溢出用省略号表示 */
-webkit-line-clamp: 2; /* 限制行数 */
-webkit-box-orient: vertical; /* 竖向排列 */
color: #333;
max-height: 72rpx;
line-height: 36rpx;
display: -webkit-box;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
/* 产品信息文本样式 */
.prod-info-text {
color: #999; /* 文字颜色 */
display: inline-block; /* 显示为行内块 */
-webkit-line-clamp: 1; /* 限制行数 */
height: 48rpx; /* 高度 */
line-height: 48rpx; /* 行高 */
background: #f9f9f9; /* 背景颜色 */
padding: 0 10rpx; /* 内边距 */
border-radius: 4rpx; /* 圆角 */
margin: 10rpx 0 0rpx 0; /* 外边距 */
overflow: hidden; /* 隐藏溢出内容 */
font-size: 24rpx; /* 字体大小 */
position: relative; /* 相对定位 */
font-family: arial; /* 字体系列 */
color: #999;
display: inline-block;
-webkit-line-clamp: 1;
height: 48rpx;
line-height: 48rpx;
background: #f9f9f9;
padding: 0 10rpx 0 10rpx;
border-radius: 4rpx;
margin: 10rpx 0 0rpx 0;
overflow: hidden;
font-size: 24rpx;
position: relative;
font-family: arial;
}
/* 空的产品信息文本样式 */
.prod-info-text.empty-n {
padding: 0; /* 内边距 */
padding: 0;
}
/* 价格和数量样式 */
.price-count {
display: flex; /* 使用弹性布局 */
align-items: center; /* 垂直居中对齐 */
justify-content: space-between; /* 两端对齐 */
/* 价格样式 */
display: flex;
align-items: center;
justify-content: space-between;
.price {
color: #eb2444; /* 文字颜色 */
color: #eb2444;
}
}
}
}
/* 产品信息文本伪元素样式 */
.prod-info-text {
&:before {
border-top: 5px solid #aaa; /* 上边框 */
border-top: 5px solid #aaa;
}
&:after {
border-top: 5px solid #f9f9f9; /* 上边框 */
top: 9px; /* 顶部偏移 */
border-top: 5px solid #f9f9f9;
top: 9px;
}
}
/* 失效产品样式 */
.lose-efficacy {
.prodinfo {
.opt {
.price-count {
.price {
color: #999; /* 文字颜色 */
color: #999;
}
}
}
}
margin-top: 20rpx; /* 上边距 */
background: #fff; /* 背景颜色 */
/* 产品项样式 */
margin-top: 20rpx;
background: #fff;
.item {
background: #f8f8f9; /* 背景颜色 */
background: #f8f8f9;
}
/* 折扣提示样式 */
.discount-tips {
.empty-prod {
color: #777; /* 文字颜色 */
font-size: 26rpx; /* 字体大小 */
border: 2rpx solid #999; /* 边框 */
padding: 0 10rpx; /* 内边距 */
border-radius: 8rpx; /* 圆角 */
float: right; /* 向右浮动 */
margin-right: 20rpx; /* 右边距 */
color: #777;
font-size: 26rpx;
border: 2rpx solid #999;
padding: 0 10rpx;
border-radius: 8rpx;
float: right;
margin-right: 20rpx;
}
}
}
/* 数量选择器样式 */
.m-numSelector {
.minus,
.plus {
float: left; /* 向左浮动 */
box-sizing: border-box; /* 盒模型 */
height: 56rpx; /* 高度 */
border: 2rpx solid #d9d9d9; /* 边框 */
position: relative; /* 相对定位 */
width: 56rpx; /* 宽度 */
&::before,
&::after {
position: absolute; /* 绝对定位 */
.minus {
float: left;
box-sizing: border-box;
height: 56rpx;
border: 2rpx solid #d9d9d9;
position: relative;
width: 56rpx;
border-right: 0;
border-top-left-radius: 4rpx;
border-bottom-left-radius: 4rpx;
&::before {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto; /* 自动外边距 */
content: ' '; /* 伪元素内容为空 */
width: 22rpx; /* 宽度 */
height: 3rpx; /* 高度 */
background-color: #7f7f7f; /* 背景颜色 */
margin: auto;
content: ' ';
width: 22rpx;
height: 3rpx;
background-color: #7f7f7f;
}
}
/* 减号按钮样式 */
.minus {
border-right: 0; /* 右边框 */
border-top-left-radius: 4rpx; /* 左上圆角 */
border-bottom-left-radius: 4rpx; /* 左下圆角 */
}
/* 输入框样式 */
input {
float: left; /* 向左浮动 */
box-sizing: border-box; /* 盒模型 */
height: 56rpx; /* 高度 */
border: 2rpx solid #d9d9d9; /* 边框 */
width: 56rpx; /* 宽度 */
text-align: center; /* 文本居中 */
color: #333; /* 文字颜色 */
float: left;
box-sizing: border-box;
height: 56rpx;
border: 2rpx solid #d9d9d9;
width: 56rpx;
text-align: center;
color: #333;
}
/* 加号按钮样式 */
.plus {
border-left: 0; /* 左边框 */
border-top-right-radius: 4rpx; /* 右上圆角 */
border-bottom-right-radius: 4rpx; /* 右下圆角 */
float: left;
box-sizing: border-box;
height: 56rpx;
border: 2rpx solid #d9d9d9;
position: relative;
width: 56rpx;
border-left: 0;
border-top-right-radius: 4rpx;
border-bottom-right-radius: 4rpx;
&::before {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
content: ' ';
width: 22rpx;
height: 3rpx;
background-color: #7f7f7f;
}
&::after {
transform: rotate(90deg); /* 旋转90度 */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
content: ' ';
width: 22rpx;
height: 3rpx;
background-color: #7f7f7f;
transform: rotate(90deg);
}
}
float: right; /* 向右浮动 */
/* 非禁用状态下的交互样式 */
float: right;
&:not(.disabled) {
.minus,
.minus {
&:not(.disabled) {
&:active {
background-color: #f4f4f4;
}
}
}
.plus {
&:not(.disabled) {
&:active {
background-color: #f4f4f4; /* 激活时背景颜色 */
background-color: #f4f4f4;
}
}
}
}
}
/* 深层选择器用于复选框样式 */
:deep(checkbox) {
.uni-checkbox-input,
.wx-checkbox-input {
border-radius: 50%; /* 圆形 */
width: 35rpx; /* 宽度 */
height: 35rpx; /* 高度 */
border-radius: 50%;
width: 35rpx;
height: 35rpx;
}
/* 选中状态下的复选框样式 */
.wx-checkbox-input.wx-checkbox-input-checked {
background: #eb2444; /* 背景颜色 */
border-color: #eb2444; /* 边框颜色 */
background: #eb2444;
border-color: #eb2444;
&::before {
text-align: center; /* 文本居中 */
font-size: 22rpx; /* 字体大小 */
color: #fff; /* 文字颜色 */
background: transparent; /* 透明背景 */
transform: translate(-50%, -50%) scale(1); /* 居中缩放 */
-webkit-transform: translate(-50%, -50%) scale(1); /* WebKit浏览器居中缩放 */
text-align: center;
font-size: 22rpx;
color: #fff;
background: transparent;
transform: translate(-50%, -50%) scale(1);
-webkit-transform: translate(-50%, -50%) scale(1);
}
}
}
/* 空状态样式 */
.empty {
font-size: 26rpx; /* 字体大小 */
color: #aaa; /* 文字颜色 */
padding-top: 200rpx; /* 上内边距 */
/* 文字样式 */
font-size: 26rpx;
color: #aaa;
padding-top: 200rpx;
.txt {
text-align: center; /* 文本居中 */
margin-top: 30rpx; /* 上边距 */
text-align: center;
margin-top: 30rpx;
}
/* 图片样式 */
.img {
margin-top: 80rpx; /* 上边距 */
text-align: center; /* 文本居中 */
margin-top: 80rpx;
text-align: center;
image {
width: 80rpx; /* 宽度 */
height: 80rpx; /* 高度 */
width: 80rpx;
height: 80rpx;
}
}
}
/* 价格和数量样式 */
.price-count {
.disable-price {
color: #999; /* 文字颜色 */
color: #999;
}
}
/* 购物车底部栏样式 */
.cart-footer {
position: fixed; /* 固定定位 */
bottom: calc(90rpx + env(safe-area-inset-bottom)); /* 底部距离 */
left: 0; /* 左偏移 */
width: 100%; /* 宽度 */
display: flex; /* 使用弹性布局 */
flex-direction: row nowrap; /* 行内不换行 */
height: 98rpx; /* 高度 */
border-top: 2rpx solid #f4f4f4; /* 上边框 */
z-index: 999; /* 层叠顺序 */
/* 按钮样式 */
position: fixed;
bottom: calc(90rpx + env(safe-area-inset-bottom));
left: 0;
width: 100%;
display: flex;
flex-direction: row nowrap;
height: 98rpx;
border-top: 2rpx solid #f4f4f4;
z-index: 999;
.btn {
position: relative; /* 相对定位 */
display: flex; /* 使用弹性布局 */
flex-grow: 1; /* 弹性增长 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
width: 0; /* 宽度 */
background-color: #fafafa; /* 背景颜色 */
background: rgba(255,255,255,0.95); /* 半透明背景 */
font-size: 28rpx; /* 字体大小 */
/* 总金额消息样式 */
position: relative;
display: flex;
flex-grow: 1;
justify-content: center;
align-items: center;
width: 0;
background-color: #fafafa;
background: rgba(255,255,255,0.95);
font-size: 28rpx;
.total-msg {
font-size: 20rpx; /* 字体大小 */
font-size: 20rpx;
}
}
/* 总价按钮样式 */
.btn.total {
display: flex; /* 使用弹性布局 */
flex-flow: column; /* 列方向 */
align-items: flex-start; /* 左对齐 */
width: 300rpx; /* 宽度 */
/* 价格样式 */
display: flex;
flex-flow: column;
align-items: flex-start;
width: 300rpx;
.price {
color: #eb2444; /* 文字颜色 */
font-size: 30rpx; /* 字体大小 */
color: #eb2444;
font-size: 30rpx;
}
}
/* 删除按钮样式 */
.btn.del {
color: #eb2444; /* 文字颜色 */
width: 70rpx; /* 宽度 */
font-size: 22rpx; /* 字体大小 */
text-align: left; /* 文本左对齐 */
display: block; /* 显示为块级元素 */
line-height: 102rpx; /* 行高 */
color: #eb2444;
width: 70rpx;
font-size: 22rpx;
text-align: left;
display: block;
line-height: 102rpx;
}
/* 全选按钮样式 */
.btn.all {
width: 150rpx; /* 宽度 */
font-size: 26rpx; /* 字体大小 */
/* 标签样式 */
width: 150rpx;
font-size: 26rpx;
label {
display: flex; /* 使用弹性布局 */
flex-grow: 1; /* 弹性增长 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
display: flex;
flex-grow: 1;
justify-content: center;
align-items: center;
}
}
/* 结算按钮样式 */
.btn.settle {
width: 200rpx; /* 宽度 */
background: #eb2444; /* 背景颜色 */
color: #fff; /* 文字颜色 */
width: 200rpx;
background: #eb2444;
color: #fff;
}
}

@ -1,86 +1,82 @@
<template>
<!-- 页面的最外层容器用于包裹整个购物车页面内容 -->
<view class="container">
<!-- 商品列表区域用于展示购物车中的商品信息 -->
<view class="prod-list">
<!-- 使用v-for循环遍历shopCartItemDiscounts数组scIndex为当前项的索引每个元素对应一组商品相关的数据可能是不同优惠规则下的商品集合等情况 -->
<block v-for="(item, scIndex) in shopCartItemDiscounts" :key="scIndex">
<block
v-for="(item, scIndex) in shopCartItemDiscounts"
:key="scIndex"
>
<view class="prod-block">
<!-- 如果当前商品组有对应的优惠信息chooseDiscountItemDto存在则展示优惠提示信息 -->
<view v-if="item.chooseDiscountItemDto" class="discount-tips">
<!-- 展示优惠规则文本通过wxs.parseDiscount函数对优惠规则进行解析处理后展示 -->
<view
v-if="item.chooseDiscountItemDto"
class="discount-tips"
>
<text class="text-block">
{{ wxs.parseDiscount(item.chooseDiscountItemDto.discountRule) }}
</text>
<!-- 展示详细的优惠信息文本通过wxs.parseDiscountMsg函数结合优惠规则所需金额优惠金额等数据进行解析展示 -->
<text class="text-list">
{{
wxs.parseDiscountMsg(item.chooseDiscountItemDto.discountRule, item.chooseDiscountItemDto.needAmount, item.chooseDiscountItemDto.discount)
}}
</text>
</view>
<!-- 内层v-for循环遍历当前商品组中的每个具体商品prod代表每个商品对象index为商品在当前组内的索引 -->
<block v-for="(prod, index) in item.shopCartItems" :key="index">
<block
v-for="(prod, index) in item.shopCartItems"
:key="index"
>
<view class="item">
<!-- 商品选择按钮所在的容器 -->
<view class="btn">
<label>
<!-- 单个商品的选择框绑定了一些自定义属性和事件 -->
<checkbox :data-scindex="scIndex" <!-- -->
:data-index="index" <!-- 传递当前商品在组内的索引 -->
:value="prod.prodId" <!-- 绑定商品的唯一标识产品ID -->
:checked="prod.checked" <!-- 绑定商品的选中状态 -->
color="#105c3e" <!-- 设置选择框选中时的颜色 -->
@tap="onSelectedItem" <!-- 绑定点击选择商品时触发的事件 -->
<checkbox
:data-scindex="scIndex"
:data-index="index"
:value="prod.prodId"
:checked="prod.checked"
color="#105c3e"
@tap="onSelectedItem"
/>
</label>
</view>
<!-- 商品详细信息展示区域 -->
<view class="prodinfo">
<!-- 商品图片展示容器 -->
<view class="pic">
<image :src="prod.pic" /> <!-- 通过绑定商品对象中的图片路径属性来展示商品图片 -->
<image :src="prod.pic" />
</view>
<view class="opt">
<!-- 展示商品名称 -->
<view class="prod-name">
{{ prod.prodName }}
</view>
<!-- 展示商品规格信息根据是否有skuName来动态添加类名若没有skuName则添加empty-n类名 -->
<text :class="'prod-info-text ' + (prod.skuName?'':'empty-n')">
{{ prod.skuName }}
</text>
<!-- 商品价格和数量操作区域 -->
<view class="price-count">
<view class="price">
<!-- 价格符号 -->
<text class="symbol">
</text>
<!-- 价格的整数部分通过wxs.parsePrice函数对商品价格进行处理后获取整数部分展示 -->
<text class="big-num">
{{ wxs.parsePrice(prod.price)[0] }}
</text>
<!-- 价格的小数部分通过wxs.parsePrice函数对商品价格进行处理后获取小数部分展示 -->
<text class="small-num">
.{{ wxs.parsePrice(prod.price)[1] }}
</text>
</view>
<view class="m-numSelector">
<!-- 数量减少按钮绑定了点击事件onCountMinus并传递相关索引信息 -->
<view class="minus"
:data-scindex="scIndex"
:data-index="index"
@tap="onCountMinus" />
<!-- 商品数量输入框设置为不可输入disabled仅用于展示当前商品数量 -->
<input type="number"
:value="prod.prodCount"
disabled>
<!-- 数量增加按钮绑定了点击事件onCountPlus并传递相关索引信息 -->
<view class="plus"
:data-scindex="scIndex"
:data-index="index"
@tap="onCountPlus" />
<view
class="minus"
:data-scindex="scIndex"
:data-index="index"
@tap="onCountMinus"
/>
<input
type="number"
:value="prod.prodCount"
disabled
>
<view
class="plus"
:data-scindex="scIndex"
:data-index="index"
@tap="onCountPlus"
/>
</view>
</view>
</view>
@ -91,359 +87,314 @@
</block>
</view>
<!-- 当购物车中没有商品shopCartItemDiscounts数组长度为0展示此区域提示用户购物车为空 -->
<view v-if="!shopCartItemDiscounts.length" class="empty">
<view
v-if="!shopCartItemDiscounts.length"
class="empty"
>
<view class="img">
<image src="@/static/images/tabbar/basket.png" /> <!-- 展示一个代表购物车的图标 -->
<image src="@/static/images/tabbar/basket.png" />
</view>
<view class="txt">
您还没有添加任何商品哦~ <!-- 显示提示文字 -->
您还没有添加任何商品哦~
</view>
</view>
<!-- 底部按钮区域当购物车中有商品shopCartItemDiscounts长度大于0时展示 -->
<view v-if="shopCartItemDiscounts.length>0" class="cart-footer">
<!-- 全选按钮所在容器 -->
<!-- 底部按钮 -->
<view
v-if="shopCartItemDiscounts.length>0"
class="cart-footer"
>
<view class="btn all">
<checkbox :checked="allChecked" <!-- -->
color="#f7d731;" <!-- 设置全选按钮选中时的颜色 -->
@tap="onSelAll" <!-- 绑定全选按钮点击触发的事件 -->
<checkbox
:checked="allChecked"
color="#f7d731;"
@tap="onSelAll"
/>
全选 <!-- 按钮显示文字 -->
全选
</view>
<!-- 删除按钮所在容器点击触发onDelBasket事件 -->
<view class="btn del" @tap="onDelBasket">
<text>删除</text> <!-- 按钮显示文字 -->
<view
class="btn del"
@tap="onDelBasket"
>
<text>删除</text>
</view>
<!-- 合计金额展示按钮所在容器 -->
<view class="btn total">
<view class="finally">
<text>合计:</text> <!-- 显示合计文字 -->
<text>合计:</text>
<view class="price">
<text class="symbol">
</text>
<!-- 最终金额的整数部分通过wxs.parsePrice函数对finalMoney进行处理后展示 -->
<text class="big-num">
{{ wxs.parsePrice(finalMoney)[0] }}
</text>
<!-- 最终金额的小数部分通过wxs.parsePrice函数对finalMoney进行处理后展示 -->
<text class="small-num">
.{{ wxs.parsePrice(finalMoney)[1] }}
</text>
</view>
</view>
<!-- 当有立减金额subtractMoney大于0展示总额和立减金额信息 -->
<view v-if="subtractMoney>0" class="total-msg">
<view
v-if="subtractMoney>0"
class="total-msg"
>
总额:{{ wxs.toPrice(totalMoney) }} 立减:{{ wxs.toPrice(subtractMoney) }}
</view>
</view>
<!-- 结算按钮所在容器点击触发toFirmOrder事件 -->
<view class="btn settle" @tap="toFirmOrder">
<text>结算</text> <!-- 按钮显示文字 -->
<view
class="btn settle"
@tap="toFirmOrder"
>
<text>结算</text>
</view>
</view>
<!-- 结束底部按钮区域 -->
<!-- end 底部按钮 -->
</view>
</template>
<script setup>
// wxsnumber()
const wxs = number()
// -
onShow(() => {
//
loadBasketData()
//
http.getCartCount()
})
// false
const allChecked = ref(false)
//
const shopCartItemDiscounts = ref([])
const wxs = number()
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
loadBasketData()
http.getCartCount() //
})
//
const loadBasketData = () => {
//
uni.showLoading()
// POST
http.request({
url: '/p/shopCart/info',
method: 'POST',
data: {}
})
.then(({ data }) => {
// 0
if (data && data.length > 0) {
//
const shopCartItemDiscountsParam = data[0].shopCartItemDiscounts
//
shopCartItemDiscountsParam.forEach(shopCartItemDiscount => {
// false
shopCartItemDiscount.shopCartItems.forEach(shopCartItem => {
shopCartItem.checked = false
})
const allChecked = ref(false)
const shopCartItemDiscounts = ref([])
const loadBasketData = () => {
uni.showLoading() //
http.request({
url: '/p/shopCart/info',
method: 'POST',
data: {}
})
.then(({ data }) => {
if (data && data.length > 0) {
//
const shopCartItemDiscountsParam = data[0].shopCartItemDiscounts
shopCartItemDiscountsParam.forEach(shopCartItemDiscount => {
shopCartItemDiscount.shopCartItems.forEach(shopCartItem => {
shopCartItem.checked = false
})
// shopCartItemDiscounts
shopCartItemDiscounts.value = shopCartItemDiscountsParam
// false
allChecked.value = false
} else {
// shopCartItemDiscounts
shopCartItemDiscounts.value = []
}
//
calTotalPrice()
//
uni.hideLoading()
})
}
//
const toFirmOrder = () => {
//
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
const basketIds = []
//
shopCartItemDiscountsParam.forEach(shopCartItemDiscount => {
//
shopCartItemDiscount.shopCartItems.forEach(shopCartItem => {
// basketIdbasketIds
if (shopCartItem.checked) {
basketIds.push(shopCartItem.basketId)
}
})
})
shopCartItemDiscounts.value = shopCartItemDiscountsParam
allChecked.value = false
} else {
shopCartItemDiscounts.value = []
}
calTotalPrice() //
uni.hideLoading()
})
}
// basketIds0
if (!basketIds.length) {
// Toastnone
uni.showToast({
title: '请选择商品',
icon: 'none'
})
return
}
/**
* 去结算
*/
const toFirmOrder = () => {
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
const basketIds = []
shopCartItemDiscountsParam.forEach(shopCartItemDiscount => {
shopCartItemDiscount.shopCartItems.forEach(shopCartItem => {
if (shopCartItem.checked) {
basketIds.push(shopCartItem.basketId)
}
})
})
// basketIdsJSON
uni.setStorageSync('basketIds', JSON.stringify(basketIds))
// orderEntry=0
uni.navigateTo({
url: '/pages/submit-order/submit-order?orderEntry=0'
if (!basketIds.length) {
uni.showToast({
title: '请选择商品',
icon: 'none'
})
return
}
//
const onSelAll = () => {
//
const allCheckedParam = !allChecked.value
//
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
//
for (let i = 0; i < shopCartItemDiscountsParam.length; i++) {
const cItems = shopCartItemDiscountsParam[i].shopCartItems
//
for (let j = 0; j < cItems.length; j++) {
cItems[j].checked = allCheckedParam
}
uni.setStorageSync('basketIds', JSON.stringify(basketIds))
uni.navigateTo({
url: '/pages/submit-order/submit-order?orderEntry=0'
})
}
/**
* 全选
*/
const onSelAll = () => {
const allCheckedParam = !allChecked.value //
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
for (let i = 0; i < shopCartItemDiscountsParam.length; i++) {
const cItems = shopCartItemDiscountsParam[i].shopCartItems
for (let j = 0; j < cItems.length; j++) {
cItems[j].checked = allCheckedParam
}
//
allChecked.value = allCheckedParam
// UI
shopCartItemDiscounts.value = shopCartItemDiscountsParam
//
calTotalPrice()
}
allChecked.value = allCheckedParam
shopCartItemDiscounts.value = shopCartItemDiscountsParam
calTotalPrice() //
}
//
const onSelectedItem = (e) => {
//
const index = e.currentTarget.dataset.index
//
const scindex = e.currentTarget.dataset.scindex
//
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
//
const checked = shopCartItemDiscountsParam[scindex].shopCartItems[index].checked
//
shopCartItemDiscountsParam[scindex].shopCartItems[index].checked = !checked
//
shopCartItemDiscounts.value = shopCartItemDiscountsParam
//
checkAllSelected()
//
calTotalPrice()
}
/**
* 每一项的选择事件
* +
*/
const onSelectedItem = (e) => {
const index = e.currentTarget.dataset.index // data- index
const scindex = e.currentTarget.dataset.scindex
const shopCartItemDiscountsParam = shopCartItemDiscounts.value //
const checked = shopCartItemDiscountsParam[scindex].shopCartItems[index].checked //
shopCartItemDiscountsParam[scindex].shopCartItems[index].checked = !checked //
shopCartItemDiscounts.value = shopCartItemDiscountsParam
checkAllSelected() //
calTotalPrice() //
}
//
const checkAllSelected = () => {
// true
let allCheckedParam = true
//
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
let flag = false
//
for (let i = 0; i < shopCartItemDiscountsParam.length; i++) {
const cItems = shopCartItemDiscountsParam[i].shopCartItems
//
for (let j = 0; j < cItems.length; j++) {
//
if (!cItems[j].checked) {
// false
allCheckedParam = !allCheckedParam
flag = true
break
}
/**
* 检查全选状态
*/
const checkAllSelected = () => {
let allCheckedParam = true
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
let flag = false
for (let i = 0; i < shopCartItemDiscountsParam.length; i++) {
const cItems = shopCartItemDiscountsParam[i].shopCartItems
for (let j = 0; j < cItems.length; j++) {
if (!cItems[j].checked) {
allCheckedParam = !allCheckedParam
flag = true
break
}
//
if (flag) break
}
//
allChecked.value = allCheckedParam
if (flag) break
}
allChecked.value = allCheckedParam
}
// 0
const totalMoney = ref(0)
// 0
const subtractMoney = ref(0)
// 0
const finalMoney = ref(0)
//
const calTotalPrice = () => {
//
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
const shopCartIds = []
//
for (let i = 0; i < shopCartItemDiscountsParam.length; i++) {
const cItems = shopCartItemDiscountsParam[i].shopCartItems
//
for (let j = 0; j < cItems.length; j++) {
// basketIdshopCartIds
if (cItems[j].checked) {
shopCartIds.push(cItems[j].basketId)
}
const totalMoney = ref(0)
const subtractMoney = ref(0)
const finalMoney = ref(0)
/**
* 计算购物车总额
*/
const calTotalPrice = () => {
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
const shopCartIds = []
for (let i = 0; i < shopCartItemDiscountsParam.length; i++) {
const cItems = shopCartItemDiscountsParam[i].shopCartItems
for (let j = 0; j < cItems.length; j++) {
if (cItems[j].checked) {
shopCartIds.push(cItems[j].basketId)
}
}
//
uni.showLoading()
// POSTbasketIds
http.request({
url: '/p/shopCart/totalPay',
method: 'POST',
data: shopCartIds
})
.then(({ data }) => {
//
if (!data) return
//
finalMoney.value = data.finalMoney
//
totalMoney.value = data.totalMoney
//
subtractMoney.value = data.subtractMoney
//
uni.hideLoading()
})
}
uni.showLoading()
http.request({
url: '/p/shopCart/totalPay',
method: 'POST',
data: shopCartIds
})
.then(({ data }) => {
if (!data) return
finalMoney.value = data.finalMoney
totalMoney.value = data.totalMoney
subtractMoney.value = data.subtractMoney
uni.hideLoading()
})
}
//
const onCountMinus = (e) => {
//
const index = e.currentTarget.dataset.index
//
const scindex = e.currentTarget.dataset.scindex
//
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
//
const prodCount = shopCartItemDiscountsParam[scindex].shopCartItems[index].prodCount
// 1
if (prodCount > 1) {
updateCount(shopCartItemDiscountsParam, scindex, index, -1)
}
/**
* 减少数量
*/
const onCountMinus = (e) => {
const index = e.currentTarget.dataset.index
const scindex = e.currentTarget.dataset.scindex
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
const prodCount = shopCartItemDiscountsParam[scindex].shopCartItems[index].prodCount
if (prodCount > 1) {
updateCount(shopCartItemDiscountsParam, scindex, index, -1)
}
}
/**
* 增加数量
*/
const onCountPlus = (e) => {
const index = e.currentTarget.dataset.index
const scindex = e.currentTarget.dataset.scindex
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
updateCount(shopCartItemDiscountsParam, scindex, index, 1)
}
/**
* 增加数量
*/
const onCountPlus = (e) => {
const index = e.currentTarget.dataset.index
const scindex = e.currentTarget.dataset.scindex
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
updateCount(shopCartItemDiscountsParam, scindex, index, 1)
}
/**
* 改变购物车数量接口
*/
const updateCount = (shopCartItemDiscountsParam, scindex, index, prodCount) => {
uni.showLoading({
mask: true
})
http.request({
url: '/p/shopCart/changeItem',
method: 'POST',
data: {
count: prodCount,
prodId: shopCartItemDiscountsParam[scindex].shopCartItems[index].prodId,
skuId: shopCartItemDiscountsParam[scindex].shopCartItems[index].skuId,
shopId: 1
}
/**
* 改变购物车数量接口
*/
const updateCount = (shopCartItemDiscountsParam, scindex, index, prodCount) => {
uni.showLoading({
mask: true
})
http.request({
url: '/p/shopCart/changeItem',
method: 'POST',
data: {
count: prodCount,
prodId: shopCartItemDiscountsParam[scindex].shopCartItems[index].prodId,
skuId: shopCartItemDiscountsParam[scindex].shopCartItems[index].skuId,
shopId: 1
}
})
.then(() => {
shopCartItemDiscountsParam[scindex].shopCartItems[index].prodCount += prodCount
shopCartItemDiscounts.value = shopCartItemDiscountsParam
calTotalPrice() //
uni.hideLoading()
http.getCartCount() //
})
.then(() => {
shopCartItemDiscountsParam[scindex].shopCartItems[index].prodCount += prodCount
shopCartItemDiscounts.value = shopCartItemDiscountsParam
calTotalPrice() //
uni.hideLoading()
http.getCartCount() //
})
}
}
/**
* 删除购物车商品
*/
const onDelBasket = () => {
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
const basketIds = []
for (let i = 0; i < shopCartItemDiscountsParam.length; i++) {
const cItems = shopCartItemDiscountsParam[i].shopCartItems
for (let j = 0; j < cItems.length; j++) {
if (cItems[j].checked) {
basketIds.push(cItems[j].basketId)
}
/**
* 删除购物车商品
*/
const onDelBasket = () => {
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
const basketIds = []
for (let i = 0; i < shopCartItemDiscountsParam.length; i++) {
const cItems = shopCartItemDiscountsParam[i].shopCartItems
for (let j = 0; j < cItems.length; j++) {
if (cItems[j].checked) {
basketIds.push(cItems[j].basketId)
}
}
if (!basketIds.length) {
uni.showToast({
title: '请选择商品',
icon: 'none'
})
} else {
uni.showModal({
title: '',
content: '确认要删除选中的商品吗?',
confirmColor: '#eb2444',
success(res) {
if (res.confirm) {
uni.showLoading({
mask: true
})
http.request({
url: '/p/shopCart/deleteItem',
method: 'DELETE',
data: basketIds
}
if (!basketIds.length) {
uni.showToast({
title: '请选择商品',
icon: 'none'
})
} else {
uni.showModal({
title: '',
content: '确认要删除选中的商品吗?',
confirmColor: '#eb2444',
success (res) {
if (res.confirm) {
uni.showLoading({
mask: true
})
http.request({
url: '/p/shopCart/deleteItem',
method: 'DELETE',
data: basketIds
})
.then(() => {
uni.hideLoading()
loadBasketData()
})
.then(() => {
uni.hideLoading()
loadBasketData()
})
}
}
}
})
}
})
}
}
</script>
<style scoped lang="scss">

@ -1,31 +1,14 @@
/*
.container
*/
.container {
display: flex;
flex-direction: row;
height: 100%;
}
/*
.main
使便
*/
.main {
position: fixed;
display: flex;
overflow: hidden;
height: 100%;
}
/*
.search-bar
宽度占满父元素一般是所在的布局区域设置为固定定位使其固定在页面顶部top: 0; left: 0;
#777#fffz-index3
padding
*/
.search-bar {
width: 100%;
position: fixed;
@ -36,11 +19,6 @@
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.07);
z-index: 3;
padding: 20rpx 0;
/*
.arrow search-bar
border-bottom border-left 45
同时设置了绝对定位指定了在父元素search-bar中的具体位置left: 30rpx; top: 41rpx;
*/
.arrow {
width: 20rpx;
height: 20rpx;
@ -51,11 +29,6 @@
left: 30rpx;
top: 41rpx;
}
/*
.search-box 使便
60rpx#f7f7f7z-index 999
92%border-radius使 margin: auto 使search-bar
*/
.search-box {
display: flex;
justify-content: center;
@ -67,18 +40,12 @@
border-radius: 50rpx;
text-align: center;
margin: auto;
/*
.search-img search-box
*/
.search-img {
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
}
}
/*
.search-hint search-barright: 30rpx; top: 32rpx;
*/
.search-hint {
font-size: 28rpx;
position: absolute;
@ -86,18 +53,9 @@
top: 32rpx;
}
}
/*
.sear-input
*/
.sear-input {
font-size: 28rpx;
}
/*
.leftmenu box-sizing#f5f6f7
overflow: scrollz-index 2使
*/
.leftmenu {
width: 200rpx;
height: 100%;
@ -105,9 +63,6 @@
background-color: #f5f6f7;
overflow: scroll;
z-index: 2;
/*
.ca-empty leftmenu
*/
.ca-empty {
padding-top: 400rpx;
text-align: center;
@ -115,11 +70,6 @@
font-size: 24rpx;
}
}
/*
.menu-item
.tips-num
*/
.menu-item {
line-height: 90rpx;
height: 90rpx;
@ -128,7 +78,6 @@
position: relative;
color: #777;
font-size: 28rpx;
text.tips-num {
position: absolute;
top: 20rpx;
@ -142,18 +91,12 @@
line-height: 30rpx;
}
}
/*
.menu-item.active #eb2444#fff
::before
*/
.menu-item.active {
color: #eb2444;
font-size: 28rpx;
font-weight: bold;
position: relative;
background: #fff;
&:before {
position: absolute;
left: 0;
@ -164,48 +107,30 @@
background: #eb2444;
}
}
/*
.rightcontent box-sizingz-index 1
*/
.rightcontent {
width: 550rpx;
height: 100%;
box-sizing: border-box;
background-color: #fff;
z-index: 1;
/*
.adver-map 广auto便
.item-a 100%
*/
.adver-map {
width: auto;
box-sizing: border-box;
overflow: hidden;
position: relative;
margin: 30rpx 20rpx 0;
.item-a {
display: block;
font-size: 0;
width: 100%;
image {
max-width: 100%;
}
}
}
/*
.cont-item 94rpx
*/
.cont-item {
padding: 0 20rpx 20rpx 20rpx;
padding-bottom: 94rpx;
/*
.show-item ::after 线线
*/
.show-item {
.more-prod-pic {
text-align: center;
@ -213,7 +138,6 @@
height: 150rpx;
line-height: 150rpx;
font-size: 0;
.more-pic {
max-width: 100%;
max-height: 100%;
@ -221,12 +145,10 @@
vertical-align: middle;
}
}
position: relative;
display: flex;
justify-content: flex-start;
padding: 20rpx 0;
&::after {
content: '';
background-color: #f4f4f4;
@ -239,13 +161,9 @@
width: 510rpx;
padding-left: 20rpx;
}
.prod-text-right {
margin-left: 20rpx;
width: 75%;
/*
.cate-prod-info -webkit-box
*/
.cate-prod-info {
font-size: 22rpx;
color: #999;
@ -255,9 +173,6 @@
-webkit-line-clamp: 1;
overflow: hidden;
}
/*
.prod-text.more -webkit-box #000
*/
.prod-text.more {
margin: 0;
font-size: 28rpx;
@ -270,9 +185,6 @@
-webkit-box-orient: vertical;
color: #000;
}
/*
.prod-price.more #eb2444arial
*/
.prod-price.more {
font-size: 28rpx;
color: #eb2444;
@ -282,19 +194,10 @@
}
}
}
/*
.th-cate-con 使
*/
.th-cate-con {
display: flex;
flex-wrap: wrap;
}
/*
.sub-category 33.33%使
flex-direction: column
*/
.sub-category {
width: 33.33%;
display: flex;
@ -303,27 +206,17 @@
box-sizing: border-box;
align-items: center;
}
/*
.sub-category-item .more-pic
textword-break: break-word
*/
.sub-category-item {
> .more-pic {
>.more-pic {
width: 120rpx;
height: 120rpx;
padding-bottom: 10rpx;
}
text {
font-size: 25rpx;
word-break: break-word;
}
}
/*
.cont-item.empty display: block
*/
.cont-item.empty {
display: block;
font-size: 24rpx;

@ -2,10 +2,14 @@
<view class="container">
<!-- 头部搜索区 -->
<view class="search-bar">
<view class="search-box"
@tap="toSearchPage">
<image src="@/static/images/icon/search.png"
class="search-img" />
<view
class="search-box"
@tap="toSearchPage"
>
<image
src="@/static/images/icon/search.png"
class="search-img"
/>
<text class="sear-input">
搜索您想要的商品
</text>
@ -14,138 +18,156 @@
<!-- 滚动内容区 -->
<view class="main">
<!-- 左侧菜单start -->
<scroll-view scroll-y="true"
class="leftmenu">
<block v-for="(item, index) in categoryList"
:key="index">
<view :class="'menu-item ' + (selIndex==index?'active':'') + ' '"
:data-index="index"
:data-id="item.categoryId"
@tap="onMenuTab">
<scroll-view
scroll-y="true"
class="leftmenu"
>
<block
v-for="(item, index) in categoryList"
:key="index"
>
<view
:class="'menu-item ' + (selIndex==index?'active':'') + ' '"
:data-index="index"
:data-id="item.categoryId"
@tap="onMenuTab"
>
{{ item.categoryName }}
</view>
</block>
<view v-if="!categoryList ||!categoryList.length"
class="ca-empty">
{{ categoryList && categoryList.length? '该分类下暂无商品' : '暂无商品' }}
<view
v-if="!categoryList || !categoryList.length"
class="ca-empty"
>
{{ categoryList && categoryList.length ? '该分类下暂无商品' : '暂无商品' }}
</view>
</scroll-view>
<!-- 左侧菜单end -->
<!-- 右侧内容start -->
<scroll-view scroll-y="true"
class="rightcontent">
<scroll-view
scroll-y="true"
class="rightcontent"
>
<view class="adver-map">
<view class="item-a">
<image :src="util.checkFileUrl(categoryImg)"
mode="widthFix" />
<image
:src="util.checkFileUrl(categoryImg)"
mode="widthFix"
/>
</view>
</view>
<!-- 子分类 -->
<!-- 这是一个条件渲染的视图元素只有当subCategoryList数组的长度大于0时才会显示 -->
<view v-if="subCategoryList.length"
class="th-cate-con">
<!-- 使用v-for指令循环遍历subCategoryList数组index为当前项的索引thCateItem代表数组中的每个子分类项对象 -->
<block v-for="(thCateItem, index) in subCategoryList"
:key="index">
<!-- 每个子分类的外层视图容器用于包裹子分类相关的内容设置了类名为sub-category -->
<view
v-if="subCategoryList.length"
class="th-cate-con"
>
<block
v-for="(thCateItem, index) in subCategoryList"
:key="index"
>
<view class="sub-category">
<!-- 子分类具体项的视图容器绑定了点击事件toCatePage并且通过自定义属性传递了子分类相关的ID信息 -->
<view class="sub-category-item"
:data-categoryid="thCateItem.categoryId"
:data-parentid="thCateItem.parentId"
@tap="toCatePage">
<!-- 展示子分类对应的图片通过util.checkFileUrl方法对图片路径进行处理可能是检查路径合法性添加域名等操作并设置了类名为more-pic以及图片的显示模式为widthFixwidthFix模式可以让图片宽度自适应容器宽度高度按比例缩放 -->
<image :src="util.checkFileUrl(thCateItem.pic)"
class="more-pic"
mode="widthFix" />
<!-- 展示子分类的名称通过双括号插值表达式绑定子分类项对象中的categoryName属性来显示具体的名称文本 -->
<view
class="sub-category-item"
:data-categoryid="thCateItem.categoryId"
:data-parentid="thCateItem.parentId"
@tap="toCatePage"
>
<image
:src="util.checkFileUrl(thCateItem.pic)"
class="more-pic"
mode="widthFix"
/>
<text>{{ thCateItem.categoryName }}</text>
</view>
</view>
</block>
</view>
<!-- 当subCategoryList数组长度为0时显示这个视图元素用于给用户提示当前分类下暂无子分类的信息 -->
<view v-else
class="cont-item empty">
<view
v-else
class="cont-item empty"
>
该分类下暂无子分类~
</view>
</scroll-view>
<!-- 右侧内容end -->
</view>
</view>
</template>
<script setup>
import util from '@/utils/util.js'
import { ref, onLoad } from 'vue'
import { uni } from '@dcloudio/uni-app'
import { http } from '@/utils/http.js' // http
//
const categoryList = ref([])
//
const subCategoryList = ref([])
//
const categoryImg = ref('')
// ID
const parentId = ref('')
// 0
const selIndex = ref(0)
// -
onLoad(() => {
//
http.request({
url: '/category/categoryInfo',
method: 'GET',
data: {
parentId: ''
}
})
.then(({ data }) => {
categoryImg.value = data[0].pic
categoryList.value = data
getProdList(data[0].categoryId)
parentId.value = categoryList.value[0].categoryId
})
})
<script setup>
import util from '@/utils/util.js'
const categoryList = ref([])
const subCategoryList = ref([])
const categoryImg = ref('')
const parentId = ref('')
/**
* 生命周期函数--监听页面加载
*/
onLoad(() => {
//
http.request({
url: '/category/categoryInfo',
method: 'GET',
data: {
parentId: ''
}
})
.then(({ data }) => {
categoryImg.value = data[0].pic
categoryList.value = data
getProdList(data[0].categoryId)
parentId.value = categoryList.value[0].categoryId
})
})
//
const onMenuTab = (e) => {
const index = e.currentTarget.dataset.index
getProdList(categoryList.value[index].categoryId)
parentId.value = categoryList.value[index].categoryId
categoryImg.value = categoryList.value[index].pic
selIndex.value = index
}
const selIndex = ref(0)
/**
* 分类点击事件
*/
const onMenuTab = (e) => {
const index = e.currentTarget.dataset.index
getProdList(categoryList.value[index].categoryId)
parentId.value = categoryList.value[index].categoryId
categoryImg.value = categoryList.value[index].pic
selIndex.value = index
}
//
const toSearchPage = () => {
uni.navigateTo({
url: '/pages/search-page/search-page'
})
}
/**
* 跳转搜索页
*/
const toSearchPage = () => {
uni.navigateTo({
url: '/pages/search-page/search-page'
})
}
//
const getProdList = (categoryId) => {
//
http.request({
url: '/category/categoryInfo',
method: 'GET',
data: {
parentId: categoryId
}
})
.then(({ data }) => {
subCategoryList.value = data
})
}
const getProdList = (categoryId) => {
//
http.request({
url: '/category/categoryInfo',
method: 'GET',
data: {
parentId: categoryId
}
})
.then(({ data }) => {
subCategoryList.value = data
})
}
//
const toCatePage = (e) => {
const { categoryid } = e.currentTarget.dataset
uni.navigateTo({
url: `/pages/sub-category/sub-category?parentId=${parentId.value}&categoryId=${categoryid}`
})
}
/**
* 跳转子分类商品页面
*/
const toCatePage = (e) => {
const { categoryid } = e.currentTarget.dataset
uni.navigateTo({
url: `/pages/sub-category/sub-category?parentId=${parentId.value}&categoryId=${categoryid}`
})
}
</script>
</script>
<style scoped lang="scss">
@import "./category.scss";
</style>
<style scoped lang="scss">
@import "./category.scss";
</style>

@ -1,53 +1,30 @@
/*
.container
#f4f4f4 2rpx #e9eaec100vh
*/
.container {
background-color: #f4f4f4;
border-top: 2rpx solid #e9eaec;
min-height: 100vh;
}
/*
.main
20rpx 150rpx
*/
.main {
margin-top: 20rpx;
padding-bottom: 150rpx;
}
/*
.address 100%#fff 2rpx #e9eaec 15rpx
*/
.address {
margin-bottom: 15rpx;
width: 100%;
background-color: #fff;
border-bottom: 2rpx solid #e9eaec;
/*
.personal address 便 3rpx 线#e9eaec
*/
.personal {
position: relative;
padding: 20rpx 30rpx;
border-bottom: 3rpx dashed #e9eaec;
/*
.info-tit .name.tel
*/
.info-tit {
.name {
margin-right: 30rpx;
font-size: 32rpx;
display: inline-block;
}
.tel {
font-size: 30rpx;
}
image {
position: absolute;
right: 30rpx;
@ -59,31 +36,21 @@
}
}
}
/*
.select-btn display: flex使便align-items: centerjustify-content: space-between.box
*/
.select-btn {
padding: 15rpx 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
.box {
font-size: 26rpx;
}
}
}
/*
.personal .addr 20rpx.addr-get display: inline-block#999100%word-break: break-word
.personal HTML HTML
*/
.personal {
.addr {
font-size: 26rpx;
margin: 10rpx 0;
margin-top: 20rpx;
.addr-get {
display: inline-block;
color: #999;
@ -92,10 +59,6 @@
}
}
}
/*
.footer position: fixed使bottom: 0100% 100rpx使text-align: center#ffftext#eb2444
*/
.footer {
position: fixed;
bottom: 0;
@ -105,24 +68,15 @@
text-align: center;
background-color: #fff;
box-shadow: 0 -1rpx 8rpx rgba(0, 0, 0, 0.05);
text {
font-size: 32rpx;
color: #eb2444;
}
}
/*
.empty .img.txt
*/
.empty {
/*
.img 130rpx使imagedisplay: block margin: auto
*/
.img {
text-align: center;
margin-top: 130rpx;
image {
width: 100rpx;
height: 100rpx;
@ -130,9 +84,6 @@
margin: auto;
}
}
/*
.txt 30rpx#999
*/
.txt {
margin-top: 30rpx;
font-size: 24rpx;

@ -1,63 +1,59 @@
<template>
<!-- 页面的最外层容器用于包裹整个页面内容 -->
<view class="container">
<!-- 页面主体内容区域 -->
<view class="main">
<!-- 当地址列表addressList长度为0时显示此区域用于提示用户还没有收货地址 -->
<view v-if="addressList.length===0"
class="empty">
<view
v-if="addressList.length===0"
class="empty"
>
<view class="img">
<!-- 展示一个表示无收货地址的图片 -->
<image src="http://jiales.gz-yami.com/addr.png" />
</view>
<view class="txt">
您还没有收货地址
</view>
</view>
<!-- radio-group 组件用于创建一组单选按钮这里用于管理收货地址相关的单选操作比如设置默认地址等情况 -->
<radio-group class="radio-group">
<!-- 使用v-for指令循环遍历addressList数组index为当前项的索引每个元素对应一个收货地址信息 -->
<block v-for="(item, index) in addressList"
:key="index">
<!-- 每个收货地址的整体视图容器 -->
<block
v-for="(item, index) in addressList"
:key="index"
>
<view class="address">
<!-- 个人信息及地址详情展示区域点击可触发选择该地址用于下单的操作通过@tap绑定selAddrToOrder方法 -->
<view class="personal"
@tap="selAddrToOrder(item)">
<!-- 地址信息标题部分包含收件人姓名电话号码以及修改地址的图标 -->
<view
class="personal"
@tap="selAddrToOrder(item)"
>
<view class="info-tit">
<!-- 展示收件人姓名通过双括号插值表达式绑定地址对象中的receiver属性 -->
<text class="name">
{{ item.receiver }}
</text>
<!-- 展示收件人电话号码通过双括号插值表达式绑定地址对象中的mobile属性 -->
<text class="tel">
{{ item.mobile }}
</text>
<!-- 修改地址的图标通过指定的路径引入图片资源绑定了点击事件toEditAddress并传递当前地址的ID通过自定义属性data-addrid@tap.stop用于阻止事件冒泡 -->
<image src="@/static/images/icon/revise.png"
:data-addrid="item.addrId"
@tap.stop="toEditAddress" />
<image
src="@/static/images/icon/revise.png"
:data-addrid="item.addrId"
@tap.stop="toEditAddress"
/>
</view>
<!-- 详细地址展示区域 -->
<view class="addr">
<!-- 展示完整的地址信息通过拼接地址对象中的省份province城市city区域area和详细地址addr属性来显示 -->
<text class="addr-get">
{{ item.province }}{{ item.city }}{{ item.area }}{{ item.addr }}
</text>
</view>
</view>
<!-- 设置默认地址相关的操作区域绑定了点击事件onDefaultAddr并传递当前地址的ID通过自定义属性data-addrid -->
<view class="select-btn"
:data-addrid="item.addrId"
@tap="onDefaultAddr">
<view
class="select-btn"
:data-addrid="item.addrId"
@tap="onDefaultAddr"
>
<view class="box">
<!-- 单选按钮用于设置默认地址操作绑定了一些属性 -->
<radio :value="item.prodId"
:checked="item.commonAddr==1"
color="#eb2444"
:data-addrid="item.addrId"
@tap="onDefaultAddr" />
<radio
:value="item.prodId"
:checked="item.commonAddr==1"
color="#eb2444"
:data-addrid="item.addrId"
@tap="onDefaultAddr"
/>
设为默认地址
</view>
</view>
@ -65,95 +61,100 @@
</block>
</radio-group>
</view>
<!-- 页面底部的按钮区域点击可触发新增收货地址的操作通过@tap绑定onAddAddr方法 -->
<view class="footer"
@tap="onAddAddr">
<view
class="footer"
@tap="onAddAddr"
>
<text>新增收货地址</text>
</view>
</view>
</template>
<script setup>
// 使reforder -1
const order = ref(-1)
// - optionorderorder
onLoad((option) => {
if (option.order) {
order.value = option.order
}
})
const order = ref(-1)
onLoad((option) => {
if (option.order) {
order.value = option.order
}
})
// 使refaddressList
const addressList = ref([])
const addressList = ref([])
/**
* 加载地址列表
*/
onShow(() => {
onGetList()
})
// - onGetList
onShow(() => {
onGetList()
/**
* 获取收获列表接口
*/
const onGetList = () => {
uni.showLoading()
http.request({
url: '/p/address/list',
method: 'GET'
})
// HTTP/p/address/list
const onGetList = () => {
//
uni.showLoading()
http.request({
url: '/p/address/list',
method: 'GET'
.then(({ data }) => {
addressList.value = data
uni.hideLoading()
})
.then(({ data }) => {
// addressList
addressList.value = data
//
uni.hideLoading()
})
}
}
// uni.navigateTo/pages/editAddress/editAddress
const onAddAddr = () => {
uni.navigateTo({
url: '/pages/editAddress/editAddress'
})
}
/**
* 新增收货地址
*/
const onAddAddr = () => {
uni.navigateTo({
url: '/pages/editAddress/editAddress'
})
}
// IDedata-addridHTTP PUT/p/address/defaultAddr/ + addrIdID
const onDefaultAddr = (e) => {
const addrId = e.currentTarget.dataset.addrid
uni.showLoading()
http.request({
url: '/p/address/defaultAddr/' + addrId,
method: 'PUT',
data: {
addrId
}
/**
* 设置为默认地址
*/
const onDefaultAddr = (e) => {
const addrId = e.currentTarget.dataset.addrid
uni.showLoading()
http.request({
url: '/p/address/defaultAddr/' + addrId,
method: 'PUT',
data: {
addrId
}
})
.then(() => {
uni.hideLoading()
})
.then(() => {
uni.hideLoading()
})
}
}
// IDedata-addriduni.navigateTo/pages/editAddress/editAddressID便
const toEditAddress = (e) => {
const addrId = e.currentTarget.dataset.addrid
uni.navigateTo({
url: '/pages/editAddress/editAddress?addrId=' + addrId
})
}
/**
* 修改地址
*/
const toEditAddress = (e) => {
const addrId = e.currentTarget.dataset.addrid
uni.navigateTo({
url: '/pages/editAddress/editAddress?addrId=' + addrId
})
}
// order.value0getCurrentPagesprevPageitemitemselAddressuni.navigateBackdelta: 1使
const selAddrToOrder = (item) => {
if (order.value == 0) {
const pages = getCurrentPages() //
const prevPage = pages[pages.length - 2] //
prevPage.item = item //
prevPage.selAddress = 'yes'
//
uni.navigateBack({
delta: 1
})
}
/**
* 选择地址 跳转回提交订单页
*/
const selAddrToOrder = (item) => {
if (order.value == 0) {
const pages = getCurrentPages() //
const prevPage = pages[pages.length - 2] //
prevPage.item = item //
prevPage.selAddress = 'yes'
//
uni.navigateBack({
delta: 1
})
}
}
</script>
<style scoped lang="scss">
// SCSSdelivery-address.scssscoped使
@use './delivery-address.scss';
@use './delivery-address.scss';
</style>

@ -1,22 +1,10 @@
/*
.container #fff
*/
.container {
background: #fff;
}
/*
.input-box 50rpx#fff 20rpx
.section
*/
.input-box {
margin-bottom: 50rpx;
background: #fff;
padding: 0 20rpx;
/*
.section display: flex使便align-items: center100%box-sizing 2rpx #e5e5e5
textinputpicker.pca.arrow
*/
.section {
display: flex;
align-items: center;
@ -27,42 +15,26 @@
height: 100%;
box-sizing: border-box;
border-bottom: 2rpx solid #e5e5e5;
/*
text 20%#333
*/
text {
width: 20%;
color: #333;
}
/*
input 70% 20rpx#333
*/
input {
width: 70%;
padding: 0 20rpx;
color: #333;
}
/*
picker 70% 30rpx
*/
picker {
width: 70%;
padding: 0 30rpx;
}
/*
.pca 70% 20rpx
*/
.pca {
width: 70%;
padding: 0 20rpx;
}
/*
.arrow 28rpximage100%vertical-align: top
*/
.arrow {
width: 28rpx;
height: 28rpx;
image {
width: 100%;
height: 100%;
@ -71,23 +43,14 @@
}
}
}
/*
.btn-box 5px 10px100% margin: auto 使
.clear.btn.keep
*/
.btn-box {
padding: 5px 10px;
width: 100%;
text-align: center;
margin: auto;
text {
font-size: 30rpx;
}
/*
.clear.btn 60% 80rpx line-height 使margin: auto#eb2444#f8f0f1b6
*/
.clear.btn {
width: 60%;
height: 80rpx;
@ -101,18 +64,11 @@
color: #eb2444;
background-color: #f8f0f1b6;
}
/*
.keep #fff#eb2444.keep.btn 使
*/
.keep {
color: #fff;
background-color: #eb2444;
}
}
/*
.keep.btn .keep 60% 80rpx line-height 使margin: auto使便
*/
.keep.btn {
width: 60%;
height: 80rpx;
@ -123,20 +79,12 @@
border-radius: 50rpx;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(255, 255, 255, 0.3);
}
/*
.infoText 20rpx100%justify-content: center使 flex HTML
*/
.infoText {
margin-top: 20rpx;
text-align: center;
width: 100%;
justify-content: center;
}
/*
picker-view whitepadding: 0100% 380rpxposition: fixed使bottom: 0text#999inline-flex
*/
picker-view {
background-color: white;
padding: 0;
@ -144,7 +92,6 @@ picker-view {
height: 380rpx;
bottom: 0;
position: fixed;
text {
color: #999;
display: inline-flex;
@ -157,10 +104,6 @@ picker-view {
font-family: Arial, Helvetica, sans-serif;
}
}
/*
picker-view-column view vertical-align: middle100%使便display: flexalign-items: centerjustify-content: center
*/
picker-view-column {
view {
vertical-align: middle;
@ -172,10 +115,6 @@ picker-view-column {
justify-content: center;
}
}
/*
.animation-element-wrapper display: flexposition: fixed使left: 0top: 0width: 100%height: 100%rgba(0, 0, 0, 0.6)z-index 999
*/
.animation-element-wrapper {
display: flex;
position: fixed;
@ -186,10 +125,6 @@ picker-view-column {
background-color: rgba(0, 0, 0, 0.6);
z-index: 999;
}
/*
.animation-element 100% 470rpx使bottom: 0#fff JavaScript
*/
.animation-element {
display: flex;
position: fixed;
@ -198,37 +133,21 @@ picker-view-column {
bottom: 0;
background-color: rgba(255, 255, 255, 1);
}
/*
.animation-button 20rpx 290rpx 100rpxalign-items: center.left-bt.right-bt
*/
.animation-button {
top: 20rpx;
width: 290rpx;
height: 100rpx;
align-items: center;
}
/*
.left-bt left 30rpx使.animation-element
*/
.left-bt {
left: 30rpx;
}
/*
.right-bt right 20rpx top 20rpx使position: absolute 80rpx!important
*/
.right-bt {
right: 20rpx;
top: 20rpx;
position: absolute;
width: 80rpx !important;
}
/*
.line 线display: block使 89rpx 2rpx100%#eee
*/
.line {
display: block;
position: fixed;

@ -1,77 +1,82 @@
<template>
<!-- 页面的最外层容器用于包裹整个页面内容 -->
<view class="container">
<!-- input列表区域用于展示多个输入项相关的视图 -->
<!--input列表 -->
<view class="input-box">
<!-- 每个输入项的整体视图容器这里用于展示收货人相关的输入项 -->
<view class="section">
<!-- 输入项的文本标签显示 字样用于提示用户此处应输入的内容 -->
<text> </text>
<!-- 文本输入框用于用户输入收货人姓名设置了占位符提示placeholder姓名输入类型为文本type="text"最大输入长度为15个字符maxlength="15"通过 :value 绑定了名为 receiver 的响应式变量来显示和更新输入框中的值并且绑定了 @input 事件onReceiverInput当用户输入内容时会触发该事件进行相应处理 -->
<input placeholder="姓名"
type="text"
maxlength="15"
:value="receiver"
@input="onReceiverInput">
<input
placeholder="姓名"
type="text"
maxlength="15"
:value="receiver"
@input="onReceiverInput"
>
</view>
<!-- 用于展示手机号码相关的输入项的视图容器 -->
<view class="section">
<!-- 输入项的文本标签显示手机号码字样提示用户此处应输入手机号码 -->
<text>手机号码</text>
<!-- 文本输入框用于用户输入手机号码设置了占位符提示为11位手机号码输入类型为数字type="number"最大输入长度为11位对应手机号码的长度要求通过 :value 绑定了名为 mobile 的响应式变量来显示和更新输入框中的值同时绑定了 @input 事件onMobileInput用于在输入时进行相关逻辑处理 -->
<input placeholder="11位手机号码"
type="number"
maxlength="11"
:value="mobile"
@input="onMobileInput">
<input
placeholder="11位手机号码"
type="number"
maxlength="11"
:value="mobile"
@input="onMobileInput"
>
</view>
<!-- 用于展示所在地区相关的输入项及操作的视图容器绑定了点击事件 translate点击可触发地区选择相关的操作如弹出地区选择器等 -->
<view class="section"
@tap="translate">
<!-- 输入项的文本标签显示所在地区字样提示用户此处应选择所在地区信息 -->
<view
class="section"
@tap="translate"
>
<text>所在地区</text>
<!-- 用于展示已选择的地区信息省份城市区县的视图元素通过双括号插值表达式绑定对应的响应式变量provincecityarea来显示具体的地区名称 -->
<view class="pca">
{{ province }} {{ city }} {{ area }}
</view>
<!-- 这是一个用于实现地区选择器弹出效果的外层包装元素通过绑定 :animation 属性值为 animation 响应式变量来应用相关动画效果通过 :style 动态控制其可见性根据 show 响应式变量的值显示或隐藏并且绑定了 @tap.stop 事件hiddenFloatView点击该区域除了内部特定元素外可触发隐藏地区选择器浮层的操作阻止事件冒泡 -->
<view class="animation-element-wrapper"
:animation="animation"
:style="'visibility:' + (show? 'visible':'hidden')"
@tap.stop="hiddenFloatView">
<!-- 地区选择器浮层的主体元素设置了 @tap.stop 事件nono可能用于阻止一些默认的点击行为具体功能要结合代码逻辑看内部包含确定按钮分割线以及 picker-view 组件用于实际的地区选择操作 -->
<view class="animation-element"
@tap.stop="nono">
<!-- 右侧的确定按钮设置了类名为 right-bt绑定了 @tap.stop 事件hiddenFloatView点击可触发隐藏地区选择器浮层的操作显示确定字样用于用户完成地区选择后确认操作 -->
<text class="right-bt"
@tap.stop="hiddenFloatView">
<view
class="animation-element-wrapper"
:animation="animation"
:style="'visibility:' + (show ? 'visible':'hidden')"
@tap.stop="hiddenFloatView"
>
<view
class="animation-element"
@tap.stop="nono"
>
<text
class="right-bt"
@tap.stop="hiddenFloatView"
>
确定
</text>
<!-- 水平分割线元素用于在视觉上区分不同区域可能是按钮与选择器部分的区分等 -->
<view class="line" />
<!-- picker-view 组件用于实现多级联动的地区选择功能设置了指示器的样式indicator-style通过 :value 绑定 valArr 响应式变量来记录当前选择的值绑定了 @change 事件bindChange当选择的值发生变化时会触发该事件进行相应的数据更新等操作同时绑定了 @tap.stop 事件nono阻止一些默认点击行为 -->
<picker-view indicator-style="height: 50rpx;"
:value="valArr"
@change="bindChange"
@tap.stop="nono">
<!-- 省选择列的视图容器内部通过 v-for 指令循环遍历 provArray 数组该数组存储省份相关数据每个元素对应一个省份选项展示省份名称 -->
<picker-view
indicator-style="height: 50rpx;"
:value="valArr"
@change="bindChange"
@tap.stop="nono"
>
<!---->
<picker-view-column>
<view v-for="(item, indexs) in provArray"
:key="indexs">
<view
v-for="(item, indexs) in provArray"
:key="indexs"
>
{{ item.areaName }}
</view>
</picker-view-column>
<!-- 地级市选择列的视图容器类似省选择列通过 v-for 指令循环遍历 cityArray 数组存储地级市相关数据展示地级市名称 -->
<!--地级市-->
<picker-view-column>
<view v-for="(item, indexss) in cityArray"
:key="indexss">
<view
v-for="(item, indexss) in cityArray"
:key="indexss"
>
{{ item.areaName }}
</view>
</picker-view-column>
<!-- 区县选择列的视图容器通过 v-for 指令循环遍历 areaArray 数组存储区县相关数据展示区县名称 -->
<!--区县-->
<picker-view-column>
<view v-for="(item, indexsss) in areaArray"
:key="indexsss">
<view
v-for="(item, indexsss) in areaArray"
:key="indexsss"
>
{{ item.areaName }}
</view>
</picker-view-column>
@ -79,343 +84,350 @@
</view>
</view>
<!-- 箭头图标元素用于提示用户可点击展开地区选择器等操作内部通过指定的路径引入图片资源一般是一个向右的箭头图标 -->
<view class="arrow">
<image src="@/static/images/icon/more.png" />
</view>
</view>
<!-- 用于展示详细地址相关的输入项的视图容器 -->
<view class="section">
<!-- 输入项的文本标签显示详细地址字样提示用户此处应输入详细的收货地址信息 -->
<text>详细地址</text>
<!-- 文本输入框用于用户输入详细地址设置了占位符提示为如楼号/单元/门牌号通过 :value 绑定了名为 addr 的响应式变量来显示和更新输入框中的值并且绑定了 @input 事件onAddrInput用于在输入时进行相关逻辑处理 -->
<input placeholder="如楼号/单元/门牌号"
type="text"
:value="addr"
@input="onAddrInput">
<input
placeholder="如楼号/单元/门牌号"
type="text"
:value="addr"
@input="onAddrInput"
>
</view>
</view>
<!-- input列表区域结束 -->
<!-- 功能按钮区域包含保存地址删除地址等功能按钮 -->
<!-- end input列表 -->
<!-- 功能按钮 -->
<view class="btn-box">
<!-- 保存收货地址按钮设置了类名为 keep btn绑定了点击事件 onSaveAddr点击可触发保存当前输入的收货地址信息的操作 -->
<view class="keep btn"
@tap="onSaveAddr">
<view
class="keep btn"
@tap="onSaveAddr"
>
<text>保存收货地址</text>
</view>
<!-- 条件渲染的删除收货地址按钮只有当 addrId 不等于0时才显示可能表示当前是在编辑已有地址的场景下才显示删除按钮设置了类名为 clear btn绑定了点击事件 onDeleteAddr点击可触发删除当前收货地址的操作 -->
<view v-if="addrId!=0"
class="clear btn"
@tap="onDeleteAddr">
<view
v-if="addrId!=0"
class="clear btn"
@tap="onDeleteAddr"
>
<text>删除收货地址</text>
</view>
</view>
<!-- 功能按钮区域结束 -->
<!-- end 功能按钮 -->
</view>
</template>
<script setup>
//
const addrId = ref(0)
const city = ref('')
const area = ref('')
const provinceId = ref(0)
const cityId = ref(0)
const areaId = ref(0)
const receiver = ref('')
const mobile = ref('')
const addr = ref('')
const province = ref('')
//
onLoad((options) => {
if (options.addrId) { // ID
uni.showLoading() //
http.request({
url: '/p/address/addrInfo/' + options.addrId,
method: 'GET'
})
.then(({ data }) => {
//
province.value = data.province
city.value = data.city
area.value = data.area
provinceId.value = data.provinceId
cityId.value = data.cityId
areaId.value = data.areaId
receiver.value = data.receiver
mobile.value = data.mobile
addr.value = data.addr
addrId.value = options.addrId
initCityData(data.provinceId, data.cityId, data.areaId) //
uni.hideLoading() //
})
} else {
initCityData(provinceId.value, cityId.value, areaId.value) //
}
})
//
const provArray = ref([])
const valArr = ref([0, 0, 0])
//
const initCityData = (provinceId, cityId, areaId) => {
const addrId = ref(0)
const city = ref('')
const area = ref('')
const provinceId = ref(0)
const cityId = ref(0)
const areaId = ref(0)
const receiver = ref('')
const mobile = ref('')
const addr = ref('')
const province = ref('')
onLoad((options) => {
if (options.addrId) {
uni.showLoading()
http.request({
url: '/p/area/listByPid',
method: 'GET',
data: {
pid: 0 //
}
url: '/p/address/addrInfo/' + options.addrId,
method: 'GET'
})
.then(({ data }) => {
provArray.value = data //
if (provinceId) {
for (const index in data) {
if (data[index].areaId === provinceId) {
valArr.value = [parseInt(index), valArr.value[1], valArr.value[2]] //
}
}
}
getCityArray(provinceId || data[0].areaId, cityId, areaId) // ID
province.value = data.province
city.value = data.city
area.value = data.area
provinceId.value = data.provinceId
cityId.value = data.cityId
areaId.value = data.areaId
receiver.value = data.receiver
mobile.value = data.mobile
addr.value = data.addr
addrId.value = options.addrId
initCityData(data.provinceId, data.cityId, data.areaId)
uni.hideLoading()
})
} else {
initCityData(provinceId.value, cityId.value, areaId.value)
}
})
//
let indexArr = [18, 0, 0]
const areaArray = ref([])
const cityArray = ref([])
//
const bindChange = (e) => {
const val = e.detail.value //
if (indexArr[0] != val[0]) { //
val[1] = 0
val[2] = 0 //
getCityArray(provArray.value[val[0]].areaId) //
} else if (indexArr[1] != val[1]) { //
val[2] = 0 //
getAreaArray(cityArray.value[val[1]].areaId) //
const provArray = ref([])
const valArr = ref([0, 0, 0])
const initCityData = (provinceId, cityId, areaId) => {
uni.showLoading()
http.request({
url: '/p/area/listByPid',
method: 'GET',
data: {
pid: 0
}
indexArr = val
valArr.value = [val[0], val[1], val[2]]
// ID
province.value = provArray.value[valArr.value[0]].areaName
city.value = cityArray.value[valArr.value[1]].areaName
area.value = areaArray.value[valArr.value[2]].areaName
provinceId.value = provArray.value[valArr.value[0]].areaId
cityId.value = cityArray.value[valArr.value[1]].areaId
areaId.value = areaArray.value[valArr.value[2]].areaId
}
// /
let t = 0
let moveY = 200
const show = ref('')
})
.then(({ data }) => {
provArray.value = data
if (provinceId) {
for (const index in data) {
if (data[index].areaId === provinceId) {
valArr.value = [parseInt(index), valArr.value[1], valArr.value[2]]
}
}
}
getCityArray(provinceId || data[0].areaId, cityId, areaId)
uni.hideLoading()
})
}
const translate = () => {
if (t == 0) {
moveY = 0
show.value = true
t = 1
} else {
moveY = 200
show.value = false
t = 0
let indexArr = [18, 0, 0]
const areaArray = ref([])
const cityArray = ref([])
/**
* 滑动事件
*/
const bindChange = (e) => {
// column
const val = e.detail.value
// column
if (indexArr[0] != val[0]) {
val[1] = 0
val[2] = 0 //
//
getCityArray(provArray.value[val[0]].areaId)
} else {
// column
if (indexArr[1] != val[1]) {
val[2] = 0 //
getAreaArray(cityArray.value[val[1]].areaId) //
}
animationEvents(moveY, show.value)
}
indexArr = val
valArr.value = [val[0], val[1], val[2]]
province.value = provArray.value[valArr.value[0]].areaName
city.value = cityArray.value[valArr.value[1]].areaName
area.value = areaArray.value[valArr.value[2]].areaName
provinceId.value = provArray.value[valArr.value[0]].areaId
cityId.value = cityArray.value[valArr.value[1]].areaId
areaId.value = areaArray.value[valArr.value[2]].areaId
}
//
const hiddenFloatView = () => {
let t = 0
let moveY = 200
const show = ref('')
/**
* 移动按钮点击事件
*/
const translate = () => {
if (t == 0) {
moveY = 0
show.value = true
t = 1
} else {
moveY = 200
show.value = false
t = 0
animationEvents(moveY, show.value)
}
animationEvents(moveY, show.value)
}
const animation = ref('')
//
const animationEvents = (moveY, showParam) => {
animation.value = uni.createAnimation({
transformOrigin: '50% 50%',
duration: 400,
timingFunction: 'ease',
delay: 0
})
animation.value.translateY(moveY + 'vh').step()
animation.value = animation.value.export()
show.value = showParam
}
/**
* 隐藏弹窗浮层
*/
const hiddenFloatView = () => {
moveY = 200
show.value = false
t = 0
animationEvents(moveY, show.value)
}
// ID
const getCityArray = (provinceId, cityId, areaId) => {
http.request({
url: '/p/area/listByPid',
method: 'GET',
data: {
pid: provinceId // IDID
}
})
.then(({ data }) => {
cityArray.value = data //
if (cityId) {
for (const index in data) {
if (data[index].areaId == cityId) {
valArr.value = [valArr.value[0], parseInt(index), valArr.value[2]] //
}
}
}
getAreaArray(cityId || data[0].areaId, areaId) // ID
uni.hideLoading()
})
}
const animation = ref('')
/**
* 动画事件
*/
const animationEvents = (moveY, showParam) => {
animation.value = uni.createAnimation({
transformOrigin: '50% 50%',
duration: 400,
timingFunction: 'ease',
delay: 0
})
animation.value.translateY(moveY + 'vh').step()
animation.value = animation.value.export()
show.value = showParam
}
// ID
const getAreaArray = (cityId, areaId) => {
http.request({
url: '/p/area/listByPid',
method: 'GET',
data: {
pid: cityId // IDID
}
}).then(({ data }) => {
areaArray.value = data //
if (areaId) {
for (const _index in data) {
if (data[_index].areaId == areaId) {
valArr.value = [valArr.value[0], valArr.value[1], parseInt(_index)] //
/**
* 根据省份ID获取 城市数据
*/
const getCityArray = (provinceId, cityId, areaId) => {
http.request({
url: '/p/area/listByPid',
method: 'GET',
data: {
pid: provinceId
}
})
.then(({ data }) => {
cityArray.value = data
if (cityId) {
for (const index in data) {
if (data[index].areaId == cityId) {
valArr.value = [valArr.value[0], parseInt(index), valArr.value[2]]
}
}
indexArr = valArr.value
} else {
// ID
province.value = provArray.value[valArr.value[0]].areaName
city.value = cityArray.value[valArr.value[1]].areaName
area.value = areaArray.value[valArr.value[2]].areaName
provinceId.value = provArray.value[valArr.value[0]].areaId
cityId.value = cityArray.value[valArr.value[1]].areaId
areaId.value = areaArray.value[valArr.value[2]].areaId
}
getAreaArray(cityId || data[0].areaId, areaId)
uni.hideLoading()
})
}
}
//
const onSaveAddr = () => {
const receiverParam = receiver.value
const mobileParam = mobile.value
const addrParam = addr.value
//
if (!receiverParam.trim()) {
receiver.value = ''
uni.showToast({
title: '请输入收货人姓名',
icon: 'none'
})
return
/**
* 根据城市ID获取 区数据
*/
const getAreaArray = (cityId, areaId) => {
http.request({
url: '/p/area/listByPid',
method: 'GET',
data: {
pid: cityId
}
if (!mobileParam) {
uni.showToast({
title: '请输入手机号码',
icon: 'none'
})
return
}
if (mobileParam.length != 11) {
uni.showToast({
title: '请输入正确的手机号码',
icon: 'none'
})
return
}
if (!addrParam.trim()) {
receiver.value = ''
uni.showToast({
title: '请输入详细地址',
icon: 'none'
})
return
}).then(({ data }) => {
areaArray.value = data
if (areaId) {
for (const _index in data) {
if (data[_index].areaId == areaId) {
valArr.value = [valArr.value[0], valArr.value[1], parseInt(_index)]
}
}
indexArr = valArr.value
} else {
province.value = provArray.value[valArr.value[0]].areaName
city.value = cityArray.value[valArr.value[1]].areaName
area.value = areaArray.value[valArr.value[2]].areaName
provinceId.value = provArray.value[valArr.value[0]].areaId
cityId.value = cityArray.value[valArr.value[1]].areaId
areaId.value = areaArray.value[valArr.value[2]].areaId
}
uni.hideLoading()
})
}
uni.showLoading()
let url = '/p/address/addAddr'
let method = 'POST'
/**
* 保存地址
*/
const onSaveAddr = () => {
const receiverParam = receiver.value
const mobileParam = mobile.value
const addrParam = addr.value
if (addrId.value != 0) { // ID
url = '/p/address/updateAddr'
method = 'PUT'
}
http.request({
url,
method,
data: {
receiver: receiver.value,
mobile: mobile.value,
addr: addr.value,
province: province.value,
provinceId: provinceId.value,
city: city.value,
cityId: cityId.value,
areaId: areaId.value,
area: area.value,
userType: 0,
addrId: addrId.value
}
if (!receiverParam.trim()) {
receiver.value = ''
uni.showToast({
title: '请输入收货人姓名',
icon: 'none'
})
.then(() => {
uni.hideLoading()
uni.navigateBack({
delta: 1 //
})
})
return
}
//
const onReceiverInput = (e) => {
receiver.value = e.detail.value
if (!mobileParam) {
uni.showToast({
title: '请输入手机号码',
icon: 'none'
})
return
}
const onMobileInput = (e) => {
mobile.value = e.detail.value
if (mobileParam.length != 11) {
uni.showToast({
title: '请输入正确的手机号码',
icon: 'none'
})
return
}
const onAddrInput = (e) => {
addr.value = e.detail.value
if (!addrParam.trim()) {
receiver.value = ''
uni.showToast({
title: '请输入详细地址',
icon: 'none'
})
return
}
//
const onDeleteAddr = () => {
uni.showModal({
title: '',
content: '确定要删除此收货地址吗?',
confirmColor: '#eb2444',
uni.showLoading()
let url = '/p/address/addAddr'
let method = 'POST'
success(res) {
if (res.confirm) {
const addrIdParam = addrId.value
uni.showLoading()
http.request({
url: '/p/address/deleteAddr/' + addrIdParam,
method: 'DELETE'
})
.then(() => {
uni.hideLoading()
uni.navigateBack({
delta: 1 //
})
if (addrId.value != 0) {
url = '/p/address/updateAddr'
method = 'PUT'
} //
http.request({
url,
method,
data: {
receiver: receiver.value,
mobile: mobile.value,
addr: addr.value,
province: province.value,
provinceId: provinceId.value,
city: city.value,
cityId: cityId.value,
areaId: areaId.value,
area: area.value,
userType: 0,
addrId: addrId.value
}
})
.then(() => {
uni.hideLoading()
uni.navigateBack({
delta: 1
})
})
}
const onReceiverInput = (e) => {
receiver.value = e.detail.value
}
const onMobileInput = (e) => {
mobile.value = e.detail.value
}
const onAddrInput = (e) => {
addr.value = e.detail.value
}
/**
* 删除配送地址
*/
const onDeleteAddr = () => {
uni.showModal({
title: '',
content: '确定要删除此收货地址吗?',
confirmColor: '#eb2444',
success (res) {
if (res.confirm) {
const addrIdParam = addrId.value
uni.showLoading()
http.request({
url: '/p/address/deleteAddr/' + addrIdParam,
method: 'DELETE'
})
.then(() => {
uni.hideLoading()
uni.navigateBack({
delta: 1
})
}
})
}
})
}
}
})
}
</script>
<style scoped lang="scss">

@ -1,67 +1,50 @@
/* 设置页面背景颜色 */
page {
background: #f7f8fa;
}
/* 容器高度设置为100%,确保容器占据整个页面的高度 */
.container {
height: 100%;
}
/* 设置顶部内边距,以适应可能存在的导航栏或其他顶部组件 */
.padding20 {
padding-top: 88rpx; /* rpx 是相对于屏幕宽度的单位,适用于移动设备 */
padding-top: 88rpx;
}
/* 左浮动样式 */
.f-fl {
float: left;
}
/* 右浮动样式 */
.f-fr {
float: right;
}
/* 导航栏包裹器,固定在页面顶部 */
.navWrap {
position: fixed;
top: 0;
left: 0;
z-index: 1; /* 确保导航栏位于其他内容之上 */
z-index: 1;
overflow: hidden;
background-color: #fafafa;
border-bottom: 2rpx solid #f4f4f4;
height: 92rpx;
}
/* 导航栏内部布局 */
.nav {
display: flex;
flex-flow: row nowrap; /* 水平排列,不换行 */
flex-flow: row nowrap;
}
/* 导航栏底部滑动指示器 */
.nav-slider {
left: 0;
bottom: 0;
height: 4rpx;
background-color: #b4282d;
transition: transform 0.3s, -webkit-transform 0.3s; /* 平滑过渡效果 */
transition: transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s;
box-sizing: border-box;
}
/* 单个导航项 */
.nav-item {
display: flex;
align-items: center;
justify-content: center;
flex: 1; /* 均分空间 */
flex: 1;
float: left;
height: 88rpx;
padding: 0 16rpx;
font-size: 28rpx;
text {
box-sizing: border-box;
color: #333;
@ -69,20 +52,14 @@ page {
line-height: 34rpx;
}
}
/* 当前选中的导航项 */
.nav-item.active {
text {
color: #b4282d; /* 改变文字颜色 */
color: #b4282d;
}
}
/* 图标对齐方式 */
.u-icon {
vertical-align: middle;
}
/* 物流信息展示区域 */
.deliveryInfo {
height: 198rpx;
width: 100%;
@ -92,23 +69,24 @@ page {
display: table;
position: relative;
box-sizing: border-box;
.companyname, .expno { /* 快递公司名称和快递单号 */
.companyname {
line-height: 1;
margin-left: 136rpx;
font-size: 28rpx;
.key { /* 标签部分的颜色 */
.key {
color: #666;
}
}
.expno { /* 快递单号样式 */
.expno {
line-height: 1;
margin-left: 136rpx;
font-size: 28rpx;
margin-top: 16rpx;
.key {
color: #666;
}
}
}
/* 物流图标 */
.icon-express {
width: 104rpx;
height: 104rpx;
@ -117,30 +95,22 @@ page {
top: 48rpx;
left: 30rpx;
}
/* 物流信息内的文本对齐 */
.infoWarp {
display: table-cell;
vertical-align: middle;
}
/* 物流详情部分 */
.deliveryDetail {
margin-top: 20rpx;
padding-top: 40rpx;
background-color: #fff;
min-height: 670rpx;
}
/* 物流进度条项 */
.detailItem {
border-left: 1px dashed #f4f4f4; /* 左侧虚线 */
border-left: 1px dashed #f4f4f4;
margin-left: 42rpx;
position: relative;
margin-bottom: 2rpx;
}
/* 进度点 */
.dot {
image {
width: 35rpx;
@ -151,37 +121,29 @@ page {
left: -18rpx;
}
}
/* 最新的物流信息 */
.lastest {
.dot {
image {
top: -2rpx; /* 调整最新点的位置 */
top: -2rpx;
}
}
.detail {
.desc {
color: #105c3e; /* 文字颜色 */
color: #105c3e;
margin-top: 0;
}
.time {
color: #105c3e; /* 时间颜色 */
color: #105c3e;
}
border-top: 0; /* 移除上边框 */
border-top: 0;
}
}
/* 物流详情描述 */
.detail {
.desc {
font-size: 24rpx;
line-height: 30rpx;
margin-top: 40rpx;
}
.time {
font-size: 24rpx;
line-height: 30rpx;
@ -189,14 +151,11 @@ page {
margin-top: 15rpx;
margin-bottom: 39rpx;
}
border-top: 1px solid #f4f4f4;
margin-left: 28rpx;
overflow: hidden;
padding-right: 30rpx;
}
/* 提示信息 */
.deliveryTip {
height: 80rpx;
background-color: #fff8d8;
@ -206,8 +165,6 @@ page {
line-height: 80rpx;
margin-bottom: 20rpx;
}
/* 空白占位符 */
.empty-space {
margin-top: 20rpx;
background: #fff;

@ -1,21 +1,16 @@
<template>
<!-- 物流信息 -->
<view class="container">
<!-- 主要内容包裹器 -->
<view class="wrapper">
<!-- 物流信息头部 -->
<view
class="deliveryInfo"
style="background:url(http://jiales.gz-yami.com/delivery-bg.png) center center no-repeat #fff;"
>
<!-- 物流图标 -->
<view
class="icon-express"
style="background:url(http://jiales.gz-yami.com/delivery-car.png) no-repeat;background-size:100% 100%;"
/>
<!-- 物流信息文本 -->
<view class="infoWarp">
<!-- 快递公司名称 -->
<view class="companyname">
<text class="key">
物流公司
@ -24,7 +19,6 @@
{{ companyName }}
</text>
</view>
<!-- 运单编号 -->
<view class="expno">
<text class="key">
运单编号
@ -35,39 +29,30 @@
</view>
</view>
</view>
<!-- 如果有物流数据则显示物流详情 -->
<view
v-if="dvyData.length"
class="deliveryDetail"
>
<!-- 使用v-for循环渲染每个物流详情项 -->
<block
v-for="(item, index) in dvyData"
:key="index"
>
<!-- 每个物流详情项如果是最新的一条记录则添加lastest类 -->
<view :class="'detailItem ' + (index==0?'lastest':'')">
<!-- 物流进度点 -->
<view class="dot">
<!-- 根据条件加载不同的点图标 -->
<image src="@/static/images/icon/delive-dot.png" />
<image src="@/static/images/icon/dot.png" />
</view>
<!-- 物流详情描述 -->
<view class="detail">
<view class="desc">
{{ item.context }} <!-- 描述文字 -->
{{ item.context }}
</view>
<view class="time">
{{ item.time }} <!-- 时间戳 -->
{{ item.time }}
</view>
</view>
</view>
</block>
</view>
<!-- 如果没有物流数据则显示暂无配送信息 -->
<view
v-else
class="empty-space"
@ -79,39 +64,30 @@
</template>
<script setup>
//
const companyName = ref('') //
const dvyFlowId = ref('') //
const dvyData = ref([]) //
const companyName = ref('')
const dvyFlowId = ref('')
const dvyData = ref([])
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
//
uni.showLoading()
//
http.request({
url: '/delivery/check', // URL
method: 'GET', // HTTP
url: '/delivery/check',
method: 'GET',
data: {
orderNumber: options.orderNum //
orderNumber: options.orderNum
}
})
.then(({ data }) => {
//
companyName.value = data.companyName
dvyFlowId.value = data.dvyFlowId
dvyData.value = data.data
//
uni.hideLoading()
})
})
</script>
<style scoped lang="scss">
/* 引入本地的SCSS文件用于定义样式 */
@use './express-delivery.scss';
</style>

@ -1,4 +1,3 @@
/* 容器背景颜色设置为浅灰色,并且高度根据内容自动调整 */
.container {
background: #f7f7f7;
height: auto;
@ -6,443 +5,409 @@
/* 轮播图及搜索框 */
swiper {
width: 100%; /* 占满整个宽度 */
height: 350rpx; /* 高度为350rpx */
overflow: hidden; /* 隐藏超出容器的内容 */
width: 100%;
height: 350rpx;
overflow: hidden;
}
swiper.pic-swiper {
padding: 10rpx 0; /* 上下内边距 */
background: #fff; /* 白色背景 */
height: 422rpx; /* 设置轮播图的高度 */
padding: 10rpx 0;
background: #fff;
height: 422rpx;
.img-box {
font-size: 0; /* 去除图片间的空白间隙 */
font-size: 0;
}
.banner {
position: absolute; /* 绝对定位 */
width: 690rpx; /* 宽度 */
margin: 0 10rpx; /* 左右外边距 */
height: 402rpx; /* 高度 */
border-radius: 8rpx; /* 圆角 */
display: inline-block; /* 内联块级元素 */
box-shadow: 0 4px 10px 0 rgba(83, 83, 83, 0.288); /* 添加阴影效果 */
position: absolute;
width: 690rpx;
margin: 0 10rpx;
height: 402rpx;
border-radius: 8rpx;
display: inline-block;
box-shadow: 0 4px 10px 0 rgba(83, 83, 83, 0.288);
}
}
swiper-item {
font-size: 26rpx; /* 文字大小 */
font-weight: bold; /* 加粗字体 */
font-size: 26rpx;
font-weight: bold;
}
.wx-swiper-dots {
margin-bottom: 15rpx; /* 下边距 */
margin-bottom: 15rpx;
}
.banner-item {
box-sizing: border-box; /* 盒模型包含内边距和边框 */
box-sizing: border-box;
}
/* 搜索框固定在顶部 */
.container {
.bg-sear {
position: fixed; /* 固定定位 */
z-index: 999; /* 确保位于其他内容之上 */
width: 100%; /* 占满整个宽度 */
line-height: 56rpx; /* 行高 */
background: #fff; /* 白色背景 */
padding: 20rpx 0; /* 上下内边距 */
text-align: center; /* 文本居中 */
top: 0; /* 顶部位置 */
position: fixed;
z-index: 999;
width: 100%;
line-height: 56rpx;
background: #fff;
padding: 20rpx 0;
text-align: center;
top: 0;
}
}
.bg-sear {
.section {
display: flex; /* 弹性布局 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
height: 60rpx; /* 高度 */
background: #fff; /* 白色背景 */
z-index: 1; /* 层叠顺序 */
border-radius: 50rpx; /* 圆角 */
width: 92%; /* 宽度 */
margin: auto; /* 自动外边距 */
left: 4%; /* 左侧位置 */
background: #f7f7f7; /* 浅灰色背景 */
display: flex;
justify-content: center;
align-items: center;
height: 60rpx;
background: #fff;
z-index: 1;
border-radius: 50rpx;
width: 92%;
margin: auto;
left: 4%;
background: #f7f7f7;
.placeholder {
display: block; /* 块级元素 */
font-size: 24rpx; /* 字体大小 */
color: #999; /* 颜色 */
display: block;
font-size: 24rpx;
color: #999;
}
.search-img {
width: 32rpx; /* 宽度 */
height: 32rpx; /* 高度 */
margin-right: 10rpx; /* 右侧外边距 */
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
}
}
}
/* 分类栏目 */
.content {
background: #fff; /* 白色背景 */
background: #fff;
}
.cat-item {
display: flex; /* 弹性布局 */
justify-content: space-between; /* 子元素两端对齐 */
background: #fff; /* 白色背景 */
padding-top: 20rpx; /* 上内边距 */
padding-bottom: 30rpx; /* 下内边距 */
display: flex;
justify-content: space-between;
background: #fff;
padding-top: 20rpx;
padding-bottom: 30rpx;
.item {
text-align: center; /* 文本居中 */
width: 25%; /* 宽度 */
display: flex; /* 弹性布局 */
flex-direction: column; /* 列方向排列 */
margin: auto; /* 自动外边距 */
align-items: center; /* 子元素垂直居中 */
text-align: center;
width: 25%;
display: flex;
flex-direction: column;
margin: auto;
align-items: center;
image {
width: 75rpx; /* 宽度 */
height: 75rpx; /* 高度 */
width: 75rpx;
height: 75rpx;
}
text {
font-size: 26rpx; /* 字体大小 */
margin-top: 20rpx; /* 上外边距 */
font-size: 26rpx;
margin-top: 20rpx;
}
}
}
/* 消息播放 */
.message-play {
position: relative; /* 相对定位 */
height: 90rpx; /* 高度 */
background: #fff; /* 白色背景 */
margin: auto; /* 自动外边距 */
padding: 0 60rpx 0 100rpx; /* 内边距 */
box-sizing: border-box; /* 盒模型包含内边距和边框 */
box-shadow: 0 16rpx 32rpx 0 rgba(7, 17, 27, 0.05); /* 添加阴影效果 */
border: 2rpx solid #fafafa; /* 边框 */
position: relative;
height: 90rpx;
background: #fff;
margin: auto;
padding: 0 60rpx 0 100rpx;
box-sizing: border-box;
box-shadow: 0 16rpx 32rpx 0 rgba(7, 17, 27, 0.05);
border: 2rpx solid #fafafa;
.hornpng {
width: 77rpx; /* 宽度 */
height: 36rpx; /* 高度 */
position: absolute; /* 绝对定位 */
left: 20rpx; /* 左侧位置 */
top: 27rpx; /* 顶部位置 */
margin-right: 8rpx; /* 右侧外边距 */
width: 77rpx;
height: 36rpx;
position: absolute;
left: 20rpx;
top: 27rpx;
margin-right: 8rpx;
}
.swiper-cont {
height: 90rpx; /* 高度 */
line-height: 90rpx; /* 行高 */
height: 90rpx;
line-height: 90rpx;
.items {
text-overflow: ellipsis; /* 超出文本显示省略号 */
display: -webkit-box; /* 使用Webkit盒模型 */
-webkit-line-clamp: 1; /* 限制行数 */
-webkit-box-orient: vertical; /* 垂直排列 */
text-align: left; /* 文本左对齐 */
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-align: left;
}
}
}
.arrow {
width: 15rpx; /* 宽度 */
height: 15rpx; /* 高度 */
border-top: 3rpx solid #686868; /* 上边框 */
border-right: 3rpx solid #686868; /* 右边框 */
transform: rotate(45deg); /* 旋转45度形成箭头 */
position: absolute; /* 绝对定位 */
right: 30rpx; /* 右侧位置 */
top: 34rpx; /* 顶部位置 */
width: 15rpx;
height: 15rpx;
border-top: 3rpx solid #686868;
border-right: 3rpx solid #686868;
transform: rotate(45deg);
position: absolute;
right: 30rpx;
top: 34rpx;
}
/* 每日上新 */
.title {
position: relative; /* 相对定位 */
height: 64rpx; /* 高度 */
line-height: 64rpx; /* 行高 */
font-size: 32rpx; /* 字体大小 */
padding: 40rpx 0 10rpx 30rpx; /* 内边距 */
color: #333; /* 颜色 */
background: #fff; /* 白色背景 */
position: relative;
height: 64rpx;
line-height: 64rpx;
font-size: 32rpx;
padding: 40rpx 0 10rpx 30rpx;
color: #333;
background: #fff;
.more-prod-cont {
color: #999; /* 颜色 */
display: inline-block; /* 内联块级元素 */
text-align: right; /* 文本右对齐 */
color: #999;
display: inline-block;
text-align: right;
.more {
position: absolute; /* 绝对定位 */
right: 30rpx; /* 右侧位置 */
top: 48rpx; /* 顶部位置 */
color: #666; /* 颜色 */
font-size: 24rpx; /* 字体大小 */
padding: 0 20rpx; /* 内边距 */
height: 44rpx; /* 高度 */
line-height: 44rpx; /* 行高 */
position: absolute;
right: 30rpx;
top: 48rpx;
color: #666;
font-size: 24rpx;
padding: 0 20rpx;
height: 44rpx;
line-height: 44rpx;
}
.arrow {
top: 58rpx; /* 顶部位置 */
right: 30rpx; /* 右侧位置 */
border-top: 2rpx solid #666; /* 上边框 */
border-right: 2rpx solid #666; /* 右边框 */
top: 58rpx;
right: 30rpx;
border-top: 2rpx solid #666;
border-right: 2rpx solid #666;
}
}
}
.up-to-date {
.title {
color: #fff; /* 颜色 */
background: none; /* 无背景 */
color: #fff;
background: none;
.more-prod-cont {
.more {
position: absolute; /* 绝对定位 */
right: 30rpx; /* 右侧位置 */
top: 48rpx; /* 顶部位置 */
color: #fff; /* 颜色 */
font-size: 24rpx; /* 字体大小 */
background: #65addf; /* 背景颜色 */
border-radius: 30rpx; /* 圆角 */
padding: 0 30rpx; /* 内边距 */
height: 44rpx; /* 高度 */
line-height: 44rpx; /* 行高 */
position: absolute;
right: 30rpx;
top: 48rpx;
color: #fff;
font-size: 24rpx;
background: #65addf;
border-radius: 30rpx;
padding: 0 30rpx;
height: 44rpx;
line-height: 44rpx;
}
}
}
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAABxCAYAAACkwXoWAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAAZBJREFUeJzt1DEBwCAAwLAxYfhEGXJABkcTBb065trnAwj6XwcAvGKAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGRdKykDj9OUNYkAAAAASUVORK5CYII=");
background-position: top; /* 背景图像位置 */
background-size: 100% 332rpx; /* 背景图像大小 */
background-repeat: no-repeat; /* 不重复背景图像 */
background-color: #fff; /* 白色背景 */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAABxCAYAAACkwXoWAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAAZBJREFUeJzt1DEBwCAAwLAxYfhEGXJABkcTBb065trnAwj6XwcAvGKAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGRdKykDj9OUNYkAAAAASUVORK5CYII=");
background-position: top;
background-size: 100% 332rpx;
background-repeat: no-repeat;
background-color: #fff;
.item-cont {
margin: auto; /* 自动外边距 */
height: auto; /* 自动高度 */
width: calc(100% - 40rpx); /* 宽度计算 */
display: flex; /* 弹性布局 */
flex-wrap: wrap; /* 换行 */
margin: auto;
height: auto;
width: calc(100% - 40rpx);
display: flex;
flex-wrap: wrap;
&::before {
clear: both; /* 清除浮动 */
height: 0; /* 高度 */
overflow: hidden; /* 隐藏溢出 */
clear: both;
height: 0;
overflow: hidden;
}
.prod-item {
border-radius: 10rpx; /* 圆角 */
width: 220rpx; /* 宽度 */
background: #fff; /* 白色背景 */
display: inline-block; /* 内联块级元素 */
margin: 0 8rpx; /* 左右边距 */
margin-bottom: 20rpx; /* 下边距 */
box-shadow: 0rpx 6rpx 8rpx rgba(58,134,185,0.2); /* 添加阴影效果 */
border-radius: 10rpx;
width: 220rpx;
background: #fff;
display: inline-block;
margin: 0 8rpx;
margin-bottom: 20rpx;
box-shadow: 0rpx 6rpx 8rpx rgba(58,134,185,0.2);
.imagecont {
width: 100%; /* 占满整个宽度 */
font-size: 0; /* 去除图片间的空白间隙 */
width: 220rpx; /* 宽度 */
height: 220rpx; /* 高度 */
width: 100%;
font-size: 0;
width: 220rpx;
height: 220rpx;
.prodimg {
width: 220rpx; /* 宽度 */
height: 220rpx; /* 高度 */
vertical-align: middle; /* 垂直居中 */
border-top-left-radius: 10rpx; /* 左上圆角 */
border-top-right-radius: 10rpx; /* 右上圆角 */
font-size: 0; /* 去除图片间的空白间隙 */
width: 220rpx;
height: 220rpx;
vertical-align: middle;
border-top-left-radius: 10rpx;
border-top-right-radius: 10rpx;
font-size: 0;
}
}
.prod-text {
font-size: 28rpx; /* 字体大小 */
overflow: hidden; /* 隐藏溢出 */
margin: 10rpx 0; /* 上下外边距 */
height: 75rpx; /* 高度 */
display: -webkit-box; /* 使用Webkit盒模型 */
word-break: break-all; /* 单词换行 */
display: -webkit-box; /* 使用Webkit盒模型 */
-webkit-line-clamp: 2; /* 限制行数 */
-webkit-box-orient: vertical; /* 垂直排列 */
color: #000; /* 颜色 */
padding: 0 10rpx; /* 内边距 */
font-size: 28rpx;
overflow: hidden;
margin: 10rpx 0;
height: 75rpx;
display: -webkit-box;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: #000;
padding: 0 10rpx;
}
.prod-price {
font-size: 25rpx; /* 字体大小 */
color: #eb2444; /* 颜色 */
font-family: Arial; /* 字体 */
padding: 0 10rpx; /* 内边距 */
font-size: 25rpx;
color: #eb2444;
font-family: Arial;
padding: 0 10rpx;
}
}
}
}
.hotsale-item-cont {
padding-bottom: 20rpx; /* 下内边距 */
background: #fff; /* 白色背景 */
padding-bottom: 20rpx;
background: #fff;
}
.more.prod-price {
position: absolute; /* 绝对定位 */
bottom: 20rpx; /* 底部位置 */
position: absolute;
bottom: 20rpx;
}
/* 商城热卖 */
.hot-sale {
.prod-items {
width: 345rpx; /* 宽度 */
display: inline-block; /* 内联块级元素 */
background: #fff; /* 白色背景 */
padding-bottom: 20rpx; /* 下内边距 */
box-sizing: border-box; /* 盒模型包含内边距和边框 */
box-shadow: 0rpx 6rpx 8rpx rgba(58,134,185,0.2); /* 添加阴影效果 */
width: 345rpx;
display: inline-block;
background: #fff;
padding-bottom: 20rpx;
box-sizing: border-box;
box-shadow: 0rpx 6rpx 8rpx rgba(58,134,185,0.2);
&:nth-child(2n-1) {
margin: 20rpx 10rpx 10rpx 20rpx; /* 奇数项的外边距 */
margin: 20rpx 10rpx 10rpx 20rpx;
}
&:nth-child(2n) {
margin: 20rpx 20rpx 10rpx 10rpx; /* 偶数项的外边距 */
margin: 20rpx 20rpx 10rpx 10rpx;
}
}
}
.prod-items {
.hot-imagecont {
width: 341rpx; /* 宽度 */
height: 341rpx; /* 高度 */
width: 341rpx;
height: 341rpx;
.hotsaleimg {
width: 341rpx; /* 宽度 */
height: 341rpx; /* 高度 */
width: 341rpx;
height: 341rpx;
}
font-size: 0; /* 去除图片间的空白间隙 */
text-align: center; /* 文本居中 */
font-size: 0;
text-align: center;
}
.hot-text {
.hotprod-text {
font-size: 28rpx; /* 字体大小 */
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 隐藏溢出 */
text-overflow: ellipsis; /* 超出文本显示省略号 */
font-size: 28rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
margin-top: 20rpx; /* 上外边距 */
padding: 0 10rpx; /* 内边距 */
margin-top: 20rpx;
padding: 0 10rpx;
.prod-info {
font-size: 22rpx; /* 字体大小 */
color: #999; /* 颜色 */
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 隐藏溢出 */
text-overflow: ellipsis; /* 超出文本显示省略号 */
font-size: 22rpx;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.prod-text-info {
position: relative; /* 相对定位 */
height: 70rpx; /* 高度 */
line-height: 70rpx; /* 行高 */
font-family: Arial; /* 字体 */
position: relative;
height: 70rpx;
line-height: 70rpx;
font-family: Arial;
.hotprod-price {
display: inline; /* 内联元素 */
font-size: 26rpx; /* 字体大小 */
color: #eb2444; /* 颜色 */
display: inline;
font-size: 26rpx;
color: #eb2444;
}
.basket-img {
width: 50rpx; /* 宽度 */
height: 50rpx; /* 高度 */
position: absolute; /* 绝对定位 */
right: 0; /* 右侧位置 */
bottom: 7rpx; /* 底部位置 */
padding: 8rpx; /* 内边距 */
width: 50rpx;
height: 50rpx;
position: absolute;
right: 0;
bottom: 7rpx;
padding: 8rpx;
}
}
}
}
.more-prod {
.prod-text-right {
.prod-info {
font-size: 22rpx; /* 字体大小 */
color: #999; /* 颜色 */
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 隐藏溢出 */
text-overflow: ellipsis; /* 超出文本显示省略号 */
font-size: 22rpx;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.singal-price {
display: inline; /* 内联元素 */
font-size: 20rpx; /* 字体大小 */
text-decoration: line-through; /* 删除线 */
color: #777; /* 颜色 */
margin-left: 15rpx; /* 左边距 */
display: inline;
font-size: 20rpx;
text-decoration: line-through;
color: #777;
margin-left: 15rpx;
}
/* 更多宝贝 */
.more-prod {
background: #fff; /* 白色背景 */
background: #fff;
.prod-show {
.show-item {
.more-prod-pic {
width: 250rpx; /* 宽度 */
height: 250rpx; /* 高度 */
width: 250rpx;
height: 250rpx;
.more-pic {
max-width: 100%; /* 最大宽度 */
max-height: 100%; /* 最大高度 */
max-width: 100%;
max-height: 100%;
}
}
position: relative; /* 相对定位 */
display: flex; /* 弹性布局 */
padding: 20rpx; /* 内边距 */
justify-content: flex-start; /* 子元素左对齐 */
border-top: 2rpx solid #f4f4f4; /* 上边框 */
position: relative;
display: flex;
padding: 20rpx;
justify-content: flex-start;
border-top: 2rpx solid #f4f4f4;
.prod-text-right {
margin-left: 30rpx; /* 左边距 */
width: 72%; /* 宽度 */
padding-bottom: 10rpx; /* 下内边距 */
display: flex; /* 弹性布局 */
flex-direction: column; /* 列方向排列 */
justify-content: center; /* 子元素垂直居中 */
margin-left: 30rpx;
width: 72%;
padding-bottom: 10rpx;
display: flex;
flex-direction: column;
justify-content: center;
.go-to-buy {
font-size: 26rpx; /* 字体大小 */
background: #fff2f5; /* 背景颜色 */
color: #eb2444; /* 颜色 */
border-radius: 50rpx; /* 圆角 */
text-align: center; /* 文本居中 */
padding: 12rpx 20rpx; /* 内边距 */
position: absolute; /* 绝对定位 */
right: 20rpx; /* 右侧位置 */
bottom: 20rpx; /* 底部位置 */
font-size: 26rpx;
background: #fff2f5;
color: #eb2444;
border-radius: 50rpx;
text-align: center;
padding: 12rpx 20rpx;
position: absolute;
right: 20rpx;
bottom: 20rpx;
}
.prod-text.more {
margin: 0; /* 外边距 */
font-size: 28rpx; /* 字体大小 */
overflow: hidden; /* 隐藏溢出 */
margin-bottom: 20rpx; /* 下外边距 */
display: -webkit-box; /* 使用Webkit盒模型 */
word-break: break-all; /* 单词换行 */
display: -webkit-box; /* 使用Webkit盒模型 */
-webkit-line-clamp: 2; /* 限制行数 */
-webkit-box-orient: vertical; /* 垂直排列 */
margin: 0;
font-size: 28rpx;
overflow: hidden;
margin-bottom: 20rpx;
display: -webkit-box;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.more.prod-price {
font-size: 28rpx; /* 字体大小 */
font-family: arial; /* 字体 */
font-size: 28rpx;
font-family: arial;
}
}
}
}
}
.b-cart {
margin-top: 30rpx; /* 上外边距 */
margin-top: 30rpx;
.basket-img {
width: 50rpx; /* 宽度 */
height: 50rpx; /* 高度 */
position: absolute; /* 绝对定位 */
right: 46rpx; /* 右侧位置 */
padding: 8rpx; /* 内边距 */
width: 50rpx;
height: 50rpx;
position: absolute;
right: 46rpx;
padding: 8rpx;
}
}

@ -1,17 +1,15 @@
<template>
<!-- 页面的最外层容器用于包裹整个页面内容 -->
<view class="container">
<!-- 搜索栏背景区域用于设置搜索相关部分的背景样式 -->
<view class="bg-sear">
<!-- 可滚动到顶部的区域通常用于放置一些操作元素方便用户能快速回到页面顶部 -->
<view class="scrolltop">
<!-- 搜索栏的具体视图容器绑定了点击事件 `toSearchPage`点击此处可触发跳转到搜索页面的操作 -->
<view class="section"
@tap="toSearchPage">
<!-- 搜索图标元素通过指定路径引入图片资源设置类名为 `search-img`用于在搜索栏中显示搜索图标提示用户此处可进行搜索操作 -->
<image src="@/static/images/icon/search.png"
class="search-img" />
<!-- 搜索栏的占位文本显示搜索字样提示用户在此处输入想要搜索的内容 -->
<view
class="section"
@tap="toSearchPage"
>
<image
src="@/static/images/icon/search.png"
class="search-img"
/>
<text class="placeholder">
搜索
</text>
@ -19,151 +17,156 @@
</view>
</view>
<!-- 页面主要内容区域包含轮播图分类导航消息通知商品展示等多个板块 -->
<view class="content">
<!-- swiper 组件用于实现轮播图功能通过一系列属性来配置轮播图的展示效果和行为 -->
<swiper :autoplay="autoplay"
:indicator-color="indicatorColor"
:interval="interval"
:duration="duration"
:indicator-active-color="indicatorActiveColor + ' '"
:circular="true"
class="pic-swiper"
indicator-dots
previous-margin="20rpx"
next-margin="20rpx">
<!-- 使用 `v-for` 指令循环遍历 `indexImgs` 数组`index` 为当前项的索引每个元素对应轮播图中的一张图片相关信息 -->
<block v-for="(item, index) in indexImgs"
:key="index">
<!-- swiper-item 组件表示轮播图中的每一项即每张图片对应的展示容器 -->
<!-- swiper -->
<swiper
:autoplay="autoplay"
:indicator-color="indicatorColor"
:interval="interval"
:duration="duration"
:indicator-active-color="indicatorActiveColor + ' '"
:circular="true"
class="pic-swiper"
indicator-dots
previous-margin="20rpx"
next-margin="20rpx"
>
<block
v-for="(item, index) in indexImgs"
:key="index"
>
<swiper-item class="banner-item">
<!-- 图片的外层视图容器方便对图片进行样式设置和布局调整 -->
<view class="img-box">
<!-- 轮播图中的图片元素通过 `:src` 绑定图片的 URL 地址来自 `item.imgUrl`同时利用自定义属性 `:data-prodid` 传递商品相关的 ID 信息并且绑定了点击事件 `toProdPage`点击图片可触发跳转到对应商品页面的操作 -->
<image :src="item.imgUrl"
:data-prodid="item.relation"
class="banner"
@tap="toProdPage" />
<image
:src="item.imgUrl"
:data-prodid="item.relation"
class="banner"
@tap="toProdPage"
/>
</view>
</swiper-item>
</block>
</swiper>
<!-- 轮播图部分结束 -->
<!-- 分类导航区域展示多个不同分类的导航项每个项可点击跳转到对应的页面 -->
<!-- end swiper -->
<view class="cat-item">
<!-- 单个分类导航项的视图容器设置了自定义属性 `data-sts="1"`并绑定点击事件 `toClassifyPage`点击该项可触发跳转到相应分类页面的操作不同分类项通过此属性传递不同参数以区分具体分类情况 -->
<view class="item"
data-sts="1"
@tap="toClassifyPage">
<!-- 分类项的图标元素通过指定路径引入图片资源此处图标可能用于标识新品推荐分类 -->
<view
class="item"
data-sts="1"
@tap="toClassifyPage"
>
<image src="@/static/images/icon/newProd.png" />
<!-- 分类项的文本标签显示新品推荐字样明确告知用户该分类的含义 -->
<text>新品推荐</text>
</view>
<view class="item"
data-sts="1"
@tap="toClassifyPage">
<!-- 类似上述分类项结构该图标用于标识限时特惠分类文本标签显示对应分类名称 -->
<view
class="item"
data-sts="1"
@tap="toClassifyPage"
>
<image src="@/static/images/icon/timePrice.png" />
<text>限时特惠</text>
</view>
<view class="item"
data-sts="3"
@tap="toClassifyPage">
<!-- 此图标对应每日疯抢分类标识文本标签显示相应分类名称 -->
<view
class="item"
data-sts="3"
@tap="toClassifyPage"
>
<image src="@/static/images/icon/neweveryday.png" />
<text>每日疯抢</text>
</view>
<view class="item"
@tap="toCouponCenter">
<!-- 该图标表示领优惠券分类相关标识文本标签显示对应操作名称点击此项会触发领优惠券相关的业务逻辑具体由 `toCouponCenter` 函数处理 -->
<view
class="item"
@tap="toCouponCenter"
>
<image src="@/static/images/icon/newprods.png" />
<text>领优惠券</text>
</view>
</view>
<!-- 消息播放区域只有当 `news` 数组有数据 `news` 存在且长度大于 0时才显示用于展示滚动播放的消息通知点击可触发跳转到消息页面的操作 -->
<view v-if="news && news.length"
class="message-play"
@tap="onNewsPage">
<!-- 消息通知的图标元素通过指定路径引入图片资源一般是类似喇叭的图标用于提示此处为消息相关区域 -->
<image src="@/static/images/icon/horn.png"
class="hornpng" />
<!-- swiper 组件用于实现消息的垂直滚动播放效果通过相关属性控制滚动行为 -->
<swiper :vertical="true"
:autoplay="true"
:circular="true"
duration="1000"
class="swiper-cont">
<!-- 使用 `v-for` 指令循环遍历 `news` 数组`index` 为当前项的索引每个元素对应一条消息内容 -->
<block v-for="(item, index) in news"
:key="index">
<!-- swiper-item 组件表示滚动消息中的每一项在此处直接通过双括号插值表达式展示消息的标题`item.title` -->
<!-- 消息播放 -->
<view
v-if="news && news.length"
class="message-play"
@tap="onNewsPage"
>
<image
src="@/static/images/icon/horn.png"
class="hornpng"
/>
<swiper
:vertical="true"
:autoplay="true"
:circular="true"
duration="1000"
class="swiper-cont"
>
<block
v-for="(item, index) in news"
:key="index"
>
<swiper-item class="items">
{{ item.title }}
</swiper-item>
</block>
</swiper>
<!-- 箭头图标元素可能用于视觉上提示用户可点击查看更多消息等操作具体功能要结合样式和交互设计来看 -->
<text class="arrow" />
</view>
</view>
<!-- 商品展示相关区域通过条件判断 `v-if="updata"` 来控制显示与否内部根据不同商品分类或样式展示相应商品信息 -->
<view v-if="updata"
class="updata">
<!-- 使用 `v-for` 指令循环遍历 `taglist` 数组`index` 为当前项的索引每个元素对应一组商品相关的信息包括商品列表标题等 -->
<block v-for="(item, index) in taglist"
:key="index">
<!-- 每日上新类型商品展示区域只有当商品组的 `style` 属性为 `'2'` 且包含商品数据 `item.prods` 存在且长度大于 0时才显示 -->
<view v-if="item.style==='2' && item.prods && item.prods.length"
class="up-to-date">
<!-- 商品组标题区域包含标题文本以及查看更多操作按钮 -->
<view
v-if="updata"
class="updata"
>
<block
v-for="(item, index) in taglist"
:key="index"
>
<!-- 每日上新 -->
<view
v-if="item.style==='2' && item.prods && item.prods.length"
class="up-to-date"
>
<view class="title">
<!-- 通过双括号插值表达式展示商品组的标题数据来源于 `item.title` 属性 -->
<text>{{ item.title }}</text>
<!-- 查看更多操作按钮的视图容器设置了多个自定义属性`data-sts``data-id``data-title`用于传递相关参数绑定点击事件 `toClassifyPage`点击可触发跳转到分类页面查看更多该类型商品的操作 -->
<view class="more-prod-cont"
data-sts="0"
:data-id="item.id"
:data-title="item.title"
@tap="toClassifyPage">
<!-- 查看更多的文本标签 -->
<view
class="more-prod-cont"
data-sts="0"
:data-id="item.id"
:data-title="item.title"
@tap="toClassifyPage"
>
<text class="more">
查看更多
</text>
</view>
</view>
<!-- 商品列表展示区域通过循环遍历 `item.prods` 数组展示每个商品的详细信息 -->
<view class="item-cont">
<block v-for="(prod, index2) in item.prods"
:key="index2">
<!-- 单个商品的视图容器绑定点击事件 `toProdPage`点击可触发跳转到对应商品页面的操作通过自定义属性 `:data-prodid` 传递商品的 ID 信息 -->
<view class="prod-item"
:data-prodid="prod.prodId"
@tap="toProdPage">
<block
v-for="(prod, index2) in item.prods"
:key="index2"
>
<view
class="prod-item"
:data-prodid="prod.prodId"
@tap="toProdPage"
>
<view>
<!-- 商品图片的外层视图容器用于包裹图片展示相关组件 -->
<view class="imagecont">
<!-- `img-show` 组件用于展示商品图片可能是自定义的图片展示组件具备特定功能或样式处理通过 `:src` 绑定商品图片的 URL 地址`prod.pic`并通过 `:class-list` 绑定类名数组`['prodimg']`来应用特定的图片样式 -->
<img-show :src="prod.pic"
:class-list="['prodimg']" />
<img-show
:src="prod.pic"
:class-list="['prodimg']"
/>
</view>
<!-- 商品名称的文本标签通过双括号插值表达式展示商品的名称`prod.prodName` -->
<view class="prod-text">
{{ prod.prodName }}
</view>
<!-- 商品价格展示区域包含价格符号整数部分小数部分的文本展示 -->
<view class="price">
<!-- 价格符号文本显示字样 -->
<text class="symbol">
</text>
<!-- 价格的整数部分通过调用 `wxs.parsePrice` 函数对商品价格`prod.price`进行处理取处理结果的第一个元素作为整数部分展示`wxs.parsePrice` 函数具体功能需查看相关代码 -->
<text class="big-num">
{{ wxs.parsePrice(prod.price)[0] }}
</text>
<!-- 价格的小数部分通过 `wxs.parsePrice` 函数取处理结果的第二个元素展示并添加小数点 -->
<text class="small-num">
.{{ wxs.parsePrice(prod.price)[1] }}
</text>
@ -174,51 +177,50 @@
</view>
</view>
<!-- 商城热卖类型商品展示区域只有当商品组的 `style` 属性为 `'1'` 且包含商品数据`item.prods` 存在且长度大于 0时才显示 -->
<view v-if="item.style==='1' && item.prods && item.prods.length"
class="hot-sale">
<!-- 商品组标题区域包含标题文本以及更多操作按钮带有箭头图标用于提示用户可点击查看更多商品 -->
<!-- 商城热卖 -->
<view
v-if="item.style==='1' && item.prods && item.prods.length"
class="hot-sale"
>
<view class="title">
<text>{{ item.title }}</text>
<view class="more-prod-cont"
data-sts="0"
:data-id="item.id"
:data-title="item.title"
@tap="toClassifyPage">
<!-- 更多的文本标签 -->
<view
class="more-prod-cont"
data-sts="0"
:data-id="item.id"
:data-title="item.title"
@tap="toClassifyPage"
>
<text class="more">
更多
</text>
<!-- 箭头图标元素用于视觉上引导用户点击操作 -->
<text class="arrow" />
</view>
</view>
<!-- 商品列表展示区域通过循环遍历 `item.prods` 数组展示每个商品的详细信息 -->
<view class="hotsale-item-cont">
<block v-for="(prod, index2) in item.prods"
:key="index2">
<!-- 单个商品的视图容器绑定点击事件 `toProdPage`点击可触发跳转到对应商品页面的操作通过自定义属性 `:data-prodid` 传递商品的 ID 信息 -->
<view class="prod-items"
:data-prodid="prod.prodId"
@tap="toProdPage">
<block
v-for="(prod, index2) in item.prods"
:key="index2"
>
<view
class="prod-items"
:data-prodid="prod.prodId"
@tap="toProdPage"
>
<view class="hot-imagecont">
<!-- 商品图片展示的外层视图容器内部使用 `img-show` 组件展示商品图片原理与前面类似 -->
<img-show :src="prod.pic"
:class-list="['hotsaleimg']" />
<img-show
:src="prod.pic"
:class-list="['hotsaleimg']"
/>
</view>
<!-- 商品详细信息展示区域包含商品名称简介价格以及购物车图标等元素 -->
<view class="hot-text">
<!-- 商品名称展示部分 -->
<view class="hotprod-text">
{{ prod.prodName }}
</view>
<!-- 商品简介的文本标签通过双括号插值表达式展示商品的简介内容`prod.brief` -->
<view class="prod-info">
{{ prod.brief }}
</view>
<!-- 商品价格及购物车图标相关展示区域 -->
<view class="prod-text-info">
<!-- 商品价格展示部分格式与前面相同包含价格符号整数部分小数部分 -->
<view class="price">
<text class="symbol">
@ -230,9 +232,10 @@
.{{ wxs.parsePrice(prod.price)[1] }}
</text>
</view>
<!-- 购物车图标元素通过指定路径引入图片资源通常表示可将该商品加入购物车的操作具体功能需结合相关交互逻辑确定 -->
<image src="@/static/images/tabbar/basket-sel.png"
class="basket-img" />
<image
src="@/static/images/tabbar/basket-sel.png"
class="basket-img"
/>
</view>
</view>
</view>
@ -240,39 +243,38 @@
</view>
</view>
<!-- 更多宝贝类型商品展示区域只有当商品组的 `style` 属性为 `'0'` 且包含商品数据`item.prods` 存在且长度大于 0时才显示 -->
<view v-if="item.style==='0' && item.prods && item.prods.length"
class="more-prod">
<!-- 商品组标题区域直接通过双括号插值表达式展示商品组的标题来源于 `item.title` 属性 -->
<!-- 更多宝贝 -->
<view
v-if="item.style==='0' && item.prods && item.prods.length"
class="more-prod"
>
<view class="title">
{{ item.title }}
</view>
<!-- 商品列表展示区域通过循环遍历 `item.prods` 数组展示每个商品的详细信息 -->
<view class="prod-show">
<block v-for="(prod, index2) in item.prods"
:key="index2">
<!-- 单个商品的视图容器绑定点击事件 `toProdPage`点击可触发跳转到对应商品页面的操作通过自定义属性 `:data-prodid` 传递商品的 ID 信息 -->
<view class="show-item"
:data-prodid="prod.prodId"
@tap="toProdPage">
<block
v-for="(prod, index2) in item.prods"
:key="index2"
>
<view
class="show-item"
:data-prodid="prod.prodId"
@tap="toProdPage"
>
<view class="more-prod-pic">
<!-- 商品图片展示的外层视图容器内部使用 `img-show` 组件展示商品图片方式与前面一致 -->
<img-show :src="prod.pic"
:class-list="['more-pic']" />
<img-show
:src="prod.pic"
:class-list="['more-pic']"
/>
</view>
<!-- 商品详细信息展示区域包含商品名称简介价格以及购物车图标等元素 -->
<view class="prod-text-right">
<!-- 商品名称展示部分 -->
<view class="prod-text more">
{{ prod.prodName }}
</view>
<!-- 商品简介的文本标签通过双括号插值表达式展示商品的简介内容`prod.brief` -->
<view class="prod-info">
{{ prod.brief }}
</view>
<!-- 商品价格及购物车图标相关展示区域 -->
<view class="b-cart">
<!-- 商品价格展示部分包含价格符号整数部分小数部分 -->
<view class="price">
<text class="symbol">
@ -284,10 +286,11 @@
.{{ wxs.parsePrice(prod.price)[1] }}
</text>
</view>
<!-- 购物车图标元素通过指定路径引入图片资源绑定点击事件 `addToCart(prod)`点击可触发将该商品加入购物车的操作 -->
<image src="@/static/images/tabbar/basket-sel.png"
class="basket-img"
@tap.stop="addToCart(prod)" />
<image
src="@/static/images/tabbar/basket-sel.png"
class="basket-img"
@tap.stop="addToCart(prod)"
/>
</view>
</view>
</view>

@ -1,42 +1,23 @@
/* 定义新闻详情页的整体样式 */
.news-detail {
/* 设置内边距padding20rpx表示在各个方向上都留出20个相对像素的空间 */
padding: 20rpx;
/* 新闻标题的样式 */
.news-detail-title {
/* 设置字体大小为32rpx */
font-size: 32rpx;
/* 设置字体加粗 */
font-weight: bold;
/* 设置行高为50rpx确保文本有适当的间距 */
line-height: 50rpx;
/* 设置内边距,使标题与容器边缘有一定的距离 */
padding: 20rpx;
}
/* 新闻正文内容的样式 */
.news-detail-text {
/* 设置字体大小为28rpx */
font-size: 28rpx;
/* 设置行高为46rpx以保证阅读的舒适性 */
line-height: 46rpx;
/* 设置文本对齐方式为两端对齐 */
text-align: justify;
/* 对于中文字符使用inter-ideograph来调整字符间距使文本更美观 */
text-justify: inter-ideograph;
/* 设置上边距,使得正文与前一个元素之间有足够的空间 */
margin-top: 20rpx;
}
}
/* 内容区域的样式 */
.content {
/* 使用 :deep 伪类选择器穿透到子组件中的 img 标签 */
:deep(img) {
/* 确保图片作为块级元素显示,占据其父容器的全部宽度 */
display: block;
/* 图片宽度设置为100%,使其适应父容器的宽度 */
width: 100%;
/* 保持图片的宽高比例不变,高度自动调整 */
height: auto;
}
}

@ -1,59 +1,41 @@
<template>
<view class="container">
<!-- 新闻详情容器 -->
<view class="news-detail">
<!-- 动态绑定新闻标题使用双大括号语法插值显示 news 对象中的 title 属性 -->
<view class="news-detail-title">
{{ news.title }}
</view>
<!-- 使用 rich-text 组件来解析并渲染 HTML 内容动态绑定 news 对象中的 content 属性 -->
<rich-text class="content"
:nodes="news.content" <!-- :nodes v-bind:nodes 的缩写用于绑定 HTML 字符串 -->
<rich-text
class="content"
:nodes="news.content"
/>
</view>
</view>
</template>
<script setup>
// ref
import { ref } from 'vue';
// http onLoad
// news title, content id
const news = ref({
title: '', //
content: '', // HTML
id: null // ID
});
/**
* 页面加载生命周期钩子当页面加载时调用
* @param options - 包含页面加载参数的对象
*/
onLoad((options) => {
// HTTP 使 options.id
http.request({
url: '/shop/notice/info/' + options.id, // API URL
method: 'GET' //
const news = ref({
title: '',
content: '',
id: null
})
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
//
http.request({
url: '/shop/notice/info/' + options.id,
method: 'GET'
})
.then(({ data }) => {
data.content = data.content.replace(/width=/gi, 'sss=')
data.content = data.content.replace(/height=/gi, 'sss=')
data.content = data.content.replace(/ \/>/gi, ' style="max-width:100% !important;display:block;" />')
news.value = data
})
.then(({ data }) => {
// img width height
//
data.content = data.content.replace(/width=/gi, 'sss=') // width
data.content = data.content.replace(/height=/gi, 'sss=') // height
data.content = data.content.replace(/ \/>/gi, ' style="max-width:100% !important;display:block;" />') //
// news
news.value = data;
})
.catch(error => {
// ()
console.error('Failed to fetch news detail:', error);
});
});
})
</script>
<style scoped lang="scss">
/* 引入外部的 SCSS 文件,以定义组件的样式 */
@use './news-detail.scss';
@use './news-detail.scss';
</style>

@ -1,284 +1,247 @@
/* 定义容器的基本样式 */
.container {
background: #f4f4f4; /* 设置背景颜色为浅灰色 */
background: #f4f4f4;
}
/* 订单详情的样式 */
.order-detail {
margin-bottom: 120rpx; /* 设置底部外边距,确保与其他元素有足够的间距 */
padding-bottom: 160rpx; /* 设置底部内边距,用于留出足够的空间 */
.delivery-addr { /* 配送地址部分的样式 */
padding: 20rpx 30rpx; /* 设置上下左右内边距 */
background: #fff; /* 设置背景颜色为白色 */
.user-info { /* 用户信息的样式 */
line-height: 48rpx; /* 设置行高 */
word-wrap: break-word; /* 允许长单词或URL地址换行到下一行 */
word-break: break-all; /* 在任何字符间断开 */
overflow: hidden; /* 超出内容隐藏 */
text-overflow: ellipsis; /* 超出内容以省略号显示 */
display: -webkit-box; /* 使用Webkit的弹性盒子模型 */
-webkit-line-clamp: 1; /* 限制文本行数为1 */
-webkit-box-orient: vertical; /* 盒子的方向是垂直的 */
.item { /* 用户信息项的样式 */
font-size: 28rpx; /* 设置字体大小 */
margin-right: 30rpx; /* 设置右边距 */
vertical-align: top; /* 垂直对齐方式为顶部 */
display: inline-block; /* 作为行内块级元素显示 */
margin-bottom: 120rpx;
padding-bottom: 160rpx;
.delivery-addr {
padding: 20rpx 30rpx;
background: #fff;
.user-info {
line-height: 48rpx;
word-wrap: break-word;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
.item {
font-size: 28rpx;
margin-right: 30rpx;
vertical-align: top;
display: inline-block;
}
}
.addr { /* 地址的样式 */
font-size: 26rpx; /* 设置字体大小 */
line-height: 36rpx; /* 设置行高 */
color: #999; /* 设置字体颜色为浅灰色 */
word-wrap: break-word; /* 允许长单词或URL地址换行到下一行 */
.addr {
font-size: 26rpx;
line-height: 36rpx;
color: #999;
word-wrap: break-word;
}
}
}
/* 商品项的样式 */
.prod-item {
background-color: #fff; /* 设置背景颜色为白色 */
margin-top: 15rpx; /* 设置上边距 */
font-size: 28rpx; /* 设置字体大小 */
.item-cont { /* 商品内容的样式 */
.prod-pic { /* 商品图片的样式 */
background-color: #fff;
margin-top: 15rpx;
font-size: 28rpx;
.item-cont {
.prod-pic {
image {
width: 180rpx;
height: 180rpx;
width: 100%;
height: 100%; /* 确保图片填满整个容器 */
height: 100%;
}
font-size: 0; /* 消除行内元素间的空隙 */
display: block; /* 作为块级元素显示 */
font-size: 0;
display: block;
width: 160rpx;
height: 160rpx;
overflow: hidden; /* 超出内容隐藏 */
background: #fff; /* 设置背景颜色为白色 */
margin-right: 16rpx; /* 设置右边距 */
overflow: hidden;
background: #fff;
margin-right: 16rpx;
}
display: flex; /* 使用弹性布局 */
align-items: center; /* 垂直居中对齐 */
padding: 30rpx; /* 设置内边距 */
border-top: 2rpx solid #f1f1f1; /* 设置上边框 */
.prod-info { /* 商品信息的样式 */
margin-left: 10rpx; /* 设置左边距 */
font-size: 28rpx; /* 设置字体大小 */
width: 100%; /* 设置宽度为100% */
position: relative; /* 设置相对定位 */
display: flex;
align-items: center;
padding: 30rpx;
border-top: 2rpx solid #f1f1f1;
.prod-info {
margin-left: 10rpx;
font-size: 28rpx;
width: 100%;
position: relative;
height: 80px;
flex: 1; /* 占据剩余空间 */
.prodname { /* 商品名称的样式 */
font-size: 28rpx; /* 设置字体大小 */
line-height: 40rpx; /* 设置行高 */
max-height: 86rpx; /* 设置最大高度 */
overflow: hidden; /* 超出内容隐藏 */
display: -webkit-box; /* 使用Webkit的弹性盒子模型 */
-webkit-line-clamp: 1; /* 限制文本行数为1 */
-webkit-box-orient: vertical; /* 盒子的方向是垂直的 */
text-overflow: ellipsis; /* 超出内容以省略号显示 */
word-break: break-all; /* 在任何字符间断开 */
-webkit-flex: 1;
-ms-flex: 1;
-webkit-box-flex: 1;
-moz-box-flex: 1;
flex: 1;
.prodname {
font-size: 28rpx;
line-height: 40rpx;
max-height: 86rpx;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
word-break: break-all;
}
.prod-info-cont { /* 商品信息内容的样式 */
position: relative; /* 设置相对定位 */
color: #999; /* 设置字体颜色为浅灰色 */
margin-top: 10rpx; /* 设置上边距 */
font-size: 24rpx; /* 设置字体大小 */
.info-item { /* 信息项的样式 */
color: #999; /* 设置字体颜色为浅灰色 */
height: 28rpx; /* 设置高度 */
margin-top: 10rpx; /* 设置上边距 */
font-size: 24rpx; /* 设置字体大小 */
overflow: hidden; /* 超出内容隐藏 */
display: -webkit-box; /* 使用Webkit的弹性盒子模型 */
-webkit-line-clamp: 1; /* 限制文本行数为1 */
-webkit-box-orient: vertical; /* 盒子的方向是垂直的 */
text-overflow: ellipsis; /* 超出内容以省略号显示 */
word-break: break-all; /* 在任何字符间断开 */
width: 70%; /* 设置宽度 */
.prod-info-cont {
position: relative;
color: #999;
margin-top: 10rpx;
font-size: 24rpx;
.info-item {
color: #999;
height: 28rpx;
margin-top: 10rpx;
font-size: 24rpx;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
word-break: break-all;
width: 70%;
}
.number { /* 数量的样式 */
float: left; /* 向左浮动 */
margin-right: 20rpx; /* 设置右边距 */
.number {
float: left;
margin-right: 20rpx;
}
}
}
}
.price-nums { /* 价格和数量的样式 */
margin-top: 30rpx; /* 设置上边距 */
.prodprice { /* 商品价格的样式 */
color: #333; /* 设置字体颜色 */
height: 50rpx; /* 设置高度 */
line-height: 50rpx; /* 设置行高 */
font-size: 24rpx; /* 设置字体大小 */
float: left; /* 向左浮动 */
.price-nums {
margin-top: 30rpx;
.prodprice {
color: #333;
height: 50rpx;
line-height: 50rpx;
font-size: 24rpx;
float: left;
}
.btn-box { /* 按钮容器的样式 */
float: right; /* 向右浮动 */
text-align: right; /* 文本右对齐 */
.btn { /* 按钮的样式 */
padding: 6rpx 30rpx; /* 设置内边距 */
line-height: 36rpx; /* 设置行高 */
margin-left: 20rpx; /* 设置左边距 */
font-size: 24rpx; /* 设置字体大小 */
display: inline-block; /* 作为行内块级元素显示 */
border: 2rpx solid #e4e4e4; /* 设置边框 */
border-radius: 50rpx; /* 设置圆角 */
.btn-box {
float: right;
text-align: right;
.btn {
padding: 6rpx 30rpx;
line-height: 36rpx;
margin-left: 20rpx;
font-size: 24rpx;
display: inline-block;
border: 2rpx solid #e4e4e4;
border-radius: 50rpx;
}
}
}
}
/* 订单消息的样式 */
.order-msg {
background: #fff; /* 设置背景颜色为白色 */
margin-top: 15rpx; /* 设置上边距 */
font-size: 28rpx; /* 设置字体大小 */
.msg-item { /* 消息项的样式 */
padding: 20rpx; /* 设置内边距 */
border-top: 2rpx solid #f1f1f1; /* 设置上边框 */
&:first-child { /* 第一个消息项没有上边框 */
background: #fff;
margin-top: 15rpx;
font-size: 28rpx;
.msg-item {
padding: 20rpx;
border-top: 2rpx solid #f1f1f1;
&:first-child {
border: 0;
}
.item { /* 消息项内部的样式 */
display: flex; /* 使用弹性布局 */
padding: 10rpx 0; /* 设置上下内边距 */
align-items: center; /* 垂直居中对齐 */
box-sizing: border-box; /* 设置盒模型 */
.item-tit { /* 项目标题的样式 */
min-width: 140rpx; /* 设置最小宽度 */
color: #999; /* 设置字体颜色为浅灰色 */
line-height: 48rpx; /* 设置行高 */
.item {
display: flex;
padding: 10rpx 0;
align-items: center;
box-sizing: border-box;
.item-tit {
min-width: 140rpx;
color: #999;
line-height: 48rpx;
}
.item-txt { /* 项目文本的样式 */
flex: 1; /* 占据剩余空间 */
line-height: 48rpx; /* 设置行高 */
.item-txt {
flex: 1;
line-height: 48rpx;
}
.item-txt.remarks { /* 备注文本的样式 */
max-width: 600rpx; /* 设置最大宽度 */
white-space: nowrap; /* 不允许换行 */
overflow: hidden; /* 超出内容隐藏 */
text-overflow: ellipsis; /* 超出内容以省略号显示 */
.item-txt.remarks {
max-width: 600rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.copy-btn { /* 复制按钮的样式 */
display: block; /* 作为块级元素显示 */
margin-left: 20rpx; /* 设置左边距 */
border: 2rpx solid #e4e4e4; /* 设置边框 */
padding: 6rpx 24rpx; /* 设置内边距 */
border-radius: 50rpx; /* 设置圆角 */
font-size: 24rpx; /* 设置字体大小 */
line-height: 28rpx; /* 设置行高 */
.copy-btn {
display: block;
margin-left: 20rpx;
border: 2rpx solid #e4e4e4;
padding: 6rpx 24rpx;
border-radius: 50rpx;
font-size: 24rpx;
line-height: 28rpx;
}
.item-txt.price { /* 价格文本的样式 */
text-align: right; /* 文本右对齐 */
.item-txt.price {
text-align: right;
}
}
.item.payment { /* 支付信息的样式 */
border-top: 2rpx solid #f1f1f1; /* 设置上边框 */
color: #eb2444; /* 设置字体颜色为红色 */
padding-top: 30rpx; /* 设置上边距 */
.item.payment {
border-top: 2rpx solid #f1f1f1;
color: #eb2444;
padding-top: 30rpx;
}
}
}
/* 订单详情页脚的样式 */
.order-detail-footer {
position: fixed; /* 固定定位 */
bottom: 0; /* 固定在页面底部 */
width: 100%; /* 设置宽度为100% */
max-width: 750rpx; /* 设置最大宽度 */
background: #fff; /* 设置背景颜色为白色 */
margin: auto; /* 水平居中 */
display: flex; /* 使用弹性布局 */
padding: 22rpx 0; /* 设置上下内边距 */
font-size: 26rpx; /* 设置字体大小 */
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.05); /* 设置阴影效果 */
.dele-order { /* 删除订单按钮的样式 */
margin-left: 20rpx; /* 设置左边距 */
line-height: 60rpx; /* 设置行高 */
display: block; /* 作为块级元素显示 */
margin-right: 20rpx; /* 设置右边距 */
width: 150rpx; /* 设置宽度 */
text-align: center; /* 文本居中 */
position: fixed;
bottom: 0;
width: 100%;
max-width: 750rpx;
background: #fff;
margin: auto;
display: -webkit-flex;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: flex;
padding: 22rpx 0;
font-size: 26rpx;
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.05);
.dele-order {
margin-left: 20rpx;
line-height: 60rpx;
display: block;
margin-right: 20rpx;
width: 150rpx;
text-align: center;
}
.footer-box { /* 页脚容器的样式 */
flex: 1; /* 占据剩余空间 */
text-align: right; /* 文本右对齐 */
line-height: 60rpx; /* 设置行高 */
.buy-again { /* 再次购买按钮的样式 */
font-size: 26rpx; /* 设置字体大小 */
color: #fff; /* 设置字体颜色为白色 */
background: #eb2444; /* 设置背景颜色为红色 */
border-radius: 50rpx; /* 设置圆角 */
padding: 10rpx 20rpx; /* 设置内边距 */
margin-right: 20rpx; /* 设置右边距 */
.footer-box {
flex: 1;
text-align: right;
line-height: 60rpx;
.buy-again {
font-size: 26rpx;
color: #fff;
background: #eb2444;
border-radius: 50rpx;
padding: 10rpx 20rpx;
margin-right: 20rpx;
}
.apply-service { /* 申请服务按钮的样式 */
font-size: 26rpx; /* 设置字体大小 */
border-radius: 50rpx; /* 设置圆角 */
padding: 10rpx 20rpx; /* 设置内边距 */
border: 1px solid #e4e4e4; /* 设置边框 */
margin-right: 20rpx; /* 设置右边距 */
.apply-service {
font-size: 26rpx;
border-radius: 50rpx;
padding: 10rpx 20rpx;
border: 1px solid #e4e4e4;
margin-right: 20rpx;
}
}
}
/* 清除浮动的样式 */
.clearfix {
&:after { /* 使用伪元素清除浮动 */
&:after {
content: " ";
display: table; /* 创建一个新的格式化上下文 */
clear: both; /* 清除所有浮动 */
display: table;
clear: both;
}
}
/* 订单状态的样式 */
.order-state {
height: 70rpx; /* 设置高度 */
line-height: 70rpx; /* 设置行高 */
text-align: right; /* 文本右对齐 */
margin-right: 20rpx; /* 设置右边距 */
.order-sts { /* 订单状态文本的样式 */
color: #eb2444; /* 设置字体颜色为红色 */
font-size: 28rpx; /* 设置字体大小 */
height: 70rpx;
line-height: 70rpx;
text-align: right;
margin-right: 20rpx;
.order-sts {
color: #eb2444;
font-size: 28rpx;
}
.order-sts.gray { /* 灰色状态文本的样式 */
color: #999; /* 设置字体颜色为浅灰色 */
height: 32rpx; /* 设置高度 */
line-height: 32rpx; /* 设置行高 */
.order-sts.gray {
color: #999;
height: 32rpx;
line-height: 32rpx;
}
.order-sts.normal { /* 正常状态文本的样式 */
color: #333; /* 设置字体颜色为深灰色 */
.order-sts.normal {
color: #333;
}
}

@ -1,35 +1,65 @@
<template>
<view class="container">
<view class="order-detail">
<!-- 配送地址信息 -->
<view v-if="userAddrDto" class="delivery-addr">
<view
v-if="userAddrDto"
class="delivery-addr"
>
<view class="user-info">
<text class="item">{{ userAddrDto.receiver }}</text> <!-- 显示收件人姓名 -->
<text class="item">{{ userAddrDto.mobile }}</text> <!-- 显示联系电话 -->
<text class="item">
{{ userAddrDto.receiver }}
</text>
<text class="item">
{{ userAddrDto.mobile }}
</text>
</view>
<view class="addr">
{{ userAddrDto.province }}{{ userAddrDto.city }}{{ userAddrDto.area }}{{ userAddrDto.addr }} <!-- 显示详细地址 -->
{{ userAddrDto.province }}{{ userAddrDto.city }}{{ userAddrDto.area }}{{
userAddrDto.area
}}{{ userAddrDto.addr }}
</view>
</view>
<!-- 商品信息 -->
<view v-if="orderItemDtos" class="prod-item">
<block v-for="(item, index) in orderItemDtos" :key="index">
<view class="item-cont" :data-prodid="item.prodId" @tap="toProdPage">
<view
v-if="orderItemDtos"
class="prod-item"
>
<block
v-for="(item, index) in orderItemDtos"
:key="index"
>
<view
class="item-cont"
:data-prodid="item.prodId"
@tap="toProdPage"
>
<view class="prod-pic">
<image :src="item.pic" /> <!-- 显示商品图片 -->
<image :src="item.pic" />
</view>
<view class="prod-info">
<view class="prodname">{{ item.prodName }}</view> <!-- 显示商品名称 -->
<view class="prodname">
{{ item.prodName }}
</view>
<view class="prod-info-cont">
<text class="number">数量{{ item.prodCount }}</text> <!-- 显示商品数量 -->
<text class="info-item">{{ item.skuName }}</text> <!-- 显示SKU信息 -->
<text class="number">
数量{{ item.prodCount }}
</text>
<text class="info-item">
{{ item.skuName }}
</text>
</view>
<view class="price-nums clearfix">
<text class="prodprice">
<text class="symbol"></text> <!-- 显示价格符号 -->
<text class="big-num">{{ wxs.parsePrice(item.price)[0] }}</text> <!-- 显示整数部分 -->
<text class="small-num">.{{ wxs.parsePrice(item.price)[1] }}</text> <!-- 显示小数部分 -->
<text class="symbol">
</text>
<text class="big-num">
{{ wxs.parsePrice(item.price)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(item.price)[1] }}
</text>
</text>
<view class="btn-box" />
</view>
@ -42,176 +72,228 @@
<view class="order-msg">
<view class="msg-item">
<view class="item">
<text class="item-tit">订单编号</text>
<text class="item-txt">{{ orderNumber }}</text> <!-- 显示订单编号 -->
<text class="item-tit">
订单编号
</text>
<text class="item-txt">
{{ orderNumber }}
</text>
</view>
<view class="item">
<text class="item-tit">下单时间</text>
<text class="item-txt">{{ createTime }}</text> <!-- 显示下单时间 -->
<text class="item-tit">
下单时间
</text>
<text class="item-txt">
{{ createTime }}
</text>
</view>
</view>
<view class="msg-item">
<view class="item">
<text class="item-tit">支付方式</text>
<text class="item-txt">微信支付</text> <!-- 固定显示支付方式为微信支付 -->
<text class="item-tit">
支付方式
</text>
<text class="item-txt">
微信支付
</text>
</view>
<view class="item">
<text class="item-tit">配送方式</text>
<text class="item-txt">普通配送</text> <!-- 固定显示配送方式为普通配送 -->
<text class="item-tit">
配送方式
</text>
<text class="item-txt">
普通配送
</text>
</view>
<view class="item" v-if="!!remarks">
<text class="item-tit">订单备注</text>
<text class="item-txt remarks">{{ remarks }}</text> <!-- 显示订单备注如果有 -->
<view class="item">
<text
v-if="!!remarks"
class="item-tit"
>
订单备注
</text>
<text class="item-txt remarks">
{{ remarks }}
</text>
</view>
</view>
</view>
<!-- 订单金额详情 -->
<view class="order-msg">
<view class="msg-item">
<view class="item">
<view class="item-tit">订单总额</view>
<view class="item-tit">
订单总额
</view>
<view class="item-txt price">
<text class="symbol"></text>
<text class="big-num">{{ wxs.parsePrice(total)[0] }}</text> <!-- 显示订单总额的整数部分 -->
<text class="small-num">.{{ wxs.parsePrice(total)[1] }}</text> <!-- 显示订单总额的小数部分 -->
<text class="symbol">
</text>
<text class="big-num">
{{ wxs.parsePrice(total)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(total)[1] }}
</text>
</view>
</view>
<view class="item">
<view class="item-tit">运费</view>
<view class="item-tit">
运费
</view>
<view class="item-txt price">
<text class="symbol"></text>
<text class="big-num">{{ wxs.parsePrice(transfee)[0] }}</text> <!-- 显示运费的整数部分 -->
<text class="small-num">.{{ wxs.parsePrice(transfee)[1] }}</text> <!-- 显示运费的小数部分 -->
<text class="symbol">
</text>
<text class="big-num">
{{ wxs.parsePrice(transfee)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(transfee)[1] }}
</text>
</view>
</view>
<view class="item">
<view class="item-tit">优惠券</view>
<view class="item-tit">
优惠券
</view>
<view class="item-txt price">
<text class="symbol">-</text>
<text class="big-num">{{ wxs.parsePrice(reduceAmount)[0] }}</text> <!-- 显示优惠券的整数部分 -->
<text class="small-num">.{{ wxs.parsePrice(reduceAmount)[1] }}</text> <!-- 显示优惠券的小数部分 -->
<text class="symbol">
-
</text>
<text class="big-num">
{{ wxs.parsePrice(reduceAmount)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(reduceAmount)[1] }}
</text>
</view>
</view>
<view class="item payment">
<view class="item-txt price">
实付款
<text class="symbol"></text>
<text class="big-num">{{ wxs.parsePrice(actualTotal)[0] }}</text> <!-- 显示实付款的整数部分 -->
<text class="small-num">.{{ wxs.parsePrice(actualTotal)[1] }}</text> <!-- 显示实付款的小数部分 -->
<text class="symbol">
</text>
<text class="big-num">
{{ wxs.parsePrice(actualTotal)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(actualTotal)[1] }}
</text>
</view>
</view>
</view>
</view>
<!-- 底部栏 -->
<view v-if="status === 5 || status === 6" class="order-detail-footer">
<text v-if="status === 5 || status === 6" class="dele-order" @tap="delOrderList">
<view
v-if="status==5||status==6"
class="order-detail-footer"
>
<text
v-if="status==5||status==6"
class="dele-order"
@tap="delOrderList"
>
删除订单
</text> <!-- 如果订单状态为已完成或已取消则显示删除订单按钮 -->
</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad, uni } from '@dcloudio/uni-app'; //
//
const wxs = number();
const wxs = number()
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
loadOrderDetail(options.orderNum); //
});
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
loadOrderDetail(options.orderNum)
})
/**
* 跳转商品详情页
* @param e - 触发事件对象
*/
const toProdPage = (e) => {
const prodid = e.currentTarget.dataset.prodid; // ID
uni.navigateTo({
url: '/pages/prod/prod?prodid=' + prodid //
});
};
/**
* 跳转商品详情页
* @param e
*/
const toProdPage = (e) => {
const prodid = e.currentTarget.dataset.prodid
uni.navigateTo({
url: '/pages/prod/prod?prodid=' + prodid
})
}
//
const remarks = ref(''); //
const orderItemDtos = ref([]); //
const reduceAmount = ref(''); //
const transfee = ref(''); //
const status = ref(0); //
const actualTotal = ref(0); //
const userAddrDto = ref(null); //
const orderNumber = ref(''); //
const createTime = ref(''); //
const total = ref(0); //
/**
* 加载订单数据
* @param orderNum - 订单编号
*/
const loadOrderDetail = (orderNum) => {
uni.showLoading(); //
http.request({
url: '/p/myOrder/orderDetail', // API URL
method: 'GET', //
data: {
orderNumber: orderNum //
}
const remarks = ref('')
const orderItemDtos = ref([])
const reduceAmount = ref('')
const transfee = ref('')
const status = ref(0)
const actualTotal = ref(0)
const userAddrDto = ref(null)
const orderNumber = ref('')
const createTime = ref('')
const total = ref(0) //
/**
* 加载订单数据
*/
const loadOrderDetail = (orderNum) => {
uni.showLoading() //
http.request({
url: '/p/myOrder/orderDetail',
method: 'GET',
data: {
orderNumber: orderNum
}
})
.then(({ data }) => {
orderNumber.value = orderNum
actualTotal.value = data.actualTotal
userAddrDto.value = data.userAddrDto
remarks.value = data.remarks
orderItemDtos.value = data.orderItemDtos
createTime.value = data.createTime
status.value = data.status
transfee.value = data.transfee
reduceAmount.value = data.reduceAmount
total.value = data.total
uni.hideLoading()
})
.then(({ data }) => {
orderNumber.value = orderNum;
actualTotal.value = data.actualTotal;
userAddrDto.value = data.userAddrDto;
remarks.value = data.remarks;
orderItemDtos.value = data.orderItemDtos;
createTime.value = data.createTime;
status.value = data.status;
transfee.value = data.transfee;
reduceAmount.value = data.reduceAmount;
total.value = data.total;
uni.hideLoading(); //
});
};
}
/**
* 删除已完成||已取消的订单
*/
const delOrderList = () => {
uni.showModal({
title: '', //
content: '确定要删除此订单吗?', //
confirmColor: '#eb2444', //
success(res) {
if (res.confirm) {
uni.showLoading(); //
http.request({
url: '/p/myOrder/' + orderNumber.value, // API URL
method: 'DELETE' //
/**
* 删除已完成||已取消的订单
*/
const delOrderList = () => {
uni.showModal({
title: '',
content: '确定要删除此订单吗?',
confirmColor: '#eb2444',
success (res) {
if (res.confirm) {
uni.showLoading()
http.request({
url: '/p/myOrder/' + orderNumber.value,
method: 'DELETE'
})
.then(() => {
uni.hideLoading()
uni.showToast({
title: res || '删除成功',
icon: 'none'
})
setTimeout(() => {
uni.redirectTo({
url: '/pages/orderList/orderList'
})
}, 1000)
})
.then(() => {
uni.hideLoading(); //
uni.showToast({
title: res || '删除成功', //
icon: 'none'
});
setTimeout(() => {
uni.redirectTo({
url: '/pages/orderList/orderList' //
});
}, 1000); // 1
});
}
}
});
};
}
})
}
</script>
<style scoped lang="scss">
@use './order-detail.scss'; /* 引入外部的 SCSS 文件,以定义组件的样式 */
@use './order-detail.scss';
</style>

@ -1,50 +1,40 @@
<style scoped lang="scss" >
/* 定义全局容器 */
.container {
position: absolute; /* 绝对定位相对于最近的非static祖先元素 */
width: 100%; /* 宽度占满整个父容器 */
height: 100%; /* 高度占满整个父容器 */
background-color: #f4f4f4; /* 背景颜色为浅灰色 */
color: #333; /* 默认文本颜色为深灰色 */
position: absolute;
width: 100%;
height: 100%;
background-color: #f4f4f4;
color: #333;
}
/* 订单标题栏 */
.order-tit {
position: fixed; /* 固定定位,不随页面滚动而移动 */
top: 0; /* 紧贴页面顶部 */
display: flex; /* 使用弹性布局 */
justify-content: space-around; /* 子元素在主轴上均匀分布 */
z-index: 999; /* 设置堆叠顺序,确保标题栏位于最上层 */
width: 100%; /* 宽度占满整个屏幕 */
height: 100rpx; /* 高度为100个rpx根据设备宽度自适应 */
line-height: 100rpx; /* 行高与高度一致,使文本垂直居中 */
background-color: #fff; /* 背景颜色为白色 */
border-bottom: 2rpx solid #f4f4f4; /* 下边框为浅灰色细线 */
position: fixed;
top: 0;
display: flex;
justify-content: space-around;
z-index: 999;
width: 100%;
height: 100rpx;
line-height: 100rpx;
background-color: #fff;
border-bottom: 2rpx solid #f4f4f4;
text {
display: block; /* 将文本块级化 */
font-size: 28rpx; /* 字体大小 */
color: #999; /* 文本颜色为浅灰色 */
width: 100rpx; /* 每个文本项宽度 */
text-align: center; /* 文本水平居中 */
display: block;
font-size: 28rpx;
color: 999;
width: 100rpx;
text-align: center;
}
text.on { /* 当前选中的文本样式 */
border-bottom: 4rpx solid #eb2444; /* 底部有红色细线 */
color: #eb2444; /* 文本颜色为红色 */
text.on {
border-bottom: 4rpx solid #eb2444;
color: #eb2444;
}
}
/* 主内容区 */
.main {
padding-top: 15rpx; /* 上内边距,避免被固定标题栏遮挡 */
padding-top: 15rpx;
}
/* 商品项目 */
.prod-item {
background-color: #fff; /* 背景颜色为白色 */
margin-top: 15rpx; /* 上外边距,与上方元素间隔 */
font-size: 28rpx; /* 默认字体大小 */
background-color: #fff;
margin-top: 15rpx;
font-size: 28rpx;
.item-cont {
.prod-pic {
image {
@ -53,199 +43,172 @@
width: 100%;
height: 100%;
}
font-size: 0; /* 去除图片之间的空白 */
display: inline-block; /* 内联块状显示 */
font-size: 0;
display: inline-block;
width: 160rpx;
height: 160rpx;
overflow: hidden; /* 超出部分隐藏 */
background: #fff; /* 背景颜色为白色 */
margin-right: 16rpx; /* 右侧外边距,与文字部分间隔 */
overflow: hidden;
background: #fff;
margin-right: 16rpx;
}
.categories {
white-space: nowrap; /* 强制在同一行内显示 */
white-space: nowrap;
}
display: flex; /* 使用弹性布局 */
align-items: center; /* 交叉轴上居中对齐 */
padding: 20rpx 30rpx; /* 内边距 */
border-radius: 10rpx; /* 圆角 */
background: #fafafa; /* 背景颜色 */
display: flex;
align-items: center;
padding: 20rpx 30rpx;
border-radius: 10rpx;
display: -webkit-flex;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
background: #fafafa;
.prod-info {
margin-left: 10rpx; /* 左侧内边距 */
font-size: 28rpx; /* 字体大小 */
width: 100%; /* 宽度占满剩余空间 */
position: relative; /* 相对定位 */
height: 160rpx; /* 高度 */
flex: 1; /* 占用剩余空间 */
margin-left: 10rpx;
font-size: 28rpx;
width: 100%;
position: relative;
height: 160rpx;
-webkit-flex: 1;
-ms-flex: 1;
-webkit-box-flex: 1;
-moz-box-flex: 1;
flex: 1;
.prodname {
font-size: 28rpx; /* 字体大小 */
line-height: 36rpx; /* 行高 */
max-height: 86rpx; /* 最大高度 */
overflow: hidden; /* 超出部分隐藏 */
display: -webkit-box; /* WebKit浏览器兼容 */
-webkit-line-clamp: 2; /* 限制显示两行 */
-webkit-box-orient: vertical; /* 垂直排列 */
text-overflow: ellipsis; /* 超出部分以省略号表示 */
word-break: break-all; /* 允许在单词内部换行 */
font-size: 28rpx;
line-height: 36rpx;
max-height: 86rpx;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
word-break: break-all;
}
.prod-info-cont {
color: #999; /* 文本颜色为浅灰色 */
line-height: 40rpx; /* 行高 */
margin-top: 10rpx; /* 上外边距 */
font-size: 22rpx; /* 字体大小 */
overflow: hidden; /* 超出部分隐藏 */
display: -webkit-box; /* WebKit浏览器兼容 */
-webkit-line-clamp: 1; /* 限制显示一行 */
-webkit-box-orient: vertical; /* 垂直排列 */
text-overflow: ellipsis; /* 超出部分以省略号表示 */
word-break: break-all; /* 允许在单词内部换行 */
color: #999;
line-height: 40rpx;
margin-top: 10rpx;
font-size: 22rpx;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
word-break: break-all;
}
}
}
.order-num {
padding: 20rpx 30rpx; /* 内边距 */
display: flex; /* 使用弹性布局 */
justify-content: space-between; /* 子元素在主轴上两端对齐 */
font-size: 28rpx; /* 字体大小 */
padding: 20rpx 30rpx;
display: flex;
justify-content: space-between;
font-size: 28rpx;
.clear-btn {
width: 32rpx;
height: 32rpx;
font-size: 0; /* 去除按钮内的空白 */
vertical-align: top; /* 顶部对齐 */
margin-left: 42rpx; /* 左侧外边距 */
position: relative; /* 相对定位 */
font-size: 0;
vertical-align: top;
margin-left: 42rpx;
position: relative;
&::after {
content: " ";
display: block;
position: absolute;
left: -10rpx;
left: -10px;
top: 0rpx;
width: 1px;
height: 32rpx;
background: #ddd; /* 背景色为浅灰色 */
background: #ddd;
}
.clear-list-btn {
width: 100%;
height: 100%;
vertical-align: middle; /* 中间对齐 */
vertical-align: middle;
}
}
}
.total-num {
text-align: right; /* 文本右对齐 */
padding: 20rpx 30rpx; /* 内边距 */
font-size: 28rpx; /* 字体大小 */
text-align: right;
padding: 20rpx 30rpx;
font-size: 28rpx;
.prodprice {
display: inline-block; /* 内联块状显示 */
color: #333; /* 文本颜色为深灰色 */
display: inline-block;
color: #333;
}
.prodcount {
margin-right: 20rpx; /* 右侧外边距 */
margin-right: 20rpx;
}
}
.price-nums {
.prodprice {
color: #333; /* 文本颜色为深灰色 */
position: absolute; /* 绝对定位 */
bottom: 0; /* 紧贴底部 */
color: #333;
position: absolute;
bottom: 0;
}
.prodcount {
position: absolute; /* 绝对定位 */
bottom: 5rpx; /* 紧贴底部并留出一些间距 */
right: 0; /* 紧贴右侧 */
color: #999; /* 文本颜色为浅灰色 */
font-family: verdana; /* 字体家族 */
position: absolute;
bottom: 5rpx;
right: 0;
color: #999;
font-family: verdana;
}
}
.prod-foot {
border-top: 2rpx solid #e6e6e6; /* 顶部边框 */
border-top: 2rpx solid #e6e6e6;
.total {
font-size: 25rpx; /* 字体大小 */
margin-bottom: 20rpx; /* 下外边距 */
padding-bottom: 20rpx; /* 下内边距 */
border-bottom: 2rpx solid #e9eaec; /* 底部边框 */
font-size: 25rpx;
margin-bottom: 20rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #e9eaec;
}
.btn {
display: flex; /* 使用弹性布局 */
align-items: center; /* 交叉轴上居中对齐 */
justify-content: flex-end; /* 主轴上右对齐 */
display: flex;
align-items: center;
justify-content: flex-end;
}
}
}
/* 订单状态 */
.order-state {
display: flex; /* 使用弹性布局 */
align-items: center; /* 交叉轴上居中对齐 */
font-size: 24rpx; /* 字体大小 */
display: flex;
align-items: center;
font-size: 24rpx;
.order-sts.red {
color: #eb2444; /* 文本颜色为红色 */
color: #eb2444;
}
.order-sts.gray {
color: #999; /* 文本颜色为浅灰色 */
color: #999;
}
}
/* 其他按钮悬停效果 */
.other-button-hover {
background-color: blue; /* 悬停时背景颜色为蓝色 */
background-color: blue;
}
/* 按钮悬停效果 */
.button-hover {
background-color: red; /* 悬停时背景颜色为红色 */
background-color: blue; /* 悬停时背景颜色为蓝色(后一个覆盖前一个) */
background-color: red;
background-color: blue;
}
/* 按钮样式 */
.button {
margin-top: 20rpx; /* 上外边距 */
margin-bottom: 20rpx; /* 下外边距 */
margin-left: 10px; /* 左外边距 */
font-size: 26rpx; /* 字体大小 */
background: #fff; /* 背景颜色为白色 */
padding: 10rpx 30rpx; /* 内边距 */
border-radius: 80rpx; /* 圆角 */
border: 2rpx solid #e1e1e1; /* 边框 */
margin-top: 20rpx;
margin-bottom: 20rpx;
margin-left: 10px;
font-size: 26rpx;
background: #fff;
padding: 10rpx 30rpx;
border-radius: 80rpx;
border: 2rpx solid #e1e1e1;
&:last-child {
margin-right: 10rpx; /* 最后一个子元素的右外边距 */
margin-right: 10rpx;
}
}
/* 警告按钮样式 */
.button.warn {
color: #eb2444; /* 文本颜色为红色 */
border-color: #eb2444; /* 边框颜色为红色 */
color: #eb2444;
border-color: #eb2444;
}
/* 空状态样式 */
.empty {
font-size: 24rpx; /* 字体大小 */
margin-top: 100rpx; /* 上外边距 */
text-align: center; /* 文本居中 */
color: #999; /* 文本颜色为浅灰色 */
height: 300rpx; /* 高度 */
line-height: 300rpx; /* 行高与高度一致,使文本垂直居中 */
font-size: 24rpx;
margin-top: 100rpx;
text-align: center;
color: #999;
height: 300rpx;
line-height: 300rpx;
}
</style >

@ -2,89 +2,142 @@
<view class="container">
<!-- 头部菜单 -->
<view class="order-tit">
<!-- 动态生成订单状态标签点击切换显示不同状态的订单列表 -->
<text v-for="(status, index) in statuses"
:key="index"
:data-sts="status.value"
:class="sts === status.value ? 'on' : ''"
@tap="onStsTap">
{{ status.label }}
<text
data-sts="0"
:class="sts==0?'on':''"
@tap="onStsTap"
>
全部
</text>
<text
data-sts="1"
:class="sts==1?'on':''"
@tap="onStsTap"
>
待支付
</text>
<text
data-sts="2"
:class="sts==2?'on':''"
@tap="onStsTap"
>
待发货
</text>
<text
data-sts="3"
:class="sts==3?'on':''"
@tap="onStsTap"
>
待收货
</text>
<text
data-sts="5"
:class="sts==5?'on':''"
@tap="onStsTap"
>
已完成
</text>
</view>
<!-- end 头部菜单 -->
<view class="main">
<!-- 如果订单列表为空则显示提示信息 -->
<view v-if="list.length === 0" class="empty">
<view
v-if="list.length==0"
class="empty"
>
还没有任何相关订单
</view>
<!-- 订单列表 -->
<block v-for="(item, index) in list" :key="index">
<block
v-for="(item, index) in list"
:key="index"
>
<view class="prod-item">
<!-- 订单编号和状态 -->
<view class="order-num">
<text>订单编号{{ item.orderNumber }}</text>
<view class="order-state">
<!-- 根据订单状态动态设置文本颜色并显示相应的状态 -->
<text :class="[
'order-sts',
item.status === 1 ? 'red' : '',
(item.status === 5 || item.status === 6) ? 'gray' : ''
]">
<text
:class="'order-sts ' + (item.status==1?'red':'') + ' ' + ((item.status==5||item.status==6)?'gray':'')"
>
{{
getStatusLabel(item.status)
item.status == 1 ? '待支付' : (item.status == 2 ? '待发货' : (item.status == 3 ? '待收货' : (item.status == 5 ? '已完成' : '已取消')))
}}
</text>
<!-- 已完成或已取消的订单显示清除按钮 -->
<view v-if="item.status === 5 || item.status === 6"
class="clear-btn">
<image src="@/static/images/icon/clear-his.png"
class="clear-list-btn"
:data-ordernum="item.orderNumber"
@tap="delOrderList" />
<view
v-if="item.status==5 || item.status==6"
class="clear-btn"
>
<image
src="@/static/images/icon/clear-his.png"
class="clear-list-btn"
:data-ordernum="item.orderNumber"
@tap="delOrderList"
/>
</view>
</view>
</view>
<!-- 商品列表 -->
<!-- 单个商品时直接显示商品详情 -->
<block v-if="item.orderItemDtos.length === 1">
<block v-for="(prod, index2) in item.orderItemDtos" :key="index2">
<!-- 一个订单单个商品的显示 -->
<block v-if="item.orderItemDtos.length==1">
<block
v-for="(prod, index2) in item.orderItemDtos"
:key="index2"
>
<view>
<view class="item-cont"
:data-ordernum="item.orderNumber"
@tap="toOrderDetailPage">
<view
class="item-cont"
:data-ordernum="item.orderNumber"
@tap="toOrderDetailPage"
>
<view class="prod-pic">
<image :src="prod.pic" />
</view>
<view class="prod-info">
<view class="prodname">{{ prod.prodName }}</view>
<view class="prod-info-cont">{{ prod.skuName }}</view>
<view class="prodname">
{{ prod.prodName }}
</view>
<view class="prod-info-cont">
{{ prod.skuName }}
</view>
<view class="price-nums">
<text class="prodprice">
<text class="symbol"></text>
<text class="big-num">{{ wxs.parsePrice(prod.price)[0] }}</text>
<text class="small-num">.{{ wxs.parsePrice(prod.price)[1] }}</text>
<text class="symbol">
</text>
<text class="big-num">
{{ wxs.parsePrice(prod.price)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(prod.price)[1] }}
</text>
</text>
<text class="prodcount">
x{{ prod.prodCount }}
</text>
<text class="prodcount">x{{ prod.prodCount }}</text>
</view>
</view>
</view>
</view>
</block>
</block>
<!-- 多个商品时使用横向滚动视图展示所有商品图片 -->
<!-- 一个订单多个商品时的显示 -->
<block v-else>
<view class="item-cont"
:data-ordernum="item.orderNumber"
@tap="toOrderDetailPage">
<scroll-view scroll-x="true"
scroll-left="0"
scroll-with-animation="false"
class="categories">
<block v-for="(prod, index2) in item.orderItemDtos" :key="index2">
<view
class="item-cont"
:data-ordernum="item.orderNumber"
@tap="toOrderDetailPage"
>
<scroll-view
scroll-x="true"
scroll-left="0"
scroll-with-animation="false"
class="categories"
>
<block
v-for="(prod, index2) in item.orderItemDtos"
:key="index2"
>
<view class="prod-pic">
<image :src="prod.pic" />
</view>
@ -93,47 +146,60 @@
</view>
</block>
<!-- 显示订单总价 -->
<view class="total-num">
<text class="prodcount">共1件商品</text>
<text class="prodcount">
共1件商品
</text>
<view class="prodprice">
合计
<text class="symbol"></text>
<text class="big-num">{{ wxs.parsePrice(item.actualTotal)[0] }}</text>
<text class="small-num">.{{ wxs.parsePrice(item.actualTotal)[1] }}</text>
<text class="symbol">
</text>
<text class="big-num">
{{ wxs.parsePrice(item.actualTotal)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(item.actualTotal)[1] }}
</text>
</view>
</view>
<!-- 操作按钮 -->
<!-- end 商品列表 -->
<view class="prod-foot">
<view class="btn">
<!-- 根据订单状态显示不同的操作按钮 -->
<text v-if="item.status === 1"
class="button"
:data-ordernum="item.orderNumber"
hover-class="none"
@tap="onCancelOrder">
<text
v-if="item.status==1"
class="button"
:data-ordernum="item.orderNumber"
hover-class="none"
@tap="onCancelOrder"
>
取消订单
</text>
<text v-if="item.status === 1"
class="button warn"
:data-ordernum="item.orderNumber"
hover-class="none"
@tap="normalPay">
<text
v-if="item.status==1"
class="button warn"
:data-ordernum="item.orderNumber"
hover-class="none"
@tap="normalPay"
>
付款
</text>
<text v-if="item.status === 3 || item.status === 5"
class="button"
:data-ordernum="item.orderNumber"
hover-class="none"
@tap="toDeliveryPage">
<text
v-if="item.status==3 || item.status==5"
class="button"
:data-ordernum="item.orderNumber"
hover-class="none"
@tap="toDeliveryPage"
>
查看物流
</text>
<text v-if="item.status === 3"
class="button warn"
:data-ordernum="item.orderNumber"
hover-class="none"
@tap="onConfirmReceive">
<text
v-if="item.status==3"
class="button warn"
:data-ordernum="item.orderNumber"
hover-class="none"
@tap="onConfirmReceive"
>
确认收货
</text>
</view>
@ -142,202 +208,217 @@
</block>
</view>
</view>
<!-- end 订单列表 -->
</template>
<script setup>
import { ref, onMounted, onReachBottom } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import http from '@/utils/http'
import number from '@/wxs/number.wxs'
const wxs = number()
//
const statuses = [
{ label: '全部', value: 0 },
{ label: '待支付', value: 1 },
{ label: '待发货', value: 2 },
{ label: '待收货', value: 3 },
{ label: '已完成', value: 5 }
]
const sts = ref(0)
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
if (options.sts) {
sts.value = options.sts
loadOrderData(options.sts, 1)
} else {
loadOrderData(0, 1)
}
})
//
const sts = ref(0)
const current = ref(1)
const pages = ref(0)
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom(() => {
if (current.value < pages.value) {
loadOrderData(sts.value, current.value + 1)
}
})
//
onLoad((options) => {
if (options.sts) {
sts.value = parseInt(options.sts)
const list = ref([])
/**
* 加载订单数据
*/
const loadOrderData = (sts, currentParam) => {
uni.showLoading() //
http.request({
url: '/p/myOrder/myOrder',
method: 'GET',
data: {
current: currentParam,
size: 10,
status: sts
}
loadOrderData(sts.value, 1)
})
//
const current = ref(1)
const pages = ref(0)
//
const list = ref([])
//
const loadOrderData = (status, currentPage) => {
uni.showLoading() //
http.request({
url: '/p/myOrder/myOrder',
method: 'GET',
data: {
current: currentPage,
size: 10,
status: status
.then(({ data }) => {
let listParam = []
if (data.current === 1) {
listParam = data.records
} else {
listParam = list.value
Array.prototype.push.apply(listParam, data.records)
}
list.value = listParam
pages.value = data.pages
current.value = data.current
uni.hideLoading()
})
.then(({ data }) => {
//
let listParam = []
if (currentPage === 1) {
listParam = data.records
} else {
listParam = list.value
Array.prototype.push.apply(listParam, data.records)
}
list.value = listParam
pages.value = data.pages
current.value = currentPage
uni.hideLoading() //
})
}
//
const onStsTap = (e) => {
const selectedStatus = e.currentTarget.dataset.sts
sts.value = parseInt(selectedStatus)
loadOrderData(sts.value, 1)
}
}
//
const getStatusLabel = (status) => {
return statuses.find(s => s.value === status)?.label || '未知状态'
}
/**
* 状态点击事件
*/
const onStsTap = (e) => {
sts.value = e.currentTarget.dataset.sts
loadOrderData(sts.value, 1)
}
//
const toDeliveryPage = (e) => {
uni.navigateTo({
url: `/pages/express-delivery/express-delivery?orderNum=${e.currentTarget.dataset.ordernum}`
})
}
/**
* 查看物流
*/
const toDeliveryPage = (e) => {
uni.navigateTo({
url: '/pages/express-delivery/express-delivery?orderNum=' + e.currentTarget.dataset.ordernum
})
}
//
const onCancelOrder = (e) => {
const ordernum = e.currentTarget.dataset.ordernum
uni.showModal({
title: '',
content: '要取消此订单?',
confirmColor: '#3e62ad',
cancelColor: '#3e62ad',
cancelText: '否',
confirmText: '是',
/**
* 取消订单
*/
const onCancelOrder = (e) => {
const ordernum = e.currentTarget.dataset.ordernum
uni.showModal({
title: '',
content: '要取消此订单?',
confirmColor: '#3e62ad',
cancelColor: '#3e62ad',
cancelText: '否',
confirmText: '是',
success(res) {
if (res.confirm) {
uni.showLoading({ mask: true })
http.request({
url: `/p/myOrder/cancel/${ordernum}`,
method: 'PUT'
success (res) {
if (res.confirm) {
uni.showLoading({
mask: true
})
http.request({
url: '/p/myOrder/cancel/' + ordernum,
method: 'PUT',
data: {}
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
})
}
}
})
}
}
})
}
//
const normalPay = (e) => {
uni.showLoading({ mask: true })
http.request({
url: '/p/order/normalPay',
method: 'POST',
data: {
orderNumbers: e.currentTarget.dataset.ordernum
/**
* 模拟支付直接提交成功
* @param e
*/
const normalPay = (e) => {
uni.showLoading({
mask: true
})
http.request({
url: '/p/order/normalPay',
method: 'POST',
data: {
orderNumbers: e.currentTarget.dataset.ordernum
}
})
.then(({ data }) => {
uni.hideLoading()
if (data) {
uni.showToast({
title: '模拟支付成功',
icon: 'none'
})
setTimeout(() => {
uni.navigateTo({
url: '/pages/pay-result/pay-result?sts=1&orderNumbers=' + e.currentTarget.dataset.ordernum
})
}, 1200)
} else {
uni.showToast({
title: '支付失败!',
icon: 'none'
})
}
})
.then(({ data }) => {
uni.hideLoading()
if (data) {
uni.showToast({
title: '模拟支付成功',
icon: 'none'
})
setTimeout(() => {
uni.navigateTo({
url: `/pages/pay-result/pay-result?sts=1&orderNumbers=${e.currentTarget.dataset.ordernum}`
})
}, 1200)
} else {
uni.showToast({
title: '支付失败!',
icon: 'none'
})
}
})
}
}
//
const toOrderDetailPage = (e) => {
uni.navigateTo({
url: `/pages/order-detail/order-detail?orderNum=${e.currentTarget.dataset.ordernum}`
})
}
/**
* 查看订单详情
*/
const toOrderDetailPage = (e) => {
uni.navigateTo({
url: '/pages/order-detail/order-detail?orderNum=' + e.currentTarget.dataset.ordernum
})
}
//
const onConfirmReceive = (e) => {
uni.showModal({
title: '',
content: '我已收到货?',
confirmColor: '#eb2444',
/**
* 确认收货
*/
const onConfirmReceive = (e) => {
uni.showModal({
title: '',
content: '我已收到货?',
confirmColor: '#eb2444',
success(res) {
if (res.confirm) {
uni.showLoading({ mask: true })
http.request({
url: `/p/myOrder/receipt/${e.currentTarget.dataset.ordernum}`,
method: 'PUT'
success (res) {
if (res.confirm) {
uni.showLoading({
mask: true
})
http.request({
url: '/p/myOrder/receipt/' + e.currentTarget.dataset.ordernum,
method: 'PUT'
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
})
}
}
})
}
}
})
}
//
const delOrderList = (e) => {
uni.showModal({
title: '',
content: '确定要删除此订单吗?',
confirmColor: '#eb2444',
/**
* 删除已完成||已取消的订单
* @param e
*/
const delOrderList = (e) => {
uni.showModal({
title: '',
content: '确定要删除此订单吗?',
confirmColor: '#eb2444',
success(res) {
if (res.confirm) {
const ordernum = e.currentTarget.dataset.ordernum
uni.showLoading()
success (res) {
if (res.confirm) {
const ordernum = e.currentTarget.dataset.ordernum
uni.showLoading()
http.request({
url: `/p/myOrder/${ordernum}`,
method: 'DELETE'
http.request({
url: '/p/myOrder/' + ordernum,
method: 'DELETE'
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
})
}
}
})
}
}
})
}
</script>
<style scoped lang="scss">
@use './orderList.scss';
@use './orderList.scss';
</style>

@ -1,92 +1,50 @@
/* 定义支付状态信息的样式 */
.pay-sts {
/* 设置字体大小为40rpx */
font-size: 40rpx;
/* 设置顶部外边距为100rpx使支付状态信息与页面顶部有一定的间距 */
margin-top: 100rpx;
/* 设置上下内边距为30rpx左右内边距为0确保文本有足够的内部空间 */
padding: 30rpx 0;
/* 文本居中对齐 */
text-align: center;
}
/* 当支付状态为失败时,设置文本颜色为红色(#f43530以视觉上突出错误信息 */
.pay-sts.fail {
color: #f43530;
}
/* 当支付状态为成功时,设置文本颜色为绿色(#19be6b表示操作成功 */
.pay-sts.succ {
color: #19be6b;
}
/* 定义按钮组的样式 */
.btns {
/* 设置顶部外边距为50rpx使按钮组与上方内容有一定的间距 */
margin-top: 50rpx;
/* 文本居中对齐 */
text-align: center;
/* 定义默认按钮样式 */
.button {
/* 设置圆角半径为10rpx使按钮看起来更加圆润 */
border-radius: 10rpx;
/* 设置字体大小为28rpx */
font-size: 28rpx;
/* 设置背景颜色为白色 */
background: #fff;
/* 设置文本颜色为深灰色 */
color: #333;
/* 设置上下内边距为20rpx左右内边距为35rpx确保按钮内的文本有足够的内部空间 */
padding: 20rpx 35rpx;
/* 设置按钮宽度为300rpx确保按钮在不同屏幕尺寸下都能保持一致的宽度 */
width: 300rpx;
/* 设置左右外边距为20rpx确保按钮之间有一定的间距 */
margin: 0 20rpx;
/* 文本居中对齐 */
text-align: center;
}
/* 定义“查看订单”按钮的样式 */
.button.checkorder {
/* 设置背景颜色为绿色(#19be6b表示这是一个积极的操作 */
background: #19be6b;
/* 设置文本颜色为白色 */
color: #fff;
/* 设置底部外边距为20rpx使按钮与其他元素之间有一定的间距 */
margin-bottom: 20rpx;
/* 设置边框为2rpx宽的绿色实线 */
border: 2rpx solid #19be6b;
}
/* 定义“再次支付”按钮的样式 */
.button.payagain {
/* 设置背景颜色为白色 */
background: #fff;
/* 设置边框为2rpx宽的橙色实线 */
border: 2rpx solid #f90;
/* 设置文本颜色为橙色 */
color: #f90;
}
/* 定义“继续购物”按钮的样式 */
.button.shopcontinue {
/* 设置背景颜色为白色 */
background: #fff;
/* 设置边框为2rpx宽的绿色实线 */
border: 2rpx solid #19be6b;
/* 设置文本颜色为绿色 */
color: #19be6b;
}
}
/* 定义提示信息的样式 */
.tips {
/* 设置字体大小为28rpx */
font-size: 28rpx;
/* 设置文本颜色为浅灰色(#999使提示信息显得不那么显眼 */
color: #999;
/* 文本居中对齐 */
text-align: center;
/* 定义警告信息的样式 */
.warn {
/* 设置文本颜色为红色(#f43530用于突出显示警告或错误信息 */
color: #f43530;
}
}

@ -1,61 +1,52 @@
<template>
<!-- 定义页面的根容器 -->
<view class="container">
<!-- 当支付状态为失败sts == 0时显示的内容 -->
<view v-if="sts == 0">
<!-- 显示支付失败的状态信息 -->
<view class="pay-sts fail">
支付失败
</view>
<!-- 提示用户在30分钟内完成付款否则订单会被取消 -->
<view class="tips">
请在
<text class="warn">30分钟</text>内完成付款
<text class="warn">
30分钟
</text>内完成付款
</view>
<view class="tips">
否则订单会被系统取消
</view>
<!-- 按钮组提供查看订单重新支付的操作选项 -->
<view class="btns">
<!-- 查看订单按钮点击后跳转到订单列表页面 -->
<text class="button checkorder"
@tap="toOrderList">
<text
class="button checkorder"
@tap="toOrderList"
>
查看订单
</text>
<!-- 重新支付按钮点击后触发重新支付的操作 -->
<text class="button payagain"
@tap="payAgain">
<text
class="button payagain"
@tap="payAgain"
>
重新支付
</text>
</view>
</view>
<!-- 当支付状态为成功sts == 1时显示的内容 -->
<view v-if="sts == 1">
<!-- 显示支付成功的状态信息 -->
<view class="pay-sts succ">
支付成功
</view>
<!-- 感谢用户的购买 -->
<view class="tips">
感谢您的购买
</view>
<!-- 按钮组提供查看订单继续购物的操作选项 -->
<view class="btns">
<!-- 查看订单按钮点击后跳转到订单列表页面 -->
<text class="button checkorder"
@tap="toOrderList">
<text
class="button checkorder"
@tap="toOrderList"
>
查看订单
</text>
<!-- 继续购物按钮点击后返回首页 -->
<text class="button shopcontinue"
@tap="toIndex">
<text
class="button shopcontinue"
@tap="toIndex"
>
继续购物
</text>
</view>
@ -64,87 +55,56 @@
</template>
<script setup>
// Vueref
import { ref } from 'vue';
// uni-appAPI
import { onLoad, navigateTo, switchTab, showLoading, hideLoading, requestPayment, redirectTo } from '@dcloudio/uni-app';
// HTTP
import http from '@/utils/http';
//
const sts = ref(0); // 0
const orderNumbers = ref(''); //
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
//
sts.value = parseInt(options.sts, 10); //
orderNumbers.value = options.orderNumbers;
});
//
const toOrderList = () => {
navigateTo({
url: '/pages/orderList/orderList?sts=0' // sts=0
});
};
//
const toIndex = () => {
switchTab({
url: '/pages/index/index' //
});
};
//
const payAgain = () => {
//
showLoading({
mask: true //
});
//
http.request({
url: '/p/order/pay', // URL
method: 'POST', // HTTP
data: {
payType: 1, // 1
orderNumbers: orderNumbers.value //
}
})
.then(({ data }) => {
//
hideLoading();
//
requestPayment({
timeStamp: data.timeStamp, //
nonceStr: data.nonceStr, //
package: data.packageValue, //
signType: data.signType, //
paySign: data.paySign, //
success: () => {
//
redirectTo({
url: `/pages/pay-result/pay-result?sts=1&orderNum=${orderNumbers.value}`
});
},
fail: (err) => {
console.error('支付失败:', err);
//
}
});
const sts = ref(0)
const orderNumbers = ref('')
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
sts.value = options.sts
orderNumbers.value = options.orderNumbers
})
const toOrderList = () => {
uni.navigateTo({
url: '/pages/orderList/orderList?sts=0'
})
}
const toIndex = () => {
uni.switchTab({
url: '/pages/index/index'
})
}
const payAgain = () => {
uni.showLoading({
mask: true
})
http.request({
url: '/p/order/pay',
method: 'POST',
data: {
payType: 1,
orderNumbers: orderNumbers.value
}
})
.then(({ data }) => {
uni.hideLoading()
uni.requestPayment({
timeStamp: data.timeStamp,
nonceStr: data.nonceStr,
package: data.packageValue,
signType: data.signType,
paySign: data.paySign,
success: () => {
uni.redirectTo({
url: '/pages/pay-result/pay-result?sts=1&orderNum=' + orderNumbers.value
})
}
})
.catch(err => {
console.error('发起支付请求失败:', err);
//
});
};
})
}
</script>
<style scoped lang="scss">
/* 使用本地的SCSS文件来定义样式 */
@use './pay-result.scss';
@use './pay-result.scss';
</style>

@ -1,48 +1,38 @@
/* 容器样式 */
.container {
background: #f4f4f4; /* 设置背景颜色为浅灰色 */
min-height: 100vh; /* 最小高度为视口高度,确保内容不满屏时也有足够的空间 */
padding: 7px; /* 内边距,给容器内的元素留出一定的空间 */
background: #f4f4f4;
min-height: 100vh;
padding: 7px;
}
/* 固定在顶部的线条 */
.line-fix {
width: 100%; /* 线条宽度为100%,覆盖整个屏幕宽度 */
height: 2rpx; /* 线条高度为2rpx响应式单位非常细的一条线 */
background: #e1e1e1; /* 线条颜色为浅灰色 */
position: fixed; /* 将线条固定在页面顶部,不会随页面滚动而移动 */
top: 0; /* 线条距离页面顶部0像素 */
width: 100%;
height: 2rpx;
background: #e1e1e1;
position: fixed;
top: 0;
}
/* 标题背景样式 */
.tit-background {
width: 100%; /* 背景宽度为100%,覆盖整个屏幕宽度 */
height: 20rpx; /* 背景高度为20rpx响应式单位 */
background: #f4f4f4; /* 背景颜色为浅灰色 */
width: 100%;
height: 20rpx;
background: #f4f4f4;
}
/* 商品列表样式 */
.prod-list {
display: flex; /* 使用弹性布局,使子元素可以灵活排列 */
flex-wrap: wrap; /* 允许子元素换行显示 */
justify-content: space-between; /* 子元素之间均匀分布,两端对齐 */
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
/* 深度选择器,用于修改子组件的样式 */
:deep(.prod-items) {
display: inline-block; /* 将商品项设置为行内块级元素,允许在同一行中显示多个商品 */
width: 345rpx !important; /* 设置商品项的宽度为345rpx并使用!important确保优先级 */
&:nth-child(2n) { /* 对于每第二个商品项 */
margin-left: 30rpx; /* 左边距为30rpx使第二列的商品与第一列保持一定间距 */
display: inline-block;
width: 345rpx !important;
&:nth-child(2n) {
margin-left: 30rpx;
}
}
/* 空状态提示样式 */
/* 空 */
.empty {
display: block; /* 将空状态提示设置为块级元素 */
width: 100%; /* 提示宽度为100%,覆盖整个父容器 */
font-size: 26rpx; /* 字体大小为26rpx响应式单位 */
color: #999; /* 文字颜色为浅灰色 */
margin-top: 20vh; /* 上边距为视口高度的20%,将提示居中显示 */
text-align: center; /* 文字水平居中 */
display: block;
width: 100%;
font-size: 26rpx;
color: #999;
margin-top: 20vh;
text-align: center;
}

@ -1,13 +1,16 @@
<template>
<view class="container">
<view>
<!-- 使用v-for循环渲染商品列表 -->
<block v-for="(item, index) in prodList" :key="index">
<!-- 将每个商品项传递给production组件 -->
<block
v-for="(item, index) in prodList"
:key="index"
>
<production :item="item" />
</block>
<!-- 如果没有商品数据则显示暂无数据的提示信息 -->
<view v-if="!prodList.length" class="empty">
<view
v-if="!prodList.length"
class="empty"
>
暂无数据
</view>
</view>
@ -15,241 +18,215 @@
</template>
<script setup>
//
const sts = ref(0) //
const title = ref('') //
const current = ref(1) //
const size = ref(10) //
const pages = ref(0) //
const tagid = ref(0) // ID
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
//
current.value = 1
pages.value = 0
// ststitle
sts.value = options.sts
title.value = options.title ? options.title : ''
// tagidID
if (options.tagid) {
tagid.value = options.tagid
}
const sts = ref(0)
const title = ref('')
const current = ref(1)
const size = ref(10)
const pages = ref(0)
const tagid = ref(0)
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
current.value = 1
pages.value = 0
sts.value = options.sts
title.value = options.title ? options.title : ''
if (options.tagid) {
tagid.value = options.tagid
}
// ststagid
if (sts.value == 0) {
switch (options.tagid) {
case '1':
uni.setNavigationBarTitle({ title: '每日上新' })
break
case '2':
uni.setNavigationBarTitle({ title: '商城热卖' })
break
case '3':
uni.setNavigationBarTitle({ title: '更多宝贝' })
break
}
} else {
switch (sts.value) {
case 1:
uni.setNavigationBarTitle({ title: '新品推荐' })
break
case 2:
uni.setNavigationBarTitle({ title: '限时特惠' })
break
case 3:
uni.setNavigationBarTitle({ title: '每日疯抢' })
break
case 4:
uni.setNavigationBarTitle({ title: '优惠券活动商品' })
break
case 5:
uni.setNavigationBarTitle({ title: '我的收藏商品' })
break
default:
uni.setNavigationBarTitle({ title: title.value })
}
if (sts.value == 0) {
if (options.tagid == 1) {
uni.setNavigationBarTitle({
title: '每日上新'
})
} else if (options.tagid == 2) {
uni.setNavigationBarTitle({
title: '商城热卖'
})
} else if (options.tagid == 3) {
uni.setNavigationBarTitle({
title: '更多宝贝'
})
}
} else if (sts.value == 1) {
uni.setNavigationBarTitle({
title: '新品推荐'
})
} else if (sts.value == 2) {
uni.setNavigationBarTitle({
title: '限时特惠'
})
} else if (sts.value == 3) {
uni.setNavigationBarTitle({
title: '每日疯抢'
})
} else if (sts.value == 4) {
uni.setNavigationBarTitle({
title: '优惠券活动商品'
})
} else if (sts.value == 5) {
uni.setNavigationBarTitle({
title: '我的收藏商品'
})
} else {
uni.setNavigationBarTitle({
title: title.value
})
}
//
loadProdData(options)
})
loadProdData(options)
})
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom(() => {
//
if (current.value < pages.value) {
current.value++
loadProdData()
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom(() => {
if (current.value < pages.value) {
current.value = current.value + 1
loadProdData()
}
})
/**
* 加载商品数据
*/
const loadProdData = (options) => {
const stsParam = sts.value
if (stsParam == 0) {
//
getTagProd()
} else if (stsParam == 1) {
//
const url = '/prod/lastedProdPage'
getActProd(url)
} else if (stsParam == 2) {
//
const url = '/prod/discountProdList'
getActProd(url)
} else if (stsParam == 3) {
//
const url = '/prod/moreBuyProdList'
getActProd(url)
} else if (stsParam == 4) {
//
getProdByCouponId(options.tagid)
} else if (stsParam == 5) {
//
getCollectionProd()
}
}
const prodList = ref([])
const getActProd = (url) => {
uni.showLoading()
http.request({
url,
method: 'GET',
data: {
current: current.value,
size: size.value
}
})
/**
* 加载商品数据
*/
const loadProdData = (options) => {
const stsParam = sts.value
// stsParam
if (stsParam == 0) {
//
getTagProd()
} else if (stsParam == 1) {
//
const url = '/prod/lastedProdPage'
getActProd(url)
} else if (stsParam == 2) {
//
const url = '/prod/discountProdList'
getActProd(url)
} else if (stsParam == 3) {
//
const url = '/prod/moreBuyProdList'
getActProd(url)
} else if (stsParam == 4) {
//
getProdByCouponId(options.tagid)
} else if (stsParam == 5) {
//
getCollectionProd()
}
}
//
const prodList = ref([])
/**
* 获取活动商品列表
*/
const getActProd = (url) => {
//
uni.showLoading()
// HTTP
http.request({
url,
method: 'GET',
data: {
current: current.value,
size: size.value
}
}).then(({ data }) => {
.then(({ data }) => {
let list
//
if (data.current === 1) {
list = data.records
} else {
list = prodList.value.concat(data.records)
list = prodList.value
list = list.concat(data.records)
}
//
prodList.value = list
pages.value = data.pages
//
uni.hideLoading()
})
}
/**
* 获取我的收藏商品
*/
const getCollectionProd = () => {
//
uni.showLoading()
// HTTP
http.request({
url: '/p/user/collection/prods',
method: 'GET',
data: {
current: current.value,
size: size.value
}
}).then(({ data }) => {
}
/**
* 获取我的收藏商品
*/
const getCollectionProd = () => {
uni.showLoading()
http.request({
url: '/p/user/collection/prods',
method: 'GET',
data: {
current: current.value,
size: size.value
}
})
.then(({ data }) => {
let list
//
if (data.current == 1) {
list = data.records
} else {
list = prodList.value.concat(data.records)
list = prodList.value
list = list.concat(data.records)
}
//
prodList.value = list
pages.value = data.pages
//
uni.hideLoading()
})
}
/**
* 获取标签商品列表
*/
const getTagProd = () => {
//
uni.showLoading()
// HTTP
http.request({
url: '/prod/prodListByTagId',
method: 'GET',
data: {
tagId: tagid.value,
current: current.value,
size: size.value
}
}).then(({ data }) => {
}
/**
* 获取标签列表
*/
const getTagProd = () => {
uni.showLoading()
http.request({
url: '/prod/prodListByTagId',
method: 'GET',
data: {
tagId: tagid.value,
current: current.value,
size: size.value
}
})
.then(({ data }) => {
let list
//
if (data.current === 1) {
list = data.records
} else {
list = prodList.value.concat(data.records)
}
//
prodList.value = list
pages.value = data.pages
//
uni.hideLoading()
})
}
/**
* 获取优惠券商品列表
*/
const getProdByCouponId = (id) => {
//
uni.showLoading()
// HTTP
http.request({
url: '/coupon/prodListByCouponId',
method: 'GET',
data: {
couponId: id,
current: current.value,
size: size.value
}
}).then(({ data }) => {
}
/**
* 获取优惠券商品列表
*/
const getProdByCouponId = (id) => {
uni.showLoading()
http.request({
url: '/coupon/prodListByCouponId',
method: 'GET',
data: {
couponId: id,
current: current.value,
size: size.value
}
})
.then(({ data }) => {
let list
//
if (data.current === 1) {
list = data.records
} else {
list = prodList.value.concat(data.records)
}
//
prodList.value = list
pages.value = data.pages
//
uni.hideLoading()
})
}
}
</script>
<style scoped lang="scss">
@use './prod-classify.scss'; /* 引入外部样式文件 */
@use './prod-classify.scss';
</style>

@ -1,35 +1,26 @@
.c /* 100%150rpx */
container {
.container {
background: #f4f4f4;
height: 100%;
padding-bottom: 150rpx;
}
/* 轮播图组件,设定了高度、宽度以及下边框。内部图片也设定了相同的高度和宽度以确保适应轮播图容器 */
swiper {
height: 750rpx;
width: 100%;
border-bottom: 2rpx solid #f8f8f8;
image {
height: 750rpx;
width: 100%;
}
}
/* 产品信息区域,设置了上下左右的内边距,相对定位,白色背景 */
.prod-info {
padding: 30rpx 30rpx 0 30rpx;
position: relative;
background: #fff;
}
/* 标题包装器,使用了相对定位来放置子元素,并定义了行高和右边距 */
.tit-wrap {
position: relative;
line-height: 40rpx;
padding-right: 104rpx;
/* 列元素,绝对定位在标题包装器的右上角,作为额外的信息展示,如收藏按钮等 */
.col {
position: absolute;
top: 0;
@ -39,14 +30,12 @@ swiper {
font-size: 20rpx;
padding-left: 20rpx;
text-align: center;
/* 列内的图标,设置为块级元素并居中显示 */
image {
display: block;
margin: auto;
width: 40rpx;
height: 40rpx;
}
/* 在列元素左侧添加一条竖线装饰 */
&::after {
content: "";
display: block;
@ -60,15 +49,11 @@ swiper {
}
}
}
/* 产品标题,设定字体大小、颜色和右边距 */
.prod-tit {
font-size: 32rpx;
color: #333;
padding-right: 20rpx;
}
/* 销量段落,设置了背景色、行高、颜色、字体大小、上边距和右边距 */
.sales-p {
background: #fff;
line-height: 40rpx;
@ -77,38 +62,29 @@ swiper {
margin-top: 6rpx;
margin-right: 104rpx;
}
/* 产品价格部分,包含新旧价格对比 */
.prod-price {
font-size: 30rpx;
height: 100rpx;
line-height: 100rpx;
/* 新价格的颜色、字体大小、加粗和右边距 */
.price {
color: #eb2444;
font-size: 24rpx;
font-weight: 600;
margin-right: 30rpx;
}
/* 新价格的数字部分,加大字体 */
.price-num {
font-size: 46rpx;
font-weight: 400;
}
/* 原价,较小字体,灰色,带删除线 */
.ori-price {
font-size: 25rpx;
color: #999;
text-decoration: line-through;
}
}
/* 销量文字的颜色 */
.price {
color: #eb2444;
font-size: 24rpx;
font-weight: 600;
margin-right: 30rpx;
}
.price-num {
font-size: 46rpx;
font-weight: 400;
}
.ori-price {
font-size: 25rpx;
color: #999;
text-decoration: line-through;
}
.sales {
color: #999;
}
/* 更多选项按钮,绝对定位在右侧,设置了宽度、顶部位置、文本对齐、字体大小和颜色 */
.more {
position: absolute;
right: 20rpx;
@ -119,280 +95,243 @@ swiper {
color: #999;
letter-spacing: 1px;
}
/* SKU库存保有单位信息设置了内边距、背景色、外边距、相对定位和行高 */
.sku {
padding: 20rpx;
background: #fff;
margin-top: 20rpx;
position: relative;
line-height: 48rpx;
/* SKU标题绝对定位在左上角作为SKU信息的标签 */
.sku-tit {
position: absolute;
display: inline-block;
width: 60rpx;
left: 20rpx;
font-size: 22rpx;
top: 20rpx;
color: #999;
}
/* SKU内容从标题右侧开始单行显示超出部分用省略号代替 */
.sku-con {
margin: 0 80rpx;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: 28rpx;
font-weight: bold;
}
}
/* 评论区包裹,设置了背景色、外边距、相对定位和行高 */
.sku-tit {
position: absolute;
display: inline-block;
width: 60rpx;
left: 20rpx;
font-size: 22rpx;
top: 20rpx;
color: #999;
}
.sku-con {
margin: 0 80rpx;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: 28rpx;
font-weight: bold;
}
.cmt-wrap {
background: #fff;
margin-top: 20rpx;
position: relative;
line-height: 48rpx;
/* 评论区标题,较大字体,带有下边框,内边距 */
.cmt-tit {
font-size: 32rpx;
position: relative;
border-bottom: 1px solid #ddd;
padding: 20rpx;
}
/* 评论区的好评率,红色字体,稍小字号 */
.cmt-good {
color: #eb2444;
font-size: 24rpx;
}
/* 评论数量,绝对定位在右上角,设置了字体大小和颜色 */
.cmt-count {
position: absolute;
right: 20rpx;
top: 20rpx;
font-size: 24rpx;
color: #666;
}
/* 展示更多评论的箭头图标,通过旋转创建 */
.cmt-more {
width: 20rpx;
height: 20rpx;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
margin-left: 10rpx;
}
.cmt-tit {
font-size: 32rpx;
position: relative;
border-bottom: 1px solid #ddd;
padding: 20rpx;
}
.cmt-t {
width: 300rpx;
}
.cmt-good {
color: #eb2444;
font-size: 24rpx;
}
.cmt-count {
position: absolute;
right: 20rpx;
top: 20rpx;
font-size: 24rpx;
color: #666;
}
.cmt-more {
width: 20rpx;
height: 20rpx;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
margin-left: 10rpx;
display: inline-block;
}
.cmt-cont {
padding: 0 20rpx;
}
.cmt-tag {
position: relative;
padding: 14px 3px 0 0;
margin: 0;
text {
margin: 0 10px 10px 0;
background: #fdf0f0;
display: inline-block;
padding: 0 10px;
height: 25px;
border-radius: 3px;
line-height: 25px;
font-size: 12px;
font-family: -apple-system, Helvetica, sans-serif;
color: #666;
}
/* 评论内容区,设置了左右内边距 */
.cmt-cont {
padding: 0 20rpx;
}
/* 评论标签,设置了相对定位,允许内部元素浮动或绝对定位 */
.cmt-tag {
position: relative;
padding: 14px 3px 0 0;
margin: 0;
/* 每个标签,设置了背景色、内边距、圆角、行高、字体大小和颜色 */
text {
margin: 0 10px 10px 0;
background: #fdf0f0;
display: inline-block;
padding: 0 10px;
height: 25px;
border-radius: 3px;
line-height: 25px;
font-size: 12px;
color: #666;
/* 当标签被选中时,改变背景色和字体颜色 */
&.selected {
color: #fff;
background: #e93b3d;
}
}
text.selected {
color: #fff;
background: #e93b3d;
}
/* 单条评论项,设置了相对定位,添加了底部边框 */
.cmt-item {
position: relative;
padding: 10px 0;
/* 使用伪元素添加底部边框 */
&::after {
content: "";
height: 0;
display: block;
border-bottom: 1px solid #ddd;
position: absolute;
left: 0;
bottom: 0;
right: -10px;
border-bottom-color: #e5e5e5;
}
}
.cmt-item {
position: relative;
padding: 10px 0;
&::after {
content: "";
height: 0;
display: block;
border-bottom: 1px solid #ddd;
position: absolute;
left: 0;
bottom: 0;
right: -10px;
border-bottom-color: #e5e5e5;
}
/* 评论列表为空时的提示 */
.cmt-items .empty {
}
.cmt-items {
.empty {
display: block;
font-size: 24rpx;
text-align: center;
color: #aaa;
margin-top: 5vh;
}
/* 用户信息,设置了行高、底边距、字体大小 */
.cmt-user {
}
.cmt-user {
line-height: 25px;
margin-bottom: 8px;
font-size: 12px;
.user-img {
width: 25px;
height: 25px;
border-radius: 50%;
vertical-align: middle;
}
.nickname {
margin-left: 10px;
display: inline-block;
color: #333;
max-width: 8.2em;
height: 25px;
line-height: 27px;
}
.date {
float: right;
color: #999;
margin-left: -60px;
}
}
.cmt-user-info {
display: flex;
align-items: center;
width: 400rpx;
}
.cmt-cnt {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
position: relative;
line-height: 1.5;
font-size: 14px;
margin: 5px 0;
word-break: break-all;
max-height: 126px;
}
.cmt-attr {
height: 85px;
width: 100%;
white-space: nowrap;
image {
display: inline-block;
width: 80px;
height: 80px;
margin-right: 5px;
margin-bottom: 5px;
border-radius: 2px;
background: #f3f3f3;
}
}
.cmt-more-v {
text-align: center;
background-color: #fff;
font-size: 12px;
text {
height: 25px;
line-height: 25px;
margin-bottom: 8px;
font-size: 12px;
/* 用户头像,圆形,设置了宽度、高度、圆角和垂直对齐方式 */
.user-img {
width: 25px;
height: 25px;
border-radius: 50%;
vertical-align: middle;
}
/* 用户昵称,设置了左边距、显示方式、颜色、最大宽度和高度 */
.nickname {
margin-left: 10px;
display: inline-block;
color: #333;
max-width: 8.2em;
height: 25px;
line-height: 27px;
}
/* 评论日期,设置为右侧浮动,颜色和左边距 */
.date {
float: right;
color: #999;
margin-left: -60px;
}
text-align: center;
color: #333;
padding: 0px 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 40px;
display: inline-block;
}
/* 用户信息容器使用flex布局对齐项目中心设置了宽度 */
.cmt-user-info {
display: flex;
align-items: center;
width: 400rpx;
}
.cmt-popup {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 998;
background-color: #fff;
padding-bottom: 98rpx;
.cmt-cont {
height: calc(100% - 80rpx);
overflow: auto;
}
/* 评论内容,设置了溢出隐藏、文本溢出处理、弹性盒子模型属性、相对定位、行高、字体大小、边距、单词断开和最大高度 */
.cmt-cnt {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
position: relative;
line-height: 1.5;
font-size: 14px;
margin: 5px 0;
word-break: break-all;
max-height: 126px;
}
/* 评论中的商品属性展示,设置了高度、宽度和白空间处理 */
.cmt-attr {
height: 85px;
width: 100%;
white-space: nowrap;
/* 商品图片,设置了宽高、右边距、底边距、圆角和背景色 */
image {
display: inline-block;
width: 80px;
height: 80px;
margin-right: 5px;
margin-bottom: 5px;
border-radius: 2px;
background: #f3f3f3;
}
-webkit-line-clamp: 20;
max-height: 500px;
}
/* 查看更多评论按钮,居中对齐,设置了背景色、字体大小、内边距、边框、圆角 */
.cmt-more-v {
.load-more {
font-size: 14px;
padding: 20px;
text-align: center;
background-color: #fff;
font-size: 12px;
/* 按钮文本,设置了高度、行高、字体大小、文本对齐、颜色、内边距、边框、圆角 */
margin-bottom: 10px;
text {
height: 25px;
line-height: 25px;
font-size: 12px;
text-align: center;
color: #333;
padding: 0px 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 40px;
display: inline-block;
}
}
/* 弹出层用于查看完整评论设置了固定定位、z-index、背景色、内边距 */
.cmt-popup {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 998;
background-color: #fff;
padding-bottom: 98rpx;
/* 弹出层内的评论内容,设置了高度、溢出滚动 */
.cmt-cont {
height: calc(100% - 80rpx);
overflow: auto;
}
/* 弹出层内的评论内容,增加了行数限制和最大高度 */
.cmt-cnt {
-webkit-line-clamp: 20;
max-height: 500px;
}
/* 加载更多按钮,设置了字体大小、内边距、文本对齐、边距、边框、圆角 */
.load-more {
font-size: 14px;
padding: 20px;
text-align: center;
margin-bottom: 10px;
/* 按钮文本,设置了边框、内边距、圆角和颜色 */
text {
border: 1px solid #ddd;
padding: 5px 10px;
border-radius: 10px;
color: #666;
}
border: 1px solid #ddd;
padding: 5px 10px;
border-radius: 10px;
color: #666;
}
}
}
. /* 线 */
.cmt-reply {
font-size: 14px;
border-top: 1px dashed #ddd;
padding: 5px 0;
/* 回复标题,红色字体强调 */
.reply-tit {
color: #eb2444;
}
}
/* 产品详情部分,设置了背景色、上外边距、相对定位和行高 */
.prod-detail {
background: #fff;
margin-top: 20rpx;
position: relative;
line-height: 48rpx;
/* 详情图片宽度设为750rpx并确保是块级元素显示 */
image {
width: 750rpx !important;
display: block;
}
}
/* 富文本组件内的图片确保宽度为100%以适应父容器 */
rich-text {
image {
width: 100% !important;
}
}
/* 深度选择器用于覆盖第三方组件或库的样式确保图片宽度为100%并作为块级元素显示 */
:deep(.img) {
width: 100% !important;
display: block;
}
/* 购物车底部固定栏设置了固定位置、底部对齐、宽度、弹性布局、高度、z-index和阴影效果 */
.cart-footer {
position: fixed;
bottom: 0;
@ -403,7 +342,6 @@ rich-text {
height: 98rpx;
z-index: 999;
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.05);
/* 按钮,设置了相对定位、弹性增长、内容居中、宽度、背景色、字体大小和列式布局 */
.btn {
position: relative;
display: flex;
@ -414,7 +352,6 @@ rich-text {
background-color: #fff;
font-size: 28rpx;
flex-flow: column;
/* 徽章,绝对定位的小圆圈,用于展示数字,如购物车商品数量 */
.badge {
position: absolute;
top: 20rpx;
@ -429,37 +366,30 @@ rich-text {
font-size: 18rpx;
color: #fff;
}
/* 稍大一点的徽章 */
.badge-1 {
width: 36rpx;
}
}
/* 图标按钮,不随其他按钮拉伸,设置了固定宽度、字体大小和颜色 */
.btn.icon {
flex-grow: 0;
flex-shrink: 0;
width: 125rpx;
font-size: 20rpx;
color: #666;
/* 图标按钮内的图片,设置了宽度和高度 */
image {
width: 50rpx;
height: 50rpx;
}
}
/* 购物车按钮,设置了背景色和文字颜色 */
.btn.cart {
background: #584e61;
color: #fff;
}
/* 立即购买按钮,设置了背景色和文字颜色 */
.btn.buy {
background: #eb2444;
color: #fff;
}
}
/* 关闭按钮,设置了颜色、圆角、行高、文本对齐、高度、宽度、字体大小、内边距、位置和伪元素内容 */
.close {
color: #aaa;
border-radius: 12px;
@ -472,20 +402,15 @@ rich-text {
top: 10px;
right: 10px;
position: absolute;
/* 伪元素,设置关闭符号 */
&::before {
content: "\2716";
}
}
/* 弹出层内容区,设置了最大高度、溢出滚动和左右内边距 */
.popup-cnt {
max-height: 429px;
overflow: auto;
padding: 0 10px;
}
/* SKU弹出层背景设置了固定位置、全屏尺寸、z-index和半透明黑色背景 */
.pup-sku {
position: fixed;
top: 0;
@ -495,8 +420,6 @@ rich-text {
z-index: 999;
background-color: rgba(0, 0, 0, 0.3);
}
/* SKU弹出层主要内容绝对定位在屏幕底部设置了宽度、最小和最大高度、白色背景 */
.pup-sku-main {
position: absolute;
bottom: 0;
@ -505,8 +428,6 @@ rich-text {
max-height: 475px;
background-color: #fff;
}
/* SKU弹出层头部设置了相对定位、行高、字体大小、颜色、高度、内边距和背景色 */
.pup-sku-header {
position: relative;
line-height: 46px;
@ -516,8 +437,6 @@ rich-text {
padding: 0 0 10px 110px;
background-color: #fff;
}
/* SKU弹出层中的产品图片设置了绝对定位、左上偏移、圆角、宽度、高度和垂直对齐 */
.pup-sku-img {
position: absolute;
left: 10px;
@ -528,8 +447,6 @@ rich-text {
border: 0 none;
vertical-align: top;
}
/* SKU弹出层价格设置了行内块显示、高度、行高、颜色和字体大小 */
.pup-sku-price {
display: inline-block;
height: 40px;
@ -537,13 +454,9 @@ rich-text {
color: #e4393c;
font-size: 10px;
}
/* SKU弹出层价格整数部分加大字体 */
.pup-sku-price-int {
font-size: 16px;
}
/* SKU弹出层属性描述允许单词断开设置了字体大小、颜色、行高、右边距、溢出隐藏、文本溢出处理和弹性盒子模型属性 */
.pup-sku-prop {
word-break: break-all;
font-size: 12px;
@ -555,22 +468,17 @@ rich-text {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
/* 属性描述中的文本,设置了颜色和右边距 */
text {
color: #999;
margin-right: 5px;
}
}
/* SKU弹出层主体内容设置了盒模型属性、最大高度、下内边距和溢出滚动 */
.pup-sku-body {
box-sizing: border-box;
max-height: 379px;
padding-bottom: 100px;
overflow: auto;
}
/* SKU区域设置了字体大小、颜色、内外边距和高度 */
.pup-sku-area {
.sku-kind {
font-size: 12px;
@ -579,14 +487,11 @@ rich-text {
height: 40px;
line-height: 40px;
}
/* SKU选择项容器设置了溢出隐藏和底边距 */
.sku-choose {
overflow: hidden;
margin-bottom: 3px;
}
}
/* SKU选择项设置了行内块显示、内边距、最小和最大宽度、高度、文本对齐、左边距、底边距、圆角、颜色、背景色和字体大小 */
.sku-choose-item {
display: inline-block;
padding: 0 10px;
@ -603,31 +508,23 @@ rich-text {
background-color: #f7f7f7;
font-size: 14px;
}
/* 当SKU选择项被选中时改变背景色和字体颜色 */
.sku-choose-item.active {
background-color: #eb2444;
color: #fff;
}
/* 当SKU选择项不可用时改变背景色和字体颜色 */
.sku-choose-item.gray {
background-color: #f9f9f9;
color: #ddd;
}
/* SKU弹出层数量选择部分设置了内边距和字体大小 */
.pup-sku-count {
padding: 0 10px 13px;
font-size: 12px;
/* 数量名称标签,设置了颜色、高度、行高和宽度 */
.count-name {
color: #999;
height: 31px;
line-height: 31px;
width: 100rpx;
}
/* 数量选择器包裹,设置了相对定位、宽度、浮动和垂直对齐 */
.num-wrap {
position: relative;
z-index: 0;
@ -636,13 +533,11 @@ rich-text {
vertical-align: middle;
display: flex;
}
/* 输入框包裹设置了相对定位、宽度、z-index和左右内边距 */
.text-wrap {
position: relative;
width: 45px;
z-index: 0;
margin: 0 1px;
/* 输入框,设置了高度、宽度、颜色、背景、字体大小、文本对齐、无边框和背景色 */
input {
height: 30px;
width: 100%;
@ -655,11 +550,8 @@ rich-text {
}
}
}
/* 数量选择器的减号和加号按钮,设置了相对定位、最大最小宽度、高度、行高、背景色、文本对齐、圆角 */
.num-wrap {
.minus,
.plus {
.minus {
position: relative;
max-width: 30px;
min-width: 30px;
@ -667,18 +559,20 @@ rich-text {
line-height: 30px;
background: #f7f7f7;
text-align: center;
}
/* 减号按钮,设置了左上和左下圆角 */
.minus {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
/* 加号按钮,设置了右上和右下圆角 */
.plus {
position: relative;
max-width: 30px;
min-width: 30px;
height: 30px;
line-height: 30px;
background: #f7f7f7;
text-align: center;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
/* 行元素,创建加号的横线,设置了圆角、绝对定位、顶部和左侧偏移、宽度、高度和背景色 */
.row {
border-radius: 20px;
position: absolute;
@ -690,7 +584,6 @@ rich-text {
height: 2px;
background-color: #ccc;
}
/* 列元素,创建加号的竖线,设置了圆角、绝对定位、顶部和左侧偏移、宽度、高度和背景色 */
.col {
border-radius: 20px;
position: absolute;
@ -703,8 +596,6 @@ rich-text {
background-color: #999;
}
}
/* SKU弹出层底部固定栏设置了固定位置、底部对齐、宽度、弹性布局、高度、z-index和阴影效果 */
.pup-sku-footer {
position: fixed;
bottom: 0;
@ -715,7 +606,6 @@ rich-text {
height: 98rpx;
z-index: 999;
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.05);
/* 按钮,设置了相对定位、弹性增长、内容居中、宽度、背景色、字体大小和列式布局 */
.btn {
position: relative;
display: flex;
@ -727,12 +617,10 @@ rich-text {
font-size: 28rpx;
flex-flow: column;
}
/* 购物车按钮,设置了背景色和文字颜色 */
.btn.cart {
background: #584e61;
color: #fff;
}
/* 立即购买按钮,设置了背景色和文字颜色 */
.btn.buy {
background: #eb2444;
color: #fff;

File diff suppressed because it is too large Load Diff

@ -1,40 +1,34 @@
.recent-news {
background: #fff; /* 设置最近新闻容器的背景颜色为白色 */
background: #fff;
.news-item {
padding: 20rpx 20rpx 0 20rpx; /* 内边距上20rpx左右各20rpx下0rpx */
position: relative; /* 使子元素可以相对于此元素进行绝对定位 */
padding: 20rpx 20rpx 0 20rpx;
position: relative;
&::after {
content: " "; /* 生成一个伪元素 */
width: 100%; /* 伪元素宽度为100% */
height: 2rpx; /* 伪元素高度为2rpx作为分隔线 */
background-color: #e1e1e1; /* 分隔线的颜色为浅灰色 */
left: 20rpx; /* 从左边开始20rpx与父元素内边距对齐 */
display: block; /* 伪元素作为块级元素显示 */
position: absolute; /* 伪元素绝对定位,相对于父元素 */
bottom: 0; /* 伪元素位于父元素底部 */
content: " ";
width: 100%;
height: 2rpx;
background-color: #e1e1e1;
left: 20rpx;
display: block;
position: absolute;
}
.news-item-title {
font-size: 28rpx; /* 新闻标题字体大小为28rpx */
text-align: left; /* 文字左对齐 */
font-size: 28rpx;
text-align: left;
}
.news-item-date {
font-size: 24rpx; /* 日期字体大小为24rpx */
color: #999; /* 日期颜色为浅灰色 */
text-align: right; /* 文字右对齐 */
margin-top: 10rpx; /* 上边距为10rpx */
margin-bottom: 20rpx; /* 下边距为20rpx */
font-size: 24rpx;
color: #999;
text-align: right;
margin-top: 10rpx;
margin-bottom: 20rpx;
}
}
.empty {
display: block; /* 空状态提示作为块级元素显示 */
padding-top: 200rpx; /* 上内边距为200rpx用于居中显示空状态提示 */
color: #999; /* 文字颜色为浅灰色 */
font-size: 26rpx; /* 字体大小为26rpx */
text-align: center; /* 文字水平居中 */
display: block;
padding-top: 200rpx;
color: #999;
font-size: 26rpx;
text-align: center;
}
}

@ -1,62 +1,60 @@
<template>
<view class="container">
<!-- 最近新闻容器 -->
<view class="recent-news">
<!-- 使用 v-for 指令循环渲染 news 数组中的每一个项目 -->
<block v-for="(item, index) in news" :key="index">
<!-- 新闻项点击时触发 toNewsDetail 方法 -->
<view class="news-item" :data-id="item.id" @tap="toNewsDetail">
<!-- 新闻标题 -->
<view class="news-item-title">{{ item.title }}</view>
<!-- 新闻发布时间 -->
<view class="news-item-date">{{ item.publishTime }}</view>
<block
v-for="(item, index) in news"
:key="index"
>
<view
class="news-item"
:data-id="item.id"
@tap="toNewsDetail"
>
<view class="news-item-title">
{{ item.title }}
</view>
<view class="news-item-date">
{{ item.publishTime }}
</view>
</view>
</block>
<!-- news 数组为空或不存在时显示的空状态提示 -->
<view v-if="!news || !news.length" class="empty"></view>
<view
v-if="!news || !news.length"
class="empty"
>
暂无数据
</view>
</view>
</view>
</template>
<script setup>
import { ref, onShow } from 'vue'; // Vue
import http from '@/utils/http'; // HTTP
//
const news = ref([]);
/**
* 生命周期函数--监听页面显示
* 当页面显示时调用此函数通常用于加载初始数据
*/
onShow(() => {
// HTTP GET
http.request({
url: '/shop/notice/noticeList', // API
method: 'GET' //
const news = ref([])
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
//
http.request({
url: '/shop/notice/noticeList',
method: 'GET'
})
.then(({ data }) => {
news.value = data.records
})
.then(({ data }) => {
// news
news.value = data.records;
})
.catch((error) => {
console.error('Failed to fetch news:', error); //
});
});
})
/**
* 跳转公告详情页
* @param e - 触发事件对象包含当前元素的数据集dataset
*/
const toNewsDetail = (e) => {
// 使 uni.navigateTo ID
uni.navigateTo({
url: `/pages/news-detail/news-detail?id=${e.currentTarget.dataset.id}`
});
};
/**
* 跳转公告详情页
* @param e
*/
const toNewsDetail = (e) => {
uni.navigateTo({
url: '/pages/news-detail/news-detail?id=' + e.currentTarget.dataset.id
})
}
</script>
<style scoped lang="scss">
/* 引入外部样式文件,这里的样式将仅应用于本组件 */
@use './recent-news.scss';
@use './recent-news.scss';
</style>

@ -1,146 +1,124 @@
/* 容器样式 */
.con {
background: #fff; /* 设置容器的背景颜色为白色 */
height: 100%; /* 容器的高度占满父元素的100% */
margin-top: 50px; /* 容器与页面顶部的距离为50像素 */
background: #fff;
height: 100%;
margin-top: 50px;
}
/* 图像样式 */
image {
display: block; /* 确保图像作为块级元素显示,避免与其他元素在同一行 */
width: 150rpx; /* 图像宽度为150响应式像素 */
height: 150rpx; /* 图像高度为150响应式像素 */
margin: auto; /* 使图像水平居中 */
border-radius: 50%; /* 将图像设置为圆形50%创建一个圆形) */
width: 150rpx; /* 重复定义图像宽度为150响应式像素 */
height: 150rpx; /* 重复定义图像高度为150响应式像素 */
margin-bottom: 8%; /* 图像底部外边距为父元素高度的8% */
display: block;
width: 150rpx;
height: 150rpx;
margin: auto;
border-radius: 50%;
width: 150rpx;
height: 150rpx;
margin-bottom: 8%;
}
/* 登录表单样式 */
.login-form {
width: 90%; /* 表单宽度为父元素宽度的90% */
margin: 0 auto; /* 表单左右外边距自动,实现水平居中 */
margin-bottom: 20%; /* 表单底部外边距为父元素高度的20%,确保与下方内容有足够的间距 */
width: 90%;
margin: 0 auto;
margin-bottom: 20%;
}
/* 授权按钮样式 */
.authorized-btn {
width: 90%; /* 按钮宽度为父元素宽度的90% */
margin: 0 auto; /* 按钮左右外边距自动,实现水平居中 */
text-align: center; /* 按钮内的文本居中对齐 */
background-color: #0ab906; /* 按钮背景颜色设置为绿色 */
border: 1rpx solid #0ab906; /* 按钮边框为1响应式像素宽颜色为绿色 */
color: #fff; /* 按钮文字颜色设置为白色 */
border-radius: 6rpx; /* 按钮边角圆滑度半径为6响应式像素 */
font-size: 26rpx; /* 按钮文字大小设置为26响应式像素 */
padding: 8rpx; /* 按钮内边距,即文字与按钮边缘之间的距离 */
margin-top: 80rpx; /* 按钮顶部外边距为80响应式像素 */
border: 1rpx solid #0ab906; /* 重复定义按钮边框为1响应式像素宽颜色为绿色 */
border-radius: 6rpx; /* 重复定义按钮边角圆滑度半径为6响应式像素 */
font-size: 26rpx; /* 重复定义按钮文字大小为26响应式像素 */
padding: 8rpx; /* 重复定义按钮内边距 */
margin-top: 80rpx; /* 重复定义按钮顶部外边距 */
width: 90%;
margin: 0 auto;
text-align: center;
background-color: #0ab906;
border: 1rpx solid #0ab906;
color: #fff;
border-radius: 6rpx;
font-size: 26rpx;
padding: 8rpx;
margin-top: 80rpx;
border: 1rpx solid #0ab906;
border-radius: 6rpx;
font-size: 26rpx;
padding: 8rpx;
margin-top: 80rpx;
}
/* 返回首页按钮样式 */
.to-idx-btn {
width: 90%; /* 按钮宽度为父元素宽度的90% */
margin: 0 auto; /* 按钮左右外边距自动,实现水平居中 */
text-align: center; /* 按钮内的文本居中对齐 */
background-color: #eeeeee; /* 按钮背景颜色设置为浅灰色 */
color: #333; /* 按钮文字颜色设置为深灰色 */
border-radius: 6rpx; /* 按钮边角圆滑度半径为6响应式像素 */
font-size: 26rpx; /* 按钮文字大小设置为26响应式像素 */
padding: 8rpx; /* 按钮内边距,即文字与按钮边缘之间的距离 */
margin-top: 30rpx; /* 按钮顶部外边距为30响应式像素 */
border-radius: 6rpx; /* 重复定义按钮边角圆滑度半径为6响应式像素 */
font-size: 26rpx; /* 重复定义按钮文字大小为26响应式像素 */
padding: 8rpx; /* 重复定义按钮内边距 */
margin-top: 30rpx; /* 重复定义按钮顶部外边距 */
width: 90%;
margin: 0 auto;
text-align: center;
background-color: #eeeeee;
color: #333;
border-radius: 6rpx;
font-size: 26rpx;
padding: 8rpx;
margin-top: 30rpx;
border-radius: 6rpx;
font-size: 26rpx;
padding: 8rpx;
margin-top: 30rpx;
}
/* 表单标题样式 */
.form-title {
width: 100%; /* 标题宽度为100%,即占满整个父元素的宽度 */
margin-bottom: 50rpx; /* 标题底部外边距为50响应式像素 */
font-size: 32rpx; /* 标题字体大小设置为32响应式像素 */
text-align: center; /* 标题文本居中对齐 */
color: #00a0e9; /* 标题文字颜色设置为蓝色 */
margin-bottom: 50rpx; /* 重复定义标题底部外边距为50响应式像素 */
font-size: 32rpx; /* 重复定义标题字体大小为32响应式像素 */
width: 100%;
margin-bottom: 50rpx;
font-size: 32rpx;
text-align: center;
color: #00a0e9;
margin-bottom: 50rpx;
font-size: 32rpx;
}
/* 表单项样式 */
.item {
display: block; /* 确保每个表单项作为一个单独的块级元素显示 */
margin-bottom: 30rpx; /* 每个表单项底部外边距为30响应式像素 */
margin-bottom: 30rpx; /* 重复定义每个表单项底部外边距为30响应式像素 */
display: block;
margin-bottom: 30rpx;
margin-bottom: 30rpx;
}
/* 账号输入框样式 */
.account {
display: flex; /* 使用弹性布局来排列子元素 */
background: #f8f8f8; /* 输入框背景颜色设置为浅灰色 */
padding: 15rpx; /* 输入框内边距为15响应式像素 */
box-sizing: border-box; /* 确保padding和border包含在元素的width和height之内 */
font-size: 26rpx; /* 输入框内的字体大小设置为26响应式像素 */
align-items: center; /* 子元素垂直居中对齐 */
display: flex;
background: #f8f8f8;
padding: 15rpx;
box-sizing: border-box;
font-size: 26rpx;
align-items: center;
input {
padding-left: 20rpx; /* 输入框内部左侧留出20响应式像素的空间 */
width: 75%; /* 输入框宽度为父元素宽度的75% */
padding-left: 20rpx; /* 重复定义输入框内部左侧留出20响应式像素的空间 */
padding-left: 20rpx;
width: 75%;
padding-left: 20rpx;
}
}
/* 移除按钮默认边框 */
button::after {
border: 0 !important; /* 移除按钮点击时出现的默认边框,使用!important确保覆盖其他样式 */
button {
&::after {
border: 0 !important;
}
}
/* 操作区域样式 */
.operate {
display: flex; /* 使用弹性布局来排列子元素 */
justify-content: space-between; /* 子元素之间均匀分布,两端对齐 */
align-items: center; /* 子元素垂直居中对齐 */
display: flex;
justify-content: space-between;
align-items: center;
}
/* 注册链接样式 */
.to-register {
font-size: 28rpx; /* 链接字体大小设置为28响应式像素 */
color: #00AAFF; /* 链接文字颜色设置为蓝色 */
font-size: 28rpx; /* 重复定义链接字体大小为28响应式像素 */
font-size: 28rpx;
color: #00AAFF;
font-size: 28rpx;
}
/* 错误提示样式 */
.error {
.error-text {
display: block; /* 错误信息作为块级元素显示 */
width: 100%; /* 错误信息宽度为100%,即占满整个父元素的宽度 */
font-size: 28rpx; /* 错误信息字体大小设置为28响应式像素 */
color: #e43130; /* 错误信息文字颜色设置为红色 */
text-align: left; /* 错误信息文本左对齐 */
margin-top: 10rpx; /* 错误信息顶部外边距为10响应式像素 */
font-size: 28rpx; /* 重复定义错误信息字体大小为28响应式像素 */
margin-top: 10rpx; /* 重复定义错误信息顶部外边距 */
display: block;
width: 100%;
font-size: 28rpx;
color: #e43130;
text-align: left;
margin-top: 10rpx;
font-size: 28rpx;
margin-top: 10rpx;
.warning-icon {
display: inline-block; /* 警告图标作为行内块级元素显示 */
color: #fff; /* 警告图标文字颜色设置为白色 */
width: 26rpx; /* 警告图标宽度为26响应式像素 */
height: 26rpx; /* 警告图标高度为26响应式像素 */
line-height: 26rpx; /* 警告图标行高设置为26响应式像素以确保文字垂直居中 */
background: #e43130; /* 警告图标背景颜色设置为红色 */
border-radius: 50%; /* 警告图标边角设置为圆形 */
text-align: center; /* 警告图标文字居中对齐 */
margin-right: 12rpx; /* 警告图标右侧留出12响应式像素的空间 */
font-size: 22rpx; /* 警告图标内的字体大小设置为22响应式像素 */
width: 26rpx; /* 重复定义警告图标宽度为26响应式像素 */
height: 26rpx; /* 重复定义警告图标高度为26响应式像素 */
line-height: 26rpx; /* 重复定义警告图标行高 */
margin-right: 12rpx; /* 重复定义警告图标右侧留出12响应式像素的空间 */
font-size: 22rpx; /* 重复定义警告图标内的字体大小 */
display: inline-block;
color: #fff;
width: 26rpx;
height: 26rpx;
line-height: 26rpx;
background: #e43130;
border-radius: 50%;
text-align: center;
margin-right: 12rpx;
font-size: 22rpx;
width: 26rpx;
height: 26rpx;
line-height: 26rpx;
margin-right: 12rpx;
font-size: 22rpx;
}
}
}

@ -1,68 +1,77 @@
<template>
<view class="register">
<!-- 注册页面的主容器 -->
<view class="con">
<!-- 显示应用logo -->
<image src="@/static/logo.png" />
<!-- 登录表单 -->
<!-- 登录 -->
<view class="login-form">
<!-- 账号输入项 -->
<view :class="['item', errorTips == 1 ? 'error' : '']">
<!-- 根据错误提示状态动态添加错误类 -->
<view :class="['item',errorTips==1? 'error':'']">
<view class="account">
<text class="input-item">账号</text> <!-- 输入项标签 -->
<input type="text"
data-type="account"
placeholder-class="inp-palcehoder"
placeholder="请输入账号名称"
@input="getInputVal" <!-- 监听输入事件获取用户输入的值 -->
/>
<text class="input-item">
账号
</text>
<input
type="text"
data-type="account"
placeholder-class="inp-palcehoder"
placeholder="请输入账号名称"
@input="getInputVal"
>
</view>
<!-- 错误提示信息仅当 errorTips 1 时显示 -->
<view v-if="errorTips == 1" class="error-text">
<text class="warning-icon">!</text> <!-- 警告图标 -->
<view
v-if="errorTips==1"
class="error-text"
>
<text class="warning-icon">
!
</text>
请输入账号
</view>
</view>
<!-- 密码输入项 -->
<view :class="['item', errorTips == 2 ? 'error' : '']">
<!-- 根据错误提示状态动态添加错误类 -->
<view :class="['item',errorTips==2? 'error':'']">
<view class="account">
<text class="input-item">密码</text> <!-- 输入项标签 -->
<input type="password"
data-type="password"
placeholder-class="inp-palcehoder"
placeholder="请输入密码"
@input="getInputVal" <!-- 监听输入事件获取用户输入的值 -->
/>
<text class="input-item">
密码
</text>
<input
type="password"
data-type="password"
placeholder-class="inp-palcehoder"
placeholder="请输入密码"
@input="getInputVal"
>
</view>
<!-- 错误提示信息仅当 errorTips 2 时显示 -->
<view v-if="errorTips == 2" class="error-text">
<text class="warning-icon">!</text> <!-- 警告图标 -->
<view
v-if="errorTips==2"
class="error-text"
>
<text class="warning-icon">
!
</text>
请输入密码
</view>
</view>
<!-- 操作区域 -->
<view class="operate">
<!-- 去登录链接 -->
<view class="to-register" @tap="toLogin">
<view
class="to-register"
@tap="toLogin"
>
已有账号
<text>去登录></text>
</view>
</view>
</view>
<!-- 按钮区域 -->
<view>
<!-- 注册按钮 -->
<button class="authorized-btn" @tap="toRegister">
<button
class="authorized-btn"
@tap="toRegister"
>
注册
</button>
<!-- 返回首页按钮 -->
<button class="to-idx-btn" @tap="toIndex">
<button
class="to-idx-btn"
@tap="toIndex"
>
回到首页
</button>
</view>
@ -71,114 +80,87 @@
</template>
<script setup>
import { encrypt } from '@/utils/crypto.js'; //
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
//
uni.setNavigationBarTitle({
title: '用户注册'
});
});
//
const principal = ref(''); //
const credentials = ref(''); //
//
const errorTips = ref(0); // 0: , 1: , 2:
/**
* 获取输入框的值
* @param e - 触发事件对象
*/
const getInputVal = (e) => {
const type = e.currentTarget.dataset.type; //
if (type === 'account') {
principal.value = e.detail.value; //
} else if (type === 'password') {
credentials.value = e.detail.value; //
}
};
/**
* 注册功能
*/
const toRegister = () => {
//
if (principal.value.length === 0) {
errorTips.value = 1; //
}
//
else if (credentials.value.length === 0) {
errorTips.value = 2; //
}
else {
errorTips.value = 0; //
//
uni.showLoading();
//
http.request({
url: '/user/register',
method: 'post',
data: {
userName: principal.value, //
passWord: encrypt(credentials.value) //
}
})
.then(() => {
//
uni.hideLoading();
//
uni.showToast({
title: '注册成功,请登录',
icon: 'none',
duration: 1500
});
// 1.8
setTimeout(() => {
uni.navigateTo({
url: '/pages/accountLogin/accountLogin'
});
}, 1800);
import { encrypt } from '@/utils/crypto.js'
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
//
uni.setNavigationBarTitle({
title: '用户注册'
})
})
const principal = ref('') //
const credentials = ref('') //
/**
* 输入框的值
*/
const getInputVal = (e) => {
const type = e.currentTarget.dataset.type
if (type == 'account') {
principal.value = e.detail.value
} else if (type == 'password') {
credentials.value = e.detail.value
}
}
const errorTips = ref(0) // : 1 2
/**
* 注册
*/
const toRegister = () => {
if (principal.value.length == 0) {
errorTips.value = 1
} else if (credentials.value.length == 0) {
errorTips.value = 2
} else {
errorTips.value = 0
uni.showLoading()
http.request({
url: '/user/register',
method: 'post',
data: {
userName: principal.value,
passWord: encrypt(credentials.value)
}
})
.then(() => {
uni.hideLoading()
uni.showToast({
title: '注册成功,请登录',
icon: 'none',
duration: 1500
})
.catch((error) => {
console.error('注册失败:', error); //
uni.hideLoading(); //
uni.showToast({
title: '注册失败,请稍后再试',
icon: 'none',
duration: 1500
});
});
}
};
/**
* 跳转到登录页面
*/
const toLogin = () => {
uni.navigateTo({
url: '/pages/accountLogin/accountLogin'
});
};
/**
* 回到首页
*/
const toIndex = () => {
uni.switchTab({
url: '/pages/index/index'
});
};
setTimeout(() => {
uni.navigateTo({
url: '/pages/accountLogin/accountLogin'
})
}, 1800)
})
}
}
/**
* 去登陆
*/
const toLogin = () => {
uni.navigateTo({
url: '/pages/accountLogin/accountLogin'
})
}
/**
* 回到首页
*/
const toIndex = () => {
uni.switchTab({
url: '/pages/index/index'
})
}
</script>
<style lang="scss" scoped>
@import "./register.scss"; /* 引入外部样式文件 */
@import "./register.scss";
</style>

@ -1,139 +1,119 @@
/* 搜索栏样式 */
.search-bar {
width: 100%; /* 搜索栏宽度占满整个父元素 */
position: fixed; /* 固定定位,使搜索栏始终位于页面顶部 */
top: 0; /* 距离页面顶部0像素 */
left: 0; /* 距离页面左侧0像素 */
color: #777; /* 搜索栏内文本颜色为浅灰色 */
background: #fff; /* 搜索栏背景颜色为白色 */
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.07); /* 添加轻微的阴影效果,增强立体感 */
z-index: 3; /* 设置堆叠顺序,确保搜索栏在其他内容之上 */
width: 100%;
position: fixed;
top: 0;
left: 0;
color: #777;
background: #fff;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.07);
z-index: 3;
.search-box {
position: relative; /* 相对定位,用于内部元素的绝对定位 */
height: 60rpx; /* 搜索框高度为60响应式像素 */
background: #f7f7f7; /* 搜索框背景颜色为浅灰色 */
z-index: 999; /* 设置较高的堆叠顺序,确保搜索框在其他元素之上 */
width: 80%; /* 搜索框宽度为父元素宽度的80% */
margin-left: 70rpx; /* 搜索框左侧外边距为70响应式像素 */
border-radius: 50rpx; /* 圆角边框半径为50响应式像素 */
margin: 20rpx 0 20rpx 20rpx; /* 上下外边距为20响应式像素右侧外边距为0左侧外边距为20响应式像素 */
position: relative;
height: 60rpx;
background: #f7f7f7;
z-index: 999;
width: 80%;
margin-left: 70rpx;
border-radius: 50rpx;
margin: 20rpx 0 20rpx 20rpx;
.search-img {
width: 32rpx; /* 搜索图标宽度为32响应式像素 */
height: 32rpx; /* 搜索图标高度为32响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
left: 20rpx; /* 搜索图标距离左侧20响应式像素 */
top: 14rpx; /* 搜索图标距离顶部14响应式像素 */
display: block; /* 确保图标作为块级元素显示 */
width: 32rpx;
height: 32rpx;
position: absolute;
left: 20rpx;
top: 14rpx;
display: block;
}
}
.search-hint {
font-size: 28rpx; /* 提示文本字体大小为28响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 30rpx; /* 提示文本距离右侧30响应式像素 */
top: 31rpx; /* 提示文本距离顶部31响应式像素 */
color: #eb2444; /* 提示文本颜色为红色 */
font-size: 28rpx;
position: absolute;
right: 30rpx;
top: 31rpx;
color: #eb2444;
}
}
/* 搜索输入框样式 */
.sear-input {
height: 60rpx; /* 输入框高度为60响应式像素 */
border-radius: 50rpx; /* 圆角边框半径为50响应式像素 */
border: 0; /* 移除默认边框 */
margin: 0 30rpx 0 64rpx; /* 左右外边距分别为30和64响应式像素 */
line-height: 48rpx; /* 行高设置为48响应式像素确保文字垂直居中 */
vertical-align: top; /* 垂直对齐方式为顶部 */
background: #f7f7f7; /* 输入框背景颜色为浅灰色 */
font-size: 28rpx; /* 输入框内的字体大小为28响应式像素 */
height: 60rpx;
border-radius: 50rpx;
border: 0;
margin: 0 30rpx 0 64rpx;
line-height: 48rpx;
vertical-align: top;
background: #f7f7f7;
font-size: 28rpx;
}
/* 搜索结果显示区域样式 */
.search-display {
background: #fff; /* 背景颜色为白色 */
padding: 20rpx; /* 内边距为20响应式像素 */
margin-top: 100rpx; /* 顶部外边距为100响应式像素确保与搜索栏有足够的间距 */
background: #fff;
padding: 20rpx;
margin-top: 100rpx;
.title-text {
padding: 30rpx 0; /* 上下内边距为30响应式像素 */
font-size: 30rpx; /* 标题文本字体大小为30响应式像素 */
color: #666; /* 标题文本颜色为深灰色 */
padding: 30rpx 0;
font-size: 30rpx;
color: #666;
}
}
/* 热门搜索样式 */
.hot-search {
.hot-search-tags {
overflow: hidden; /* 隐藏超出容器的内容 */
font-size: 26rpx; /* 标签字体大小为26响应式像素 */
text-align: center; /* 标签文本居中对齐 */
padding-bottom: 30rpx; /* 底部内边距为30响应式像素 */
overflow: hidden;
font-size: 26rpx;
text-align: center;
padding-bottom: 30rpx;
.tags {
display: block; /* 标签作为块级元素显示 */
max-width: 100%; /* 标签最大宽度为100% */
overflow: hidden; /* 隐藏超出标签的内容 */
float: left; /* 标签左浮动,实现水平排列 */
border-radius: 50rpx; /* 圆角边框半径为50响应式像素 */
white-space: nowrap; /* 防止文本换行 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
background-color: #f2f2f2; /* 标签背景颜色为浅灰色 */
box-sizing: border-box; /* 确保padding和border包含在元素的width和height之内 */
margin-right: 20rpx; /* 右侧外边距为20响应式像素 */
margin-bottom: 20rpx; /* 底部外边距为20响应式像素 */
padding: 10rpx 30rpx; /* 内边距为10和30响应式像素 */
display: block;
max-width: 100%;
overflow: hidden;
float: left;
border-radius: 50rpx;
white-space: nowrap;
text-overflow: ellipsis;
background-color: #f2f2f2;
box-sizing: border-box;
margin-right: 20rpx;
margin-bottom: 20rpx;
padding: 10rpx 30rpx;
}
}
}
/* 历史搜索样式 */
.history-search {
.title-text.history-line {
position: relative; /* 相对定位,用于内部元素的绝对定位 */
border-top: 2rpx solid #e1e1e1; /* 顶部边框为2响应式像素宽颜色为浅灰色 */
position: relative;
border-top: 2rpx solid #e1e1e1;
}
.his-search-tags {
overflow: hidden; /* 隐藏超出容器的内容 */
font-size: 26rpx; /* 标签字体大小为26响应式像素 */
text-align: center; /* 标签文本居中对齐 */
display: inline-block; /* 作为行内块级元素显示 */
overflow: hidden;
font-size: 26rpx;
text-align: center;
display: inline-block;
.tags {
max-width: 300rpx; /* 标签最大宽度为300响应式像素 */
overflow: hidden; /* 隐藏超出标签的内容 */
float: left; /* 标签左浮动,实现水平排列 */
border-radius: 50rpx; /* 圆角边框半径为50响应式像素 */
white-space: nowrap; /* 防止文本换行 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
background-color: #f2f2f2; /* 标签背景颜色为浅灰色 */
box-sizing: border-box; /* 确保padding和border包含在元素的width和height之内 */
margin-right: 20rpx; /* 右侧外边距为20响应式像素 */
margin-bottom: 20rpx; /* 底部外边距为20响应式像素 */
padding: 10rpx 30rpx; /* 内边距为10和30响应式像素 */
max-width: 300rpx;
overflow: hidden;
float: left;
border-radius: 50rpx;
white-space: nowrap;
text-overflow: ellipsis;
background-color: #f2f2f2;
box-sizing: border-box;
margin-right: 20rpx;
margin-bottom: 20rpx;
padding: 10rpx 30rpx;
}
}
}
/* 清除历史记录按钮样式 */
.clear-history {
image {
width: 32rpx; /* 图标宽度为32响应式像素 */
height: 32rpx; /* 图标高度为32响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 10rpx; /* 图标距离右侧10响应式像素 */
top: 30rpx; /* 图标距离顶部30响应式像素 */
width: 32rpx;
height: 32rpx;
position: absolute;
right: 10rpx;
top: 30rpx;
}
}
/* 搜索结果为空时的提示样式 */
.search-tit-empty {
display: block; /* 作为块级元素显示 */
margin: 0 auto; /* 水平居中 */
text-align: center; /* 文本居中对齐 */
width: 100%; /* 宽度为100%,即占满整个父元素 */
font-size: 24rpx; /* 提示文本字体大小为24响应式像素 */
color: #aaa; /* 提示文本颜色为浅灰色 */
display: block;
margin: 0 auto;
text-align: center;
width: 100%;
font-size: 24rpx;
color: #aaa;
}

@ -2,86 +2,84 @@
<view class="container">
<!-- 搜索框 -->
<view class="search-bar">
<!-- 搜索输入框 -->
<view class="search-box">
<input placeholder="输入关键字搜索" <!-- -->
class="sear-input" <!-- 应用样式类 sear-input -->
confirm-type="search" <!-- 设置确认按钮类型为搜索 -->
:value="prodName" <!-- 绑定输入框的值到 prodName 变量 -->
@confirm="toSearchProdPage" <!-- 监听用户按下确认键时触发搜索 -->
@input="getSearchContent" <!-- 监听用户输入内容并更新 prodName -->
/>
<!-- 搜索图标 -->
<image src="@/static/images/icon/search.png" <!-- -->
class="search-img" <!-- 应用样式类 search-img -->
<input
placeholder="输入关键字搜索"
class="sear-input"
confirm-type="search"
:value="prodName"
@confirm="toSearchProdPage"
@input="getSearchContent"
>
<image
src="@/static/images/icon/search.png"
class="search-img"
/>
</view>
<!-- 取消按钮 -->
<text class="search-hint" <!-- search-hint -->
@tap="goBackIndex" <!-- 点击取消按钮返回首页 -->
<text
class="search-hint"
@tap="goBackIndex"
>
取消
</text>
</view>
<!-- 搜索结果显示区域 -->
<view class="search-display">
<!-- 热门搜索 -->
<view class="hot-search">
<view class="title-text">
<!-- 热门搜索标题 -->
热门搜索
</view>
<!-- 如果有热门搜索数据则显示标签 -->
<view v-if="hotSearchList && hotSearchList.length" <!-- hotSearchList -->
<view
v-if="hotSearchList && hotSearchList.length"
class="hot-search-tags"
>
<block
v-for="(item, index) in hotSearchList" <!-- 遍历热门搜索列表 -->
:key="index" <!-- 使用索引作为唯一标识 -->
v-for="(item, index) in hotSearchList"
:key="index"
>
<text
class="tags" <!-- 应用样式类 tags -->
:data-name="item.content" <!-- 绑定 data-name 属性用于传递搜索关键词 -->
@tap="onHistSearch" <!-- 点击标签时触发搜索 -->
class="tags"
:data-name="item.content"
@tap="onHistSearch"
>
{{ item.title }} <!-- 显示标签文本 -->
{{ item.title }}
</text>
</block>
</view>
<!-- 如果没有热门搜索数据显示暂无数据提示 -->
<view v-else
class="search-tit-empty">
<view
v-else
class="search-tit-empty"
>
暂无数据
</view>
</view>
<!-- 搜索历史 -->
<view v-if="recentSearch && recentSearch.length" <!-- recentSearch -->
<view
v-if="recentSearch && recentSearch.length"
class="history-search"
>
<view class="title-text history-line"> <!-- 搜索历史标题 -->
<view class="title-text history-line">
搜索历史
<!-- 清除历史记录按钮 -->
<view class="clear-history">
<image
src="@/static/images/icon/clear-his.png" <!-- 清除图标图片路径 -->
@tap="clearSearch" <!-- 点击清除图标时清空历史记录 -->
src="@/static/images/icon/clear-his.png"
@tap="clearSearch"
/>
</view>
</view>
<!-- 遍历最近搜索历史并显示 -->
<block
v-for="(item, index) in recentSearch" <!-- 遍历最近搜索历史列表 -->
:key="index" <!-- 使用索引作为唯一标识 -->
v-for="(item, index) in recentSearch"
:key="index"
>
<view class="his-search-tags">
<text
class="tags" <!-- 应用样式类 tags -->
:data-name="item" <!-- 绑定 data-name 属性用于传递搜索关键词 -->
@tap="onHistSearch" <!-- 点击标签时触发搜索 -->
class="tags"
:data-name="item"
@tap="onHistSearch"
>
{{ item }} <!-- 显示历史记录文本 -->
{{ item }}
</text>
</view>
</block>
@ -91,114 +89,95 @@
</template>
<script setup>
import { ref } from 'vue'; // Vue ref
//
const hotSearchList = ref([]);
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
//
http.request({
url: '/search/hotSearchByShopId', // API
method: 'GET', // GET
data: {
number: 10, // 10
shopId: 1, // ID
sort: 1 //
}
const hotSearchList = ref([])
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
http.request({
url: '/search/hotSearchByShopId',
method: 'GET',
data: {
number: 10,
shopId: 1,
sort: 1
}
})
.then(({ data }) => {
hotSearchList.value = data
})
.then(({ data }) => {
hotSearchList.value = data; // hotSearchList
});
//
getRecentSearch();
});
//
const prodName = ref('');
/**
* 生命周期函数--监听页面隐藏
*/
onHide(() => {
prodName.value = ''; //
});
//
const recentSearch = ref([]);
/**
* 获取历史搜索记录
*/
const getRecentSearch = () => {
//
recentSearch.value = uni.getStorageSync('recentSearch') || [];
};
/**
* 搜索提交
*/
const toSearchProdPage = () => {
if (prodName.value.trim()) { //
//
let recentSearchStorage = uni.getStorageSync('recentSearch') || []; //
recentSearchStorage = recentSearchStorage.filter(item => item !== prodName.value); //
recentSearchStorage.unshift(prodName.value); //
if (recentSearchStorage.length > 10) { // 10
recentSearchStorage.pop();
}
uni.setStorageSync('recentSearch', recentSearchStorage); //
//
uni.navigateTo({
url: `/pages/search-prod-show/search-prod-show?prodName=${encodeURIComponent(prodName.value)}`
});
//
getRecentSearch()
})
const prodName = ref('')
/**
* 生命周期函数--监听页面隐藏
*/
onHide(() => {
prodName.value = ''
})
const recentSearch = ref([])
/**
* 获取历史搜索
*/
const getRecentSearch = () => {
recentSearch.value = uni.getStorageSync('recentSearch')
}
/**
* 搜索提交
*/
const toSearchProdPage = () => {
if (prodName.value.trim()) {
//
let recentSearchStorage = uni.getStorageSync('recentSearch') || []
recentSearchStorage = recentSearchStorage.filter(item => item !== prodName.value)
recentSearchStorage.unshift(prodName.value)
if (recentSearchStorage.length > 10) {
recentSearchStorage.pop()
}
};
/**
* 清空搜索历史
*/
const clearSearch = () => {
//
uni.removeStorageSync('recentSearch');
//
getRecentSearch();
};
/**
* 返回首页
*/
const goBackIndex = () => {
//
uni.navigateBack({
delta: 1 //
});
};
/**
* 输入商品名获取数据 || 绑定输入值
*/
const getSearchContent = (e) => {
// prodName
prodName.value = e.detail.value;
};
/**
* 点击搜素历史
*/
const onHistSearch = (e) => {
//
prodName.value = e.currentTarget.dataset.name;
//
toSearchProdPage();
};
uni.setStorageSync('recentSearch', recentSearchStorage) //
uni.navigateTo({
url: '/pages/search-prod-show/search-prod-show?prodName=' + prodName.value
})
}
}
/**
* 清空搜索历史
*/
const clearSearch = () => {
uni.removeStorageSync('recentSearch')
getRecentSearch()
}
/**
* 返回首页
*/
const goBackIndex = () => {
uni.navigateBack({
url: '/pages/search-page/search-page'
})
}
/**
* 输入商品名获取数据 || 绑定输入值
*/
const getSearchContent = (e) => {
prodName.value = e.detail.value
}
/**
* 点击搜素历史
*/
const onHistSearch = (e) => {
prodName.value = e.currentTarget.dataset.name
toSearchProdPage()
}
</script>
<style scoped lang="scss">
@use './search-page.scss'; /* 引入外部样式文件 */
@use './search-page.scss';
</style>

@ -1,218 +1,184 @@
/* 容器样式 */
.container {
background: #f4f4f4; /* 设置背景颜色为浅灰色 */
background: #f4f4f4;
.empty {
text-align: center; /* 文本居中对齐 */
color: #999; /* 文本颜色为灰色 */
font-size: 26rpx; /* 字体大小为26响应式像素 */
text-align: center;
color: #999;
font-size: 26rpx;
}
.empty.empty-top {
margin-top: 300rpx; /* 顶部外边距为300响应式像素用于在页面顶部显示空状态时有足够的间距 */
margin-top: 300rpx;
}
}
/* 固定定位的盒子样式 */
.fixed-box {
position: fixed; /* 固定定位,使盒子始终位于页面顶部 */
width: 100%; /* 宽度占满整个父元素 */
top: 0; /* 距离页面顶部0像素 */
z-index: 999; /* 设置较高的堆叠顺序,确保盒子在其他内容之上 */
background: #fff; /* 背景颜色为白色 */
position: fixed;
width: 100%;
top: 0;
z-index: 999;
background: #fff;
.tabs {
width: 100%; /* 宽度占满整个父元素 */
height: 80rpx; /* 高度为80响应式像素 */
line-height: 80rpx; /* 行高设置为80响应式像素确保文本垂直居中 */
padding: 10rpx 0; /* 上下内边距为10响应式像素 */
z-index: 999; /* 设置较高的堆叠顺序,确保标签栏在其他内容之上 */
background: #fff; /* 背景颜色为白色 */
width: 100%;
height: 80rpx;
line-height: 80rpx;
padding: 10rpx 0;
z-index: 999;
background: #fff;
&::after {
content: ''; /* 伪元素内容为空 */
background-color: #e1e1e1; /* 伪元素背景颜色为浅灰色 */
left: 0; /* 伪元素距离左侧0像素 */
height: 1px; /* 伪元素高度为1像素 */
transform-origin: 50% 100% 0; /* 伪元素变换原点为底部中心 */
bottom: 0; /* 伪元素距离底部0像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
display: block; /* 确保伪元素作为块级元素显示 */
width: 100%; /* 伪元素宽度为100%,即占满整个父元素 */
content: '';
background-color: #e1e1e1;
left: 0;
height: 1px;
transform-origin: 50% 100% 0;
bottom: 0;
position: absolute;
display: block;
width: 100%;
}
.tab-item {
display: inline-block; /* 标签项作为行内块级元素显示 */
width: 33.33%; /* 每个标签项占据父元素宽度的33.33%,实现三等分 */
text-align: center; /* 文本居中对齐 */
font-size: 28rpx; /* 字体大小为28响应式像素 */
display: inline-block;
width: 33.33%;
text-align: center;
font-size: 28rpx;
}
.tab-item.on {
color: #eb2444; /* 当前选中的标签项文本颜色为红色 */
color: #eb2444;
}
}
}
/* 搜索栏样式 */
.search-bar {
display: flex; /* 使用弹性布局 */
align-items: center; /* 垂直居中对齐 */
justify-content: space-between; /* 内容左右对齐 */
width: 100%; /* 宽度占满整个父元素 */
color: #777; /* 搜索栏内文本颜色为浅灰色 */
background: #fff; /* 搜索栏背景颜色为白色 */
z-index: 3; /* 设置堆叠顺序,确保搜索栏在其他内容之上 */
padding: 0 30rpx; /* 左右内边距为30响应式像素 */
box-sizing: border-box; /* 确保padding和border包含在元素的width和height之内 */
margin: 30rpx 0; /* 上下外边距为30响应式像素 */
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
color: #777;
background: #fff;
z-index: 3;
padding: 0 30rpx;
box-sizing: border-box;
margin: 30rpx 0;
.search-box {
position: relative; /* 相对定位,用于内部元素的绝对定位 */
height: 60rpx; /* 搜索框高度为60响应式像素 */
background: #f7f7f7; /* 搜索框背景颜色为浅灰色 */
z-index: 999; /* 设置较高的堆叠顺序,确保搜索框在其他内容之上 */
width: 80%; /* 搜索框宽度为父元素宽度的80% */
border-radius: 50rpx; /* 圆角边框半径为50响应式像素 */
margin-right: 30rpx; /* 右侧外边距为30响应式像素 */
flex: 1; /* 搜索框占据剩余空间 */
position: relative;
height: 60rpx;
background: #f7f7f7;
z-index: 999;
width: 80%;
border-radius: 50rpx;
margin-right: 30rpx;
flex: 1;
.search-img {
width: 32rpx; /* 搜索图标宽度为32响应式像素 */
height: 32rpx; /* 搜索图标高度为32响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
left: 20rpx; /* 搜索图标距离左侧20响应式像素 */
top: 14rpx; /* 搜索图标距离顶部14响应式像素 */
display: block; /* 确保图标作为块级元素显示 */
width: 32rpx;
height: 32rpx;
position: absolute;
left: 20rpx;
top: 14rpx;
display: block;
}
}
.search-hint {
font-size: 28rpx; /* 提示文本字体大小为28响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 30rpx; /* 提示文本距离右侧30响应式像素 */
top: 31rpx; /* 提示文本距离顶部31响应式像素 */
color: #eb2444; /* 提示文本颜色为红色 */
font-size: 28rpx;
position: absolute;
right: 30rpx;
top: 31rpx;
color: #eb2444;
}
.search-list-img {
width: 40rpx; /* 图标宽度为40响应式像素 */
height: 40rpx; /* 图标高度为40响应式像素 */
font-size: 0; /* 移除默认字体大小,防止影响图标显示 */
width: 40rpx;
height: 40rpx;
font-size: 0;
image {
width: 100%; /* 图片宽度为100%,即占满父元素 */
height: 100%; /* 图片高度为100%,即占满父元素 */
width: 100%;
height: 100%;
}
}
}
/* 搜索输入框样式 */
.sear-input {
height: 60rpx; /* 输入框高度为60响应式像素 */
border-radius: 50rpx; /* 圆角边框半径为50响应式像素 */
border: 0; /* 移除默认边框 */
margin: 0 30rpx 0 64rpx; /* 左右外边距分别为30和64响应式像素 */
line-height: 48rpx; /* 行高设置为48响应式像素确保文字垂直居中 */
vertical-align: top; /* 垂直对齐方式为顶部 */
background: #f7f7f7; /* 输入框背景颜色为浅灰色 */
font-size: 28rpx; /* 输入框内的字体大小为28响应式像素 */
height: 60rpx;
border-radius: 50rpx;
border: 0;
margin: 0 30rpx 0 64rpx;
line-height: 48rpx;
vertical-align: top;
background: #f7f7f7;
font-size: 28rpx;
}
/* 商品展示区域样式 */
.prod-show {
background: #fff; /* 背景颜色为白色 */
background: #fff;
.prod-items {
float: left; /* 商品项左浮动,实现水平排列 */
background: #fff; /* 商品项背景颜色为白色 */
padding-bottom: 20rpx; /* 底部内边距为20响应式像素 */
box-sizing: border-box; /* 确保padding和border包含在元素的width和height之内 */
float: left;
background: #fff;
padding-bottom: 20rpx;
box-sizing: border-box;
}
}
.prod-items {
margin: 0 20rpx; /* 左右外边距为20响应式像素 */
margin: 0 20rpx;
}
/* 热销商品容器样式 */
.hotsale-item-cont {
display: flex; /* 使用弹性布局 */
flex-wrap: wrap; /* 允许换行 */
justify-content: space-between; /* 内容左右对齐 */
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
/* 商品列表样式 */
.prod-list {
padding-top: 150rpx; /* 顶部内边距为150响应式像素确保与固定头部有足够的间距 */
min-height: calc(100vh - 150rpx); /* 最小高度为视口高度减去150响应式像素 */
padding-top: 150rpx;
min-height: calc(100vh - 150rpx);
.cont-item {
padding: 0 20rpx 20rpx 20rpx; /* 内边距为上下20响应式像素左右20响应式像素 */
padding: 0 20rpx 20rpx 20rpx;
.show-item {
.more-prod-pic {
text-align: center; /* 图片文本居中对齐 */
width: 170rpx; /* 图片容器宽度为170响应式像素 */
height: 170rpx; /* 图片容器高度为170响应式像素 */
font-size: 0; /* 移除默认字体大小,防止影响图片显示 */
text-align: center;
width: 170rpx;
height: 170rpx;
font-size: 0;
.more-pic {
width: 100%; /* 图片宽度为100%,即占满父元素 */
height: 100%; /* 图片高度为100%,即占满父元素 */
vertical-align: middle; /* 图片垂直居中对齐 */
width: 100%;
height: 100%;
vertical-align: middle;
}
}
position: relative; /* 相对定位,用于内部元素的绝对定位 */
display: flex; /* 使用弹性布局 */
justify-content: flex-start; /* 内容左对齐 */
padding: 20rpx; /* 内边距为20响应式像素 */
border-radius: 20rpx; /* 圆角边框半径为20响应式像素 */
background: #fff; /* 背景颜色为白色 */
margin-bottom: 20rpx; /* 底部外边距为20响应式像素 */
box-shadow: 0 16rpx 32rpx 0 rgba(7, 17, 27, 0.05); /* 添加轻微的阴影效果,增强立体感 */
position: relative;
display: flex;
justify-content: flex-start;
padding: 20rpx;
border-radius: 20rpx;
background: #fff;
margin-bottom: 20rpx;
box-shadow: 0 16rpx 32rpx 0 rgba(7, 17, 27, 0.05);
.prod-text-right {
margin-left: 20rpx; /* 左侧外边距为20响应式像素 */
width: 75%; /* 占据父元素宽度的75% */
margin-left: 20rpx;
width: 75%;
.cate-prod-info {
font-size: 22rpx; /* 字体大小为22响应式像素 */
color: #999; /* 文本颜色为灰色 */
margin: 10rpx 0 20rpx 0; /* 上下外边距分别为10和20响应式像素 */
font-size: 22rpx;
color: #999;
margin: 10rpx 0 20rpx 0;
}
.go-to-buy {
font-size: 26rpx; /* 字体大小为26响应式像素 */
background: #eb2444; /* 背景颜色为红色 */
color: #fff; /* 文本颜色为白色 */
border-radius: 50rpx; /* 圆角边框半径为50响应式像素 */
width: 150rpx; /* 宽度为150响应式像素 */
text-align: center; /* 文本居中对齐 */
padding: 8rpx 3rpx; /* 内边距为8和3响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 20rpx; /* 按钮距离右侧20响应式像素 */
bottom: 20rpx; /* 按钮距离底部20响应式像素 */
font-size: 26rpx;
background: #eb2444;
color: #fff;
border-radius: 50rpx;
width: 150rpx;
text-align: center;
padding: 8rpx 3rpx;
position: absolute;
right: 20rpx;
bottom: 20rpx;
}
.prod-text.more {
margin: 0; /* 移除默认外边距 */
height: 78rpx; /* 高度为78响应式像素 */
font-size: 28rpx; /* 字体大小为28响应式像素 */
display: -webkit-box; /* 使用Webkit的多行文本溢出处理 */
word-break: break-all; /* 允许单词内断行 */
overflow: hidden; /* 隐藏超出容器的内容 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
display: -webkit-box; /* 重复声明以确保兼容性 */
-webkit-line-clamp: 2; /* 限制显示两行文本 */
-webkit-box-orient: vertical; /* 文本方向为垂直 */
color: #000; /* 文本颜色为黑色 */
margin: 0;
height: 78rpx;
font-size: 28rpx;
display: -webkit-box;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: #000;
}
.prod-price.more {
font-size: 28rpx; /* 字体大小为28响应式像素 */
color: #eb2444; /* 文本颜色为红色 */
font-family: arial; /* 字体系列为Arial */
font-size: 28rpx;
color: #eb2444;
font-family: arial;
}
}
}

@ -2,52 +2,54 @@
<view class="container">
<!-- 搜索框 -->
<view class="fixed-box">
<!-- 固定定位的盒子通常用于页面顶部导航栏或标签栏 -->
<view class="search-bar">
<!-- 搜索栏容器 -->
<view class="search-box">
<!-- 搜索输入框容器 -->
<input placeholder="输入关键字搜索" <!-- -->
class="sear-input" <!-- 应用样式类 sear-input -->
:value="prodName" <!-- 绑定输入框的值到 prodName 变量 -->
confirm-type="search" <!-- 设置确认按钮类型为搜索 -->
@input="getSearchContent" <!-- 监听用户输入内容并更新 prodName -->
@confirm="toSearchConfirm" <!-- 监听用户按下确认键时触发搜索 -->
/>
<image src="@/static/images/icon/search.png" <!-- -->
class="search-img" <!-- 应用样式类 search-img -->
<input
placeholder="输入关键字搜索"
class="sear-input"
:value="prodName"
confirm-type="search"
@input="getSearchContent"
@confirm="toSearchConfirm"
>
<image
src="@/static/images/icon/search.png"
class="search-img"
/>
</view>
<view class="search-list-img" <!-- -->
@tap="changeShowType" <!-- 点击切换显示类型 -->
<view
class="search-list-img"
@tap="changeShowType"
>
<image
v-if="showType == 1" <!-- showType 1 时显示此图标 -->
src="@/static/images/icon/search-col.png" <!-- 图标图片路径 -->
v-if="showType==1"
src="@/static/images/icon/search-col.png"
/>
<image
v-if="showType == 2" <!-- showType 2 时显示此图标 -->
src="@/static/images/icon/search-col2.png" <!-- 图标图片路径 -->
v-if="showType==2"
src="@/static/images/icon/search-col2.png"
/>
</view>
</view>
<view class="tabs">
<!-- 标签栏容器 -->
<text :class="'tab-item complete ' + (sts == 0 ? 'on' : '')" <!-- sts 'on' -->
data-sts="0" <!-- 数据属性用于标识当前标签 -->
@tap="onStsTap" <!-- 点击标签时触发排序方式切换 -->
<text
:class="'tab-item complete ' + (sts==0?'on':'')"
data-sts="0"
@tap="onStsTap"
>
综合
</text>
<text :class="'tab-item ' + (sts == 1 ? 'on' : '')" <!-- sts 'on' -->
data-sts="1" <!-- 数据属性用于标识当前标签 -->
@tap="onStsTap" <!-- 点击标签时触发排序方式切换 -->
<text
:class="'tab-item ' + (sts==1?'on':'')"
data-sts="1"
@tap="onStsTap"
>
销量
</text>
<text :class="'tab-item ' + (sts == 2 ? 'on' : '')" <!-- sts 'on' -->
data-sts="2" <!-- 数据属性用于标识当前标签 -->
@tap="onStsTap" <!-- 点击标签时触发排序方式切换 -->
<text
:class="'tab-item ' + (sts==2?'on':'')"
data-sts="2"
@tap="onStsTap"
>
价格
</text>
@ -56,54 +58,61 @@
<!-- 商品列表 -->
<view class="prod-list">
<!-- 商品列表容器 -->
<!-- 横向列表 -->
<view v-if="showType == 1" <!-- showType 1 -->
<view
v-if="showType==1"
class="prod-show"
>
<view class="hotsale-item-cont"> <!-- 热销商品容器 -->
<view class="hotsale-item-cont">
<block
v-for="(item, index) in searchProdList" <!-- 遍历搜索结果列表 -->
:key="index" <!-- 使用索引作为唯一标识 -->
v-for="(item, index) in searchProdList"
:key="index"
>
<production
:item="item" <!-- 传递商品项数据 -->
sts="6" <!-- 传递状态码可能是用于样式或其他逻辑 -->
:item="item"
sts="6"
/>
</block>
</view>
</view>
<!-- 纵向列表 -->
<view v-if="showType == 2" <!-- showType 2 -->
<view
v-if="showType==2"
class="cont-item"
>
<block
v-for="(item, index) in searchProdList" <!-- 遍历搜索结果列表 -->
:key="index" <!-- 使用索引作为唯一标识 -->
v-for="(item, index) in searchProdList"
:key="index"
>
<view
class="show-item" <!-- 商品项容器 -->
:data-prodid="item.prodId" <!-- 传递商品ID -->
@tap="toProdPage" <!-- 点击商品项时跳转到商品详情页 -->
class="show-item"
:data-prodid="item.prodId"
@tap="toProdPage"
>
<view class="more-prod-pic"> <!-- 商品图片容器 -->
<view class="more-prod-pic">
<image
:src="item.pic" <!-- 动态绑定图片路径 -->
class="more-pic" <!-- 应用样式类 more-pic -->
:src="item.pic"
class="more-pic"
/>
</view>
<view class="prod-text-right"> <!-- 商品文本信息容器 -->
<view class="prod-text more"> <!-- 商品名称 -->
{{ item.prodName }} <!-- 显示商品名称 -->
<view class="prod-text-right">
<view class="prod-text more">
{{ item.prodName }}
</view>
<view class="cate-prod-info"> <!-- 商品评价信息 -->
{{ item.praiseNumber }}评价 {{ item.positiveRating }}%好评 <!-- 显示评价数量和好评率 -->
<view class="cate-prod-info">
{{ item.praiseNumber }}评价 {{ item.positiveRating }}%好评
</view>
<view class="prod-price more"> <!-- 商品价格 -->
<text class="symbol"></text> <!-- 人民币符号 -->
<text class="big-num">{{ wxs.parsePrice(item.price)[0] }}</text> <!-- 显示整数部分价格 -->
<text class="small-num">.{{ wxs.parsePrice(item.price)[1] }}</text> <!-- 显示小数部分价格 -->
<view class="prod-price more">
<text class="symbol">
</text>
<text class="big-num">
{{ wxs.parsePrice(item.price)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(item.price)[1] }}
</text>
</view>
</view>
</view>
@ -111,8 +120,9 @@
</view>
<!-- 空占位 -->
<view v-if="!searchProdList.length" <!-- -->
:class="['empty', showType == 1 ? 'empty-top' : '']" <!-- 动态绑定类名根据 showType 决定是否添加 'empty-top' -->
<view
v-if="!searchProdList.length"
:class="['empty',showType==1? 'empty-top':'']"
>
暂无结果
</view>
@ -121,128 +131,93 @@
</template>
<script setup>
import { ref } from 'vue'; // Vue ref
//
const wxs = number(); // `number`
//
const prodName = ref('');
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
// prodName prodName
prodName.value = options.prodName;
});
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
// toLoadData
toLoadData();
});
// 1: , 2:
const showType = ref(2);
//
const changeShowType = () => {
// showType
if (showType.value == 1) {
showType.value = 2;
} else {
showType.value = 1;
const wxs = number()
const prodName = ref('')
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
prodName.value = options.prodName
})
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
toLoadData()
})
const showType = ref(2)
const changeShowType = () => {
if (showType.value == 1) {
showType.value = 2
} else {
showType.value = 1
}
}
/**
* 输入商品获取数据
* @param e
*/
const getSearchContent = (e) => {
prodName.value = e.detail.value
}
const sts = ref(0)
const searchProdList = ref([])
/**
* 请求热门搜索商品接口
*/
const toLoadData = () => {
http.request({
url: '/search/searchProdPage',
method: 'GET',
data: {
current: 1,
prodName: prodName.value,
size: 10,
sort: sts.value
}
};
/**
* 输入商品获取数据
* @param e - 事件对象
*/
const getSearchContent = (e) => {
// prodName
prodName.value = e.detail.value;
};
// 0: , 1: , 2:
const sts = ref(0);
//
const searchProdList = ref([]);
/**
* 请求热门搜索商品接口
*/
const toLoadData = () => {
//
http.request({
url: '/search/searchProdPage', // API
method: 'GET', // GET
data: {
current: 1, //
prodName: prodName.value, //
size: 10, //
sort: sts.value //
}
})
.then(({ data }) => {
searchProdList.value = data.records
})
.then(({ data }) => {
// searchProdList
searchProdList.value = data.records;
});
};
/**
* 当前搜索页二次搜索商品
* @param e - 事件对象
*/
const toSearchConfirm = (e) => {
//
if (e.detail.value) {
//
let recentSearch = uni.getStorageSync('recentSearch') || [];
//
recentSearch = recentSearch.filter(item => item !== prodName.value);
//
recentSearch.unshift(prodName.value);
// 10
if (recentSearch.length > 10) {
recentSearch.pop();
}
//
uni.setStorageSync('recentSearch', recentSearch);
}
/**
* 当前搜索页二次搜索商品
*/
const toSearchConfirm = (e) => {
if (e.detail.value) {
let recentSearch = uni.getStorageSync('recentSearch') || []
recentSearch = recentSearch.filter(item => item !== prodName.value)
recentSearch.unshift(prodName.value)
if (recentSearch.length > 10) {
recentSearch.pop()
}
//
uni.redirectTo({
url: `/pages/search-prod-show/search-prod-show?prodName=${encodeURIComponent(e.detail.value)}`
});
};
/**
* 状态点击事件
* @param e - 事件对象
*/
const onStsTap = (e) => {
// sts
sts.value = e.currentTarget.dataset.sts;
//
toLoadData();
};
/**
* 跳转到商品详情页
* @param e - 事件对象
*/
const toProdPage = (e) => {
// ID
uni.navigateTo({
url: `/pages/prod/prod?prodid=${e.currentTarget.dataset.prodid}`
});
};
uni.setStorageSync('recentSearch', recentSearch)
}
uni.redirectTo({
url: '/pages/search-prod-show/search-prod-show?prodName=' + e.detail.value
})
}
/**
* 状态点击事件
*/
const onStsTap = (e) => {
sts.value = e.currentTarget.dataset.sts
toLoadData()
}
const toProdPage = (e) => {
uni.navigateTo({
url: '/pages/prod/prod?prodid=' + e.currentTarget.dataset.prodid
})
}
</script>
<style scoped lang="scss">
@use './search-prod-show.scss'; /* 引入外部样式文件 */
@use './search-prod-show.scss';
</style>

@ -1,138 +1,110 @@
/* 容器样式 */
.container {
background: #f4f4f4; /* 设置背景颜色为浅灰色 */
background: #f4f4f4;
}
/* 分类标题栏样式 */
.category-tit {
width: 100%; /* 宽度占满整个父元素 */
white-space: nowrap; /* 禁止文本换行,确保所有分类项在一行显示 */
position: fixed; /* 固定定位,使标题栏始终位于页面顶部 */
top: 0px; /* 距离页面顶部0像素 */
z-index: 999; /* 设置较高的堆叠顺序,确保标题栏在其他内容之上 */
background-color: #fff; /* 背景颜色为白色 */
border-bottom: 2rpx solid #f4f4f4; /* 底部边框,使用浅灰色实线 */
font-size: 30rpx; /* 标题文字大小为30响应式像素 */
width: 100%;
white-space: nowrap;
position: fixed;
top: 0px;
z-index: 999;
background-color: #fff;
border-bottom: 2rpx solid #f4f4f4;
font-size: 30rpx;
.category-item {
display: inline-block; /* 分类项作为行内块级元素显示,确保它们在同一行 */
padding: 20rpx 10rpx; /* 内边距为上下20响应式像素左右10响应式像素 */
margin: 0 20rpx; /* 左右外边距为20响应式像素 */
box-sizing: border-box; /* 确保padding和border包含在元素的width和height之内 */
font-size: 28rpx; /* 分类项文字大小为28响应式像素 */
display: inline-block;
padding: 20rpx 10rpx;
margin: 0 20rpx;
box-sizing: border-box;
font-size: 28rpx;
}
}
/* 当前选中的分类项样式 */
.on {
border-bottom: 4rpx solid #e43130; /* 底部边框,使用红色实线,表示当前选中 */
color: #e43130; /* 文本颜色为红色 */
}
/* 商品项容器样式 */
.prod-item {
height: calc(100vh - 100rpx); /* 高度为视口高度减去100响应式像素确保与固定头部有足够的间距 */
height: calc(100vh - 100rpx);
}
.on {
border-bottom: 4rpx solid #e43130;
color: #e43130;
}
/* 隐藏滚动条样式 */
::-webkit-scrollbar {
width: 0; /* 滚动条宽度为0 */
height: 0; /* 滚动条高度为0 */
color: transparent; /* 滚动条颜色为透明 */
width: 0;
height: 0;
color: transparent;
}
/* 空状态样式 */
.empty {
margin-top: 200rpx; /* 顶部外边距为200响应式像素确保空状态提示在页面中间 */
margin-top: 200rpx;
}
/* 商品项样式 */
.prod-items {
width: 345rpx; /* 商品项宽度为345响应式像素 */
display: inline-block; /* 作为行内块级元素显示,确保它们在同一行 */
background: #fff; /* 背景颜色为白色 */
padding-bottom: 20rpx; /* 底部内边距为20响应式像素 */
box-sizing: border-box; /* 确保padding和border包含在元素的width和height之内 */
box-shadow: 0rpx 6rpx 8rpx rgba(58, 134, 185, 0.2); /* 添加轻微的阴影效果,增强立体感 */
width: 345rpx;
display: inline-block;
background: #fff;
padding-bottom: 20rpx;
box-sizing: border-box;
box-shadow: 0rpx 6rpx 8rpx rgba(58,134,185,0.2);
&:nth-child(2n-1) {
margin: 20rpx 10rpx 10rpx 20rpx; /* 奇数商品项的外边距设置 */
margin: 20rpx 10rpx 10rpx 20rpx;
}
&:nth-child(2n) {
margin: 20rpx 20rpx 10rpx 10rpx; /* 偶数商品项的外边距设置 */
margin: 20rpx 20rpx 10rpx 10rpx;
}
.hot-imagecont {
.hotsaleimg {
width: 341rpx; /* 图片宽度为341响应式像素 */
height: 341rpx; /* 图片高度为341响应式像素 */
width: 341rpx;
height: 341rpx;
}
font-size: 0; /* 移除默认字体大小,防止影响图片显示 */
text-align: center; /* 图片居中对齐 */
font-size: 0;
text-align: center;
}
.hot-text {
.hotprod-text {
font-size: 28rpx; /* 商品名称文字大小为28响应式像素 */
white-space: nowrap; /* 禁止文本换行 */
overflow: hidden; /* 隐藏超出容器的内容 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
font-size: 28rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
margin-top: 20rpx; /* 顶部外边距为20响应式像素 */
padding: 0 10rpx; /* 左右内边距为10响应式像素 */
margin-top: 20rpx;
padding: 0 10rpx;
.prod-info {
min-height: 30rpx; /* 最小高度为30响应式像素 */
font-size: 22rpx; /* 商品信息文字大小为22响应式像素 */
color: #999; /* 文本颜色为灰色 */
white-space: nowrap; /* 禁止文本换行 */
overflow: hidden; /* 隐藏超出容器的内容 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
min-height: 30rpx;
font-size: 22rpx;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.prod-text-info {
position: relative; /* 相对定位,用于内部元素的绝对定位 */
height: 70rpx; /* 高度为70响应式像素 */
line-height: 70rpx; /* 行高设置为70响应式像素确保文本垂直居中 */
font-family: Arial; /* 字体系列为Arial */
position: relative;
height: 70rpx;
line-height: 70rpx;
font-family: Arial;
.hotprod-price {
display: inline; /* 价格文本作为行内元素显示 */
font-size: 26rpx; /* 价格文字大小为26响应式像素 */
color: #eb2444; /* 价格颜色为红色 */
display: inline;
font-size: 26rpx;
color: #eb2444;
}
.basket-img {
width: 50rpx; /* 购物车图标宽度为50响应式像素 */
height: 50rpx; /* 购物车图标高度为50响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 0; /* 购物车图标距离右侧0像素 */
bottom: 7rpx; /* 购物车图标距离底部7响应式像素 */
padding: 8rpx; /* 内边距为8响应式像素 */
width: 50rpx;
height: 50rpx;
position: absolute;
right: 0;
bottom: 7rpx;
padding: 8rpx;
}
}
}
}
/* 更多商品样式 */
.more-prod {
.prod-text-right {
.prod-info {
font-size: 22rpx; /* 商品信息文字大小为22响应式像素 */
color: #999; /* 文本颜色为灰色 */
white-space: nowrap; /* 禁止文本换行 */
overflow: hidden; /* 隐藏超出容器的内容 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
font-size: 22rpx;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
/* 空状态占位样式 */
.empty-wrap {
color: #aaa; /* 文本颜色为浅灰色 */
text-align: center; /* 文本居中对齐 */
padding-top: 400rpx; /* 顶部内边距为400响应式像素确保空状态提示在页面中间 */
color: #aaa;
text-align: center;
padding-top: 400rpx;
}

@ -1,65 +1,71 @@
<template>
<view class="Mall4j container">
<!-- 顶部子分类tab -->
<scroll-view scroll-x="true" <!-- -->
class="category-tit" <!-- 应用样式类 category-tit -->
:scroll-into-view="intoView" <!-- 滚动到指定元素确保选中的分类项可见 -->
:scroll-with-animation="true" <!-- 滚动时启用动画效果 -->
<scroll-view
scroll-x="true"
class="category-tit"
:scroll-into-view="intoView"
:scroll-with-animation="true"
>
<block
v-for="(item, index) in subCategoryList" <!-- 遍历子分类列表动态生成每个分类项 -->
:key="index" <!-- 使用索引作为唯一标识 -->
v-for="(item, index) in subCategoryList"
:key="index"
>
<view
:id="'sw' + item.categoryId" <!-- 动态设置每个分类项的ID -->
:class="'category-item ' + (item.categoryId == categoryId ? 'on' : '')" <!-- 动态绑定类名当前选中的分类项添加 'on' -->
:data-id="item.categoryId" <!-- 传递分类ID -->
@tap="onSubCategoryTap" <!-- 点击分类项时触发切换分类的方法 -->
:id="'sw' + item.categoryId"
:class="'category-item ' + (item.categoryId==categoryId? 'on':'')"
:data-id="item.categoryId"
@tap="onSubCategoryTap"
>
{{ item.categoryName }} <!-- 显示分类名称 -->
{{ item.categoryName }}
</view>
</block>
</scroll-view>
<!-- 商品列表 -->
<view class="prod-item">
<!-- 商品列表容器 -->
<block v-if="prodList.length">
<!-- 条件渲染只有当商品列表不为空时显示商品项 -->
<block v-for="(prod, key) in prodList" <!-- -->
:key="key" <!-- 使用索引作为唯一标识 -->
<block
v-for="(prod, key) in prodList"
:key="key"
>
<view
class="prod-items" <!-- 商品项容器 -->
:data-prodid="prod.prodId" <!-- 传递商品ID -->
@tap="toProdPage" <!-- 点击商品项时跳转到商品详情页 -->
class="prod-items"
:data-prodid="prod.prodId"
@tap="toProdPage"
>
<view class="hot-imagecont"> <!-- 商品图片容器 -->
<view class="hot-imagecont">
<image
:src="prod.pic" <!-- 动态绑定商品图片路径 -->
class="hotsaleimg" <!-- 应用样式类 hotsaleimg -->
:src="prod.pic"
class="hotsaleimg"
/>
</view>
<view class="hot-text"> <!-- 商品文本信息容器 -->
<view class="hotprod-text"> <!-- 商品名称 -->
{{ prod.prodName }} <!-- 显示商品名称 -->
<view class="hot-text">
<view class="hotprod-text">
{{ prod.prodName }}
</view>
<view class="prod-info"> <!-- 商品简介 -->
{{ prod.brief }} <!-- 显示商品简介 -->
<view class="prod-info">
{{ prod.brief }}
</view>
<view class="prod-text-info"> <!-- 商品价格信息 -->
<view class="price"> <!-- 价格容器 -->
<text class="symbol"></text> <!-- 人民币符号 -->
<text class="big-num">{{ wxs.parsePrice(prod.price)[0] }}</text> <!-- 显示整数部分价格 -->
<text class="small-num">.{{ wxs.parsePrice(prod.price)[1] }}</text> <!-- 显示小数部分价格 -->
<view class="prod-text-info">
<view class="price">
<text class="symbol">
</text>
<text class="big-num">
{{ wxs.parsePrice(prod.price)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(prod.price)[1] }}
</text>
</view>
</view>
</view>
</view>
</block>
</block>
<view v-else <!-- -->
class="empty-wrap" <!-- 应用样式类 empty-wrap -->
<view
v-else
class="empty-wrap"
>
暂无商品数据~
</view>
@ -68,147 +74,104 @@
</template>
<script setup>
import { ref, nextTick } from 'vue'; // Vue ref nextTick
//
const wxs = number(); // `number`
// ID
const parentId = ref('');
// ID
const categoryId = ref(0);
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
//
parentId.value = options.parentId;
categoryId.value = options.categoryId;
//
getSubCategory();
getProdList();
});
//
const current = ref(1);
//
const pages = ref(0);
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom(() => {
//
if (current.value < pages.value) {
current.value = current.value + 1;
getProdList();
const wxs = number()
const parentId = ref('')
const categoryId = ref(0)
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
parentId.value = options.parentId
categoryId.value = options.categoryId
getSubCategory()
getProdList()
})
const current = ref(1)
const pages = ref(0)
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom(() => {
if (current.value < pages.value) {
current.value = current.value + 1
getProdList()
}
})
const intoView = ref('')
const subCategoryList = ref([])
/**
* 获取顶栏子分类数据
*/
const getSubCategory = () => {
http.request({
url: '/category/categoryInfo',
method: 'GET',
data: {
parentId: parentId.value
}
});
//
const intoView = ref('');
//
const subCategoryList = ref([]);
/**
* 获取顶栏子分类数据
*/
const getSubCategory = () => {
//
http.request({
url: '/category/categoryInfo', // API
method: 'GET', // GET
data: {
parentId: parentId.value // ID
}
})
.then(({ data }) => {
// subCategoryList
subCategoryList.value = data;
// 使 nextTick DOM
nextTick(() => {
intoView.value = 'sw' + categoryId.value; //
});
});
};
//
const prodList = ref([]);
//
const isLoaded = ref(false);
/**
* 根据分类ID获取商品列表数据
*/
const getProdList = () => {
isLoaded.value = false; //
//
http.request({
url: '/prod/pageProd', // API
method: 'GET', // GET
data: {
categoryId: categoryId.value, // ID
current: current.value, //
size: 10, //
sort: 0, // 0
isAllProdType: true //
}
})
.then(({ data }) => {
subCategoryList.value = data
nextTick(() => {
intoView.value = 'sw' + categoryId.value
})
})
.then(({ data }) => {
isLoaded.value = true; //
//
prodList.value = data.current == 1 ? data.records : prodList.value.concat(data.records);
//
current.value = data.current;
pages.value = data.pages;
});
};
/**
* 切换顶部分类
* @param e - 事件对象
*/
const onSubCategoryTap = (e) => {
// ID
categoryId.value = e.currentTarget.dataset.id;
//
current.value = 1;
pages.value = 0;
//
intoView.value = 'sw' + e.currentTarget.dataset.id;
//
getProdList();
};
/**
* 跳转商品详情页
* @param e - 事件对象
*/
const toProdPage = (e) => {
const prodid = e.currentTarget.dataset.prodid; // ID
// ID
if (prodid) {
uni.navigateTo({
url: `/pages/prod/prod?prodid=${prodid}`
});
}
const prodList = ref([])
const isLoaded = ref(false) //
/**
* 根据分类id获取商品列表数据
*/
const getProdList = () => {
isLoaded.value = false
http.request({
url: '/prod/pageProd',
method: 'GET',
data: {
categoryId: categoryId.value,
current: current.value,
size: 10,
sort: 0,
isAllProdType: true
}
};
})
.then(({ data }) => {
isLoaded.value = true
prodList.value = data.current == 1 ? data.records : prodList.value.concat(data.records)
current.value = data.current
pages.value = data.pages
})
}
/**
* 切换顶部分类
*/
const onSubCategoryTap = (e) => {
categoryId.value = e.currentTarget.dataset.id
current.value = 1
pages.value = 0
intoView.value = 'sw' + e.currentTarget.dataset.id
getProdList()
}
/**
* 跳转商品下详情
*/
const toProdPage = (e) => {
const prodid = e.currentTarget.dataset.prodid
if (prodid) {
uni.navigateTo({
url: '/pages/prod/prod?prodid=' + prodid
})
}
}
</script>
<style lang="scss" scoped>
@import "./sub-category.scss"; /* 引入外部样式文件 */
@import "./sub-category.scss";
</style>

@ -1,489 +1,429 @@
/* 容器样式 */
.container {
background: #f4f4f4; /* 设置背景颜色为浅灰色 */
background: #f4f4f4;
}
/* 提交订单区域样式 */
.submit-order {
margin-bottom: 100rpx; /* 底部外边距为100响应式像素 */
padding-bottom: 160rpx; /* 底部内边距为160响应式像素 */
.delivery-addr { /* 配送地址样式 */
position: relative; /* 相对定位,用于内部元素的绝对定位 */
background: #fff; /* 背景颜色为白色 */
.addr-icon { /* 地址图标样式 */
width: 32rpx; /* 图标宽度为32响应式像素 */
height: 32rpx; /* 图标高度为32响应式像素 */
display: block; /* 作为块级元素显示 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
left: 30rpx; /* 距离左侧30响应式像素 */
top: 24rpx; /* 距离顶部24响应式像素 */
image { /* 图片样式 */
width: 100%; /* 宽度占满父元素 */
height: 100%; /* 高度占满父元素 */
margin-bottom: 100rpx;
padding-bottom: 160rpx;
.delivery-addr {
position: relative;
background: #fff;
.addr-icon {
width: 32rpx;
height: 32rpx;
display: block;
position: absolute;
left: 30rpx;
top: 24rpx;
image {
width: 100%;
height: 100%;
}
}
.user-info { /* 用户信息样式 */
padding-top: 20rpx; /* 顶部内边距为20响应式像素 */
line-height: 48rpx; /* 行高设置为48响应式像素 */
word-wrap: break-word; /* 允许长单词或URL地址换行到下一行 */
word-break: break-all; /* 强制文本在任意字符处换行 */
overflow: hidden; /* 隐藏超出容器的内容 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
display: -webkit-box; /* 使用Webkit盒模型 */
-webkit-line-clamp: 1; /* 限制文本显示为1行 */
-webkit-box-orient: vertical; /* 文本垂直排列 */
.item { /* 用户信息项样式 */
font-size: 30rpx; /* 文字大小为30响应式像素 */
margin-right: 30rpx; /* 右侧外边距为30响应式像素 */
vertical-align: top; /* 垂直对齐方式为顶部 */
display: inline-block; /* 作为行内块级元素显示 */
.user-info {
padding-top: 20rpx;
line-height: 48rpx;
word-wrap: break-word;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
.item {
font-size: 30rpx;
margin-right: 30rpx;
vertical-align: top;
display: inline-block;
}
}
.addr { /* 地址样式 */
font-size: 26rpx; /* 文字大小为26响应式像素 */
line-height: 36rpx; /* 行高设置为36响应式像素 */
color: #999; /* 文本颜色为灰色 */
width: 90%; /* 宽度为父元素的90% */
padding-bottom: 20rpx; /* 底部内边距为20响应式像素 */
margin-top: 15rpx; /* 顶部外边距为15响应式像素 */
word-wrap: break-word; /* 允许长单词或URL地址换行到下一行 */
.addr {
font-size: 26rpx;
line-height: 36rpx;
color: #999;
width: 90%;
padding-bottom: 20rpx;
margin-top: 15rpx;
word-wrap: break-word;
}
.arrow { /* 箭头样式 */
width: 15rpx; /* 宽度为15响应式像素 */
height: 15rpx; /* 高度为15响应式像素 */
border-top: 2rpx solid #777; /* 上边框为2响应式像素宽的灰色实线 */
border-right: 2rpx solid #777; /* 右边框为2响应式像素宽的灰色实线 */
transform: rotate(45deg); /* 旋转45度形成箭头形状 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 30rpx; /* 距离右侧30响应式像素 */
top: 60rpx; /* 距离顶部60响应式像素 */
.arrow {
width: 15rpx;
height: 15rpx;
border-top: 2rpx solid #777;
border-right: 2rpx solid #777;
transform: rotate(45deg);
position: absolute;
right: 30rpx;
top: 60rpx;
}
.arrow.empty { /* 空状态下的箭头样式 */
top: 39rpx; /* 距离顶部39响应式像素 */
.arrow.empty {
top: 39rpx;
}
}
}
.delivery-addr { /* 配送地址样式(重复定义,可能用于覆盖或扩展) */
.addr-bg { /* 地址背景样式 */
.add-addr { /* 添加地址按钮样式 */
.plus-sign { /* 加号样式 */
color: #eb2444; /* 文本颜色为红色 */
border: 2rpx solid #eb2444; /* 边框为2响应式像素宽的红色实线 */
padding: 0rpx 6rpx; /* 内边距为上下0响应式像素左右6响应式像素 */
margin-right: 10rpx; /* 右侧外边距为10响应式像素 */
.delivery-addr {
.addr-bg {
.add-addr {
.plus-sign {
color: #eb2444;
border: 2rpx solid #eb2444;
padding: 0rpx 6rpx;
margin-right: 10rpx;
}
font-size: 28rpx; /* 文字大小为28响应式像素 */
color: #666; /* 文本颜色为深灰色 */
display: flex; /* 使用Flex布局 */
align-items: center; /* 垂直居中对齐 */
padding: 30rpx 0; /* 上下内边距为30响应式像素 */
font-size: 28rpx;
color: #666;
display: flex;
align-items: center;
padding: 30rpx 0;
}
padding: 0 30rpx; /* 左右内边距为30响应式像素 */
padding: 0 30rpx;
}
.addr-bg.whole { /* 整个地址背景样式 */
padding: 0 39rpx 0 77rpx; /* 左右内边距分别为39响应式像素和77响应式像素 */
.addr-bg.whole {
padding: 0 39rpx 0 77rpx;
}
}
.addr-bg { /* 地址背景样式(重复定义,可能用于覆盖或扩展) */
.add-addr { /* 添加地址按钮样式 */
.plus-sign-img { /* 加号图片样式 */
width: 32rpx; /* 宽度为32响应式像素 */
height: 32rpx; /* 高度为32响应式像素 */
font-size: 0; /* 移除默认字体大小,防止影响图片显示 */
margin-right: 10rpx; /* 右侧外边距为10响应式像素 */
image { /* 图片样式 */
width: 100%; /* 宽度占满父元素 */
height: 100%; /* 高度占满父元素 */
.addr-bg {
.add-addr {
.plus-sign-img {
width: 32rpx;
height: 32rpx;
font-size: 0;
margin-right: 10rpx;
image {
width: 100%;
height: 100%;
}
}
}
}
.prod-item { /* 商品项样式 */
background-color: #fff; /* 背景颜色为白色 */
margin-top: 15rpx; /* 顶部外边距为15响应式像素 */
font-size: 28rpx; /* 文字大小为28响应式像素 */
.item-cont { /* 商品内容样式 */
.prod-pic { /* 商品图片样式 */
image { /* 图片样式 */
width: 180rpx; /* 宽度为180响应式像素 */
height: 180rpx; /* 高度为180响应式像素 */
width: 100%; /* 宽度占满父元素 */
height: 100%; /* 高度占满父元素 */
.prod-item {
background-color: #fff;
margin-top: 15rpx;
font-size: 28rpx;
.item-cont {
.prod-pic {
image {
width: 180rpx;
height: 180rpx;
width: 100%;
height: 100%;
}
font-size: 0; /* 移除默认字体大小,防止影响图片显示 */
display: block; /* 作为块级元素显示 */
width: 160rpx; /* 宽度为160响应式像素 */
height: 160rpx; /* 高度为160响应式像素 */
overflow: hidden; /* 隐藏超出容器的内容 */
background: #fff; /* 背景颜色为白色 */
margin-right: 16rpx; /* 右侧外边距为16响应式像素 */
font-size: 0;
display: block;
width: 160rpx;
height: 160rpx;
overflow: hidden;
background: #fff;
margin-right: 16rpx;
}
display: flex; /* 使用Flex布局 */
align-items: center; /* 垂直居中对齐 */
padding: 30rpx; /* 内边距为30响应式像素 */
border-bottom: 2rpx solid #f1f1f1; /* 底部边框为2响应式像素宽的浅灰色实线 */
.prod-info { /* 商品信息样式 */
margin-left: 10rpx; /* 左侧外边距为10响应式像素 */
font-size: 28rpx; /* 文字大小为28响应式像素 */
width: 100%; /* 宽度占满父元素 */
position: relative; /* 相对定位,用于内部元素的绝对定位 */
height: 160rpx; /* 高度为160响应式像素 */
flex: 1; /* 占用剩余空间 */
.prodname { /* 商品名称样式 */
font-size: 28rpx; /* 文字大小为28响应式像素 */
line-height: 40rpx; /* 行高设置为40响应式像素 */
max-height: 86rpx; /* 最大高度为86响应式像素 */
overflow: hidden; /* 隐藏超出容器的内容 */
display: -webkit-box; /* 使用Webkit盒模型 */
-webkit-line-clamp: 2; /* 限制文本显示为2行 */
-webkit-box-orient: vertical; /* 文本垂直排列 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
word-break: break-all; /* 强制文本在任意字符处换行 */
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 2rpx solid #f1f1f1;
.prod-info {
margin-left: 10rpx;
font-size: 28rpx;
width: 100%;
position: relative;
height: 160rpx;
-webkit-flex: 1;
-ms-flex: 1;
-webkit-box-flex: 1;
-moz-box-flex: 1;
flex: 1;
.prodname {
font-size: 28rpx;
line-height: 40rpx;
max-height: 86rpx;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
word-break: break-all;
}
.prod-info-cont { /* 商品信息内容样式 */
color: #999; /* 文本颜色为灰色 */
line-height: 40rpx; /* 行高设置为40响应式像素 */
margin-top: 10rpx; /* 顶部外边距为10响应式像素 */
font-size: 22rpx; /* 文字大小为22响应式像素 */
overflow: hidden; /* 隐藏超出容器的内容 */
display: -webkit-box; /* 使用Webkit盒模型 */
-webkit-line-clamp: 1; /* 限制文本显示为1行 */
-webkit-box-orient: vertical; /* 文本垂直排列 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
word-break: break-all; /* 强制文本在任意字符处换行 */
.prod-info-cont {
color: #999;
line-height: 40rpx;
margin-top: 10rpx;
font-size: 22rpx;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
word-break: break-all;
}
}
}
.order-num { /* 订单数量样式 */
padding: 20rpx 30rpx; /* 内边距为上下20响应式像素左右30响应式像素 */
display: flex; /* 使用Flex布局 */
justify-content: space-between; /* 水平两端对齐 */
font-size: 28rpx; /* 文字大小为28响应式像素 */
.clear-btn { /* 清空按钮样式 */
width: 32rpx; /* 宽度为32响应式像素 */
height: 32rpx; /* 高度为32响应式像素 */
font-size: 0; /* 移除默认字体大小,防止影响图片显示 */
vertical-align: top; /* 垂直对齐方式为顶部 */
margin-top: 6rpx; /* 顶部外边距为6响应式像素 */
margin-left: 42rpx; /* 左侧外边距为42响应式像素 */
position: relative; /* 相对定位,用于内部元素的绝对定位 */
&::after { /* 清空按钮后的竖线样式 */
content: " "; /* 生成伪元素 */
display: block; /* 作为块级元素显示 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
left: -10px; /* 距离左侧-10像素 */
top: 1px; /* 距离顶部1像素 */
width: 1px; /* 宽度为1像素 */
height: 12px; /* 高度为12像素 */
background: #ddd; /* 背景颜色为浅灰色 */
.order-num {
padding: 20rpx 30rpx;
display: flex;
justify-content: space-between;
font-size: 28rpx;
.clear-btn {
width: 32rpx;
height: 32rpx;
font-size: 0;
vertical-align: top;
margin-top: 6rpx;
margin-left: 42rpx;
position: relative;
&::after {
content: " ";
display: block;
position: absolute;
left: -10px;
top: 1px;
width: 1px;
height: 12px;
background: #ddd;
}
.clear-list-btn { /* 清空列表按钮样式 */
width: 100%; /* 宽度占满父元素 */
height: 100%; /* 高度占满父元素 */
vertical-align: middle; /* 垂直居中对齐 */
.clear-list-btn {
width: 100%;
height: 100%;
vertical-align: middle;
}
}
}
.total-num { /* 总数量样式 */
text-align: right; /* 文本右对齐 */
padding: 20rpx 30rpx; /* 内边距为上下20响应式像素左右30响应式像素 */
font-size: 28rpx; /* 文字大小为28响应式像素 */
.prodprice { /* 商品价格样式 */
display: inline-block; /* 作为行内块级元素显示 */
color: #333; /* 文本颜色为深灰色 */
.total-num {
text-align: right;
padding: 20rpx 30rpx;
font-size: 28rpx;
.prodprice {
display: inline-block;
color: #333;
}
.prodcount { /* 商品数量样式 */
margin-right: 20rpx; /* 右侧外边距为20响应式像素 */
.prodcount {
margin-right: 20rpx;
}
}
.price-nums { /* 价格和数量样式 */
.prodprice { /* 商品价格样式 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
bottom: 0; /* 距离底部0响应式像素 */
.price-nums {
.prodprice {
position: absolute;
bottom: 0;
}
.prodcount { /* 商品数量样式 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
bottom: 5rpx; /* 距离底部5响应式像素 */
right: 0; /* 距离右侧0响应式像素 */
color: #999; /* 文本颜色为灰色 */
font-family: verdana; /* 字体系列为Verdana */
.prodcount {
position: absolute;
bottom: 5rpx;
right: 0;
color: #999;
font-family: verdana;
}
}
}
.order-state { /* 订单状态样式 */
display: flex; /* 使用Flex布局 */
align-items: center; /* 垂直居中对齐 */
.order-state {
display: flex;
align-items: center;
}
.order-msg { /* 订单消息样式 */
background: #fff; /* 背景颜色为白色 */
margin-top: 15rpx; /* 顶部外边距为15响应式像素 */
padding: 0 30rpx; /* 左右内边距为30响应式像素 */
font-size: 28rpx; /* 文字大小为28响应式像素 */
.msg-item { /* 消息项样式 */
border-top: 2rpx solid #f1f1f1; /* 顶部边框为2响应式像素宽的浅灰色实线 */
&:first-child { /* 第一个消息项样式 */
border: 0; /* 移除边框 */
.order-msg {
background: #fff;
margin-top: 15rpx;
padding: 0 30rpx;
font-size: 28rpx;
.msg-item {
border-top: 2rpx solid #f1f1f1;
&:first-child {
border: 0;
}
.item { /* 消息项内容样式 */
position: relative; /* 相对定位,用于内部元素的绝对定位 */
display: flex; /* 使用Flex布局 */
padding: 16rpx 0; /* 上下内边距为16响应式像素 */
align-items: center; /* 垂直居中对齐 */
.item-tit { /* 消息标题样式 */
line-height: 48rpx; /* 行高设置为48响应式像素 */
.item {
position: relative;
display: flex;
padding: 16rpx 0;
align-items: center;
.item-tit {
line-height: 48rpx;
}
.item-txt { /* 消息文本样式 */
flex: 1; /* 占用剩余空间 */
font-family: arial; /* 字体系列为Arial */
max-height: 48rpx; /* 最大高度为48响应式像素 */
overflow: hidden; /* 隐藏超出容器的内容 */
line-height: 48rpx; /* 行高设置为48响应式像素 */
display: -webkit-box; /* 使用Webkit盒模型 */
-webkit-line-clamp: 1; /* 限制文本显示为1行 */
-webkit-box-orient: vertical; /* 文本垂直排列 */
text-overflow: ellipsis; /* 当文本溢出时显示省略号 */
word-break: break-all; /* 强制文本在任意字符处换行 */
.item-txt {
-webkit-box-flex: 1;
-moz-box-flex: 1;
flex: 1;
font-family: arial;
max-height: 48rpx;
overflow: hidden;
line-height: 48rpx;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
word-break: break-all;
}
.item-txt.price { /* 价格文本样式 */
padding: 0; /* 移除内边距 */
text-align: right; /* 文本右对齐 */
.item-txt.price {
padding: 0;
text-align: right;
}
input { /* 输入框样式 */
flex: 1; /* 占用剩余空间 */
input {
flex: 1;
}
.coupon-btn { /* 优惠券按钮样式 */
display: block; /* 作为块级元素显示 */
margin: 0 30rpx; /* 左右外边距为30响应式像素 */
line-height: 28rpx; /* 行高设置为28响应式像素 */
color: #999; /* 文本颜色为灰色 */
.coupon-btn {
display: block;
margin: 0 30rpx;
line-height: 28rpx;
color: #999;
}
.arrow { /* 箭头样式 */
width: 15rpx; /* 宽度为15响应式像素 */
height: 15rpx; /* 高度为15响应式像素 */
border-top: 2rpx solid #999; /* 上边框为2响应式像素宽的灰色实线 */
border-right: 2rpx solid #999; /* 右边框为2响应式像素宽的灰色实线 */
transform: rotate(45deg); /* 旋转45度形成箭头形状 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 0rpx; /* 距离右侧0响应式像素 */
.arrow {
width: 15rpx;
height: 15rpx;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
position: absolute;
right: 0rpx;
}
}
.item.payment { /* 支付项样式 */
border-top: 2rpx solid #f1f1f1; /* 顶部边框为2响应式像素宽的浅灰色实线 */
color: #eb2444; /* 文本颜色为红色 */
.item.payment {
border-top: 2rpx solid #f1f1f1;
color: #eb2444;
}
.item.coupon { /* 优惠券项样式 */
border-bottom: 2rpx solid #e1e1e1; /* 底部边框为2响应式像素宽的浅灰色实线 */
.item.coupon {
border-bottom: 2rpx solid #e1e1e1;
}
}
}
.submit-order-footer { /* 提交订单页脚样式 */
position: fixed; /* 固定定位,确保始终位于页面底部 */
bottom: 0; /* 距离页面底部0像素 */
width: 100%; /* 宽度占满整个屏幕 */
max-width: 750rpx; /* 最大宽度为750响应式像素 */
background: #fff; /* 背景颜色为白色 */
margin: auto; /* 水平居中 */
display: -webkit-flex; /* 使用Webkit Flex布局 */
display: -webkit-box; /* 使用Webkit盒模型 */
display: -moz-box; /* 使用Mozilla盒模型 */
display: -ms-flexbox; /* 使用IE Flex布局 */
display: flex; /* 使用标准Flex布局 */
font-size: 26rpx; /* 文字大小为26响应式像素 */
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.05); /* 添加轻微的阴影效果 */
.sub-order { /* 订单提交按钮样式 */
flex: 1; /* 占用剩余空间 */
margin: 0 30rpx; /* 左右外边距为30响应式像素 */
line-height: 100rpx; /* 行高设置为100响应式像素 */
display: block; /* 作为块级元素显示 */
text-align: left; /* 文本左对齐 */
font-size: 28rpx; /* 文字大小为28响应式像素 */
.item-txt { /* 订单金额样式 */
.price { /* 价格样式 */
display: inline; /* 作为行内元素显示 */
color: #eb2444; /* 文本颜色为红色 */
font-size: 28rpx; /* 文字大小为28响应式像素 */
.submit-order-footer {
position: fixed;
bottom: 0;
width: 100%;
max-width: 750rpx;
background: #fff;
margin: auto;
display: -webkit-flex;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: flex;
font-size: 26rpx;
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.05);
.sub-order {
flex: 1;
margin: 0 30rpx;
line-height: 100rpx;
display: block;
text-align: left;
font-size: 28rpx;
.item-txt {
.price {
display: inline;
color: #eb2444;
font-size: 28rpx;
}
}
}
.footer-box { /* 页脚按钮样式 */
padding: 0 10rpx; /* 左右内边距为10响应式像素 */
width: 200rpx; /* 宽度为200响应式像素 */
background: #eb2444; /* 背景颜色为红色 */
text-align: center; /* 文本居中对齐 */
line-height: 100rpx; /* 行高设置为100响应式像素 */
color: #fff; /* 文本颜色为白色 */
.footer-box {
padding: 0 10rpx;
width: 200rpx;
background: #eb2444;
text-align: center;
line-height: 100rpx;
color: #fff;
}
}
.clearfix { /* 清除浮动样式 */
&:after { /* 生成伪元素清除浮动 */
content: " "; /* 生成空内容 */
display: table; /* 作为表格元素显示 */
clear: both; /* 清除浮动 */
.clearfix {
&:after {
content: " ";
display: table;
clear: both;
}
}
.popup-hide { /* 弹出层隐藏样式 */
position: fixed; /* 固定定位,覆盖整个屏幕 */
top: 0; /* 距离页面顶部0像素 */
bottom: 0; /* 距离页面底部0像素 */
left: 0; /* 距离页面左侧0像素 */
right: 0; /* 距离页面右侧0像素 */
z-index: 999; /* 设置较高的堆叠顺序,确保弹出层在其他内容之上 */
background-color: rgba(0, 0, 0, 0.3); /* 背景颜色为半透明黑色 */
.popup-hide {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 999;
background-color: rgba(0, 0, 0, 0.3);
}
.popup-box { /* 弹出层盒子样式 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
bottom: 0; /* 距离页面底部0像素 */
width: 100%; /* 宽度占满整个屏幕 */
height: 80%; /* 高度为屏幕高度的80% */
overflow: hidden; /* 隐藏超出容器的内容 */
background-color: #fff; /* 背景颜色为白色 */
.popup-box {
position: absolute;
bottom: 0;
width: 100%;
height: 80%;
overflow: hidden;
background-color: #fff;
}
.popup-tit { /* 弹出层标题样式 */
position: relative; /* 相对定位,用于内部元素的绝对定位 */
height: 46px; /* 高度为46像素 */
line-height: 46px; /* 行高设置为46像素 */
padding-left: 10px; /* 左侧内边距为10像素 */
font-size: 16px; /* 文字大小为16像素 */
color: #333; /* 文本颜色为深灰色 */
font-weight: bold; /* 文本加粗 */
.popup-tit {
position: relative;
height: 46px;
line-height: 46px;
padding-left: 10px;
font-size: 16px;
color: #333;
font-weight: bold;
}
.close { /* 关闭按钮样式 */
color: #aaa; /* 文本颜色为浅灰色 */
border-radius: 12px; /* 圆角半径为12像素 */
line-height: 20px; /* 行高设置为20像素 */
text-align: center; /* 文本居中对齐 */
height: 20px; /* 高度为20像素 */
width: 20px; /* 宽度为20像素 */
font-size: 18px; /* 文字大小为18像素 */
padding: 1px; /* 内边距为1像素 */
top: 10px; /* 距离顶部10像素 */
right: 10px; /* 距离右侧10像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
&::before { /* 生成关闭符号样式 */
content: "\2716"; /* 使用Unicode字符表示关闭符号 */
.close {
color: #aaa;
border-radius: 12px;
line-height: 20px;
text-align: center;
height: 20px;
width: 20px;
font-size: 18px;
padding: 1px;
top: 10px;
right: 10px;
position: absolute;
&::before {
content: "\2716";
}
}
.coupon-tabs { /* 优惠券标签样式 */
display: flex; /* 使用Flex布局 */
font-size: 14px; /* 文字大小为14像素 */
justify-content: space-around; /* 水平均匀分布 */
border-bottom: 1px solid #f2f2f2; /* 底部边框为1像素宽的浅灰色实线 */
.coupon-tabs {
display: flex;
font-size: 14px;
justify-content: space-around;
border-bottom: 1px solid #f2f2f2;
}
.coupon-tab { /* 优惠券标签项样式 */
padding: 10px 0; /* 上下内边距为10像素 */
.coupon-tab {
padding: 10px 0;
}
.coupon-tab.on { /* 选中的优惠券标签项样式 */
border-bottom: 2px solid #eb2444; /* 底部边框为2像素宽的红色实线 */
font-weight: 600; /* 文本加粗 */
.coupon-tab.on {
border-bottom: 2px solid #eb2444;
font-weight: 600;
}
.popup-cnt { /* 弹出层内容样式 */
height: calc(100% - 88px); /* 高度为弹出层盒子高度减去88像素 */
overflow: auto; /* 允许滚动 */
padding: 0 10px; /* 左右内边距为10像素 */
background: #f4f4f4; /* 背景颜色为浅灰色 */
.popup-cnt {
height: calc(100% - 88px);
overflow: auto;
padding: 0 10px;
background: #f4f4f4;
}
.coupon-ok { /* 优惠券确认按钮样式 */
position: fixed; /* 固定定位,确保始终位于页面底部 */
bottom: 0; /* 距离页面底部0像素 */
width: 100%; /* 宽度占满整个屏幕 */
height: 60px; /* 高度为60像素 */
line-height: 50px; /* 行高设置为50像素 */
font-size: 14px; /* 文字大小为14像素 */
text-align: center; /* 文本居中对齐 */
box-shadow: 0px -1px 1px #ddd; /* 添加轻微的阴影效果 */
text { /* 按钮文本样式 */
border-radius: 20px; /* 圆角半径为20像素 */
display: inline-block; /* 作为行内块级元素显示 */
height: 20px; /* 高度为20像素 */
line-height: 20px; /* 行高设置为20像素 */
width: 450rpx; /* 宽度为450响应式像素 */
padding: 7px; /* 内边距为7像素 */
color: #fff; /* 文本颜色为白色 */
box-shadow: -1px 3px 3px #aaa; /* 添加轻微的阴影效果 */
.coupon-ok {
position: fixed;
bottom: 0;
width: 100%;
height: 60px;
line-height: 50px;
font-size: 14px;
text-align: center;
box-shadow: 0px -1px 1px #ddd;
text {
border-radius: 20px;
display: inline-block;
height: 20px;
line-height: 20px;
width: 450rpx;
padding: 7px;
color: #fff;
box-shadow: -1px 3px 3px #aaa;
}
}
.botm-empty { /* 底部空白样式 */
height: 60px; /* 高度为60像素 */
.botm-empty {
height: 60px;
}
checkbox { /* 复选框样式 */
.wx-checkbox-input { /* 复选框输入框样式 */
border-radius: 50%; /* 圆形边框 */
width: 35rpx; /* 宽度为35响应式像素 */
height: 35rpx; /* 高度为35响应式像素 */
checkbox {
.wx-checkbox-input {
border-radius: 50%;
width: 35rpx;
height: 35rpx;
}
.wx-checkbox-input.wx-checkbox-input-checked { /* 选中的复选框输入框样式 */
background: #eb2444; /* 背景颜色为红色 */
border-color: #eb2444; /* 边框颜色为红色 */
&::before { /* 选中符号样式 */
text-align: center; /* 文本居中对齐 */
font-size: 22rpx; /* 文字大小为22响应式像素 */
color: #fff; /* 文本颜色为白色 */
background: transparent; /* 背景颜色为透明 */
transform: translate(-50%, -50%) scale(1); /* 居中并缩放 */
-webkit-transform: translate(-50%, -50%) scale(1); /* Webkit浏览器兼容 */
.wx-checkbox-input.wx-checkbox-input-checked {
background: #eb2444;
border-color: #eb2444;
&::before {
text-align: center;
font-size: 22rpx;
color: #fff;
background: transparent;
transform: translate(-50%, -50%) scale(1);
-webkit-transform: translate(-50%, -50%) scale(1);
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save