1.4 #5

Merged
pvtfxms7o merged 45 commits from qjh into main 2 months ago

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

@ -1,4 +1,23 @@
# 启用严格的引擎检查
# 当设置为 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,20 +2,33 @@
<html>
<head>
<meta charset="UTF-8" />
<!-- 设置文档字符编码为 UTF-8确保页面正确显示中文等多语言字符 -->
<meta name="renderer" content="webkit">
<!-- 强制使用 WebKit 渲染引擎,适用于某些浏览器(如 QQ 浏览器)以确保更好的兼容性 -->
<title>mall4uni-pro</title>
<!-- 页面标题,显示在浏览器标签上 -->
<script>
let coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
// 检查浏览器是否支持 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` 用于确保页面内容完全覆盖整个屏幕,避免出现空白区域
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>
<script type="module" src="/src/main.js"></script>
<div id="app"><!--app-html--></div>
<!-- 主应用容器Vue 或其他前端框架的根元素将挂载在这里 -->
<script type="module" src="/src/main.js"></script>
<!-- 引入主 JavaScript 文件,使用 ES6 模块语法 (`type="module"`),确保代码模块化和更好的性能 -->
</body>
</html>

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

@ -1,108 +1,176 @@
/**app.wxss**/
/** app.wxss **/
/*
*
* 100%
* 使 border-box 使 padding border
* #333
*/
.container {
height: 100%;
box-sizing: border-box;
color: #333;
font-family: helvetica,'Heiti SC',PingFangSC-Light;
font-family: helvetica, 'Heiti SC', PingFangSC-Light;
}
.price{
/*
*
* Arial (#eb2444)
* 使
*/
.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
*
*/
/* reg */
* checkbox
*
*/
/*
* checkbox
* checkbox 100%
*/
uni-checkbox-group {
width: 100% !important;
}
uni-checkbox-group uni-label{
width: 33% !important;
display: inline-flex;
margin-bottom: 20rpx;
}
/*checkbox 选项框大小 */
uni-checkbox .uni-checkbox-input{
/*
* 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 {
width: 38rpx !important;
height: 38rpx !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);
border-radius: 50% !important;
}
/*
* 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
*
*/
/* 未选中的 背景样式 */
uni-radio .uni-radio-input{
* radio
*
*/
/*
* radio
* radio 36rpx
*/
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);
}
/* 底部按钮兼容 iPhone X以上 */
@media screen and (width: 375px) and (height: 812px){
/*
* 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) {
.container {
padding-bottom: 70px;
}
}
@media screen and (width: 414px) and (height: 736px){
/*
* iPhone 8 Plus
* iPhone 8 Plus
*/
@media screen and (width: 414px) and (height: 736px) {
.container {
padding-bottom: 70px;
}

@ -1,79 +1,94 @@
<template>
<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"
/>
<!-- 根据条件判断显示不同的图片元素 -->
<!-- 如果不存在错误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" />
</template>
<script setup>
const props = defineProps({
src: {
type: String,
default: ''
},
imgMode: {
type: String,
default: 'scaleToFill'
},
classList: {
type: Array,
default: () => {
return []
// 使 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
}
},
imgStyle: {
type: Object,
default: () => {
return {}
}
},
// , false, true
defaultImgType: {
type: Boolean,
default: false
}
})
})
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'
})
// 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 emit = defineEmits(['imgError', 'imgLoad', 'handleTap'])
const isError = ref(false)
const imgError = () => {
isError.value = true
emit('imgError')
}
// 使 defineEmits
const emit = defineEmits(['imgError', 'imgLoad', 'handleTap'])
// isError false
const isError = ref(false)
//
// isError true 'imgError'
const imgError = () => {
isError.value = true
emit('imgError')
}
const imgLoad = (e) => {
emit('imgLoad', e)
}
// 'imgLoad' e
const imgLoad = (e) => {
emit('imgLoad', e)
}
const handleTap = () => {
emit('handleTap')
}
// 'handleTap'
const handleTap = () => {
emit('handleTap')
}
</script>
<style scoped>
image {
width: 100%;
height: 100%;
}
/* 为 image 标签设置样式,使其宽度和高度都占满父元素的 100% */
image {
width: 100%;
height: 100%;
}
</style>

@ -1,53 +1,86 @@
/*
.prod-items
43%40rpx使 border-box使
*/
.prod-items {
width: 43%;
background: #fff;
margin-bottom: 40rpx;
box-sizing: border-box;
.hot-imagecont {
/*
.hot-imagecont
0
*/
.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 {
/*
.price #eb2444
*/
.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;
@ -61,6 +94,9 @@ prod {
color: #000;
}
}
/*
.deadline-price
*/
.deadline-price {
font-size: 22rpx;
margin-right: 5rpx;

@ -1,39 +1,70 @@
<template>
<view
class="prod-items"
:data-prodid="item.prodId"
@tap="toProdPage"
>
<!--
使用view标签创建一个类名为"prod-items"的组件元素通过":data-prodid"绑定了一个名为"item.prodId"的数据属性
并监听了"tap"事件当触发点击时会调用"toProdPage"方法
-->
<view class="prod-items"
:data-prodid="item.prodId"
@tap="toProdPage">
<!--
创建一个类名为"hot-imagecont"的view元素用于包裹商品图片相关内容起到容器的作用
-->
<view class="hot-imagecont">
<image
:src="item.pic"
class="hotsaleimg"
/>
<!--
使用image标签展示商品图片图片的src属性通过":src"绑定了名为"item.pic"的数据
并设置了类名"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>
<view
v-if="sts===6"
class="prod-info"
>
<!--
根据"sts"的值来决定是否显示该元素"sts === 6"显示这个类名为"prod-info"的view元素
用于展示商品的评价相关信息如评价数量和好评率等同样通过插值表达式来展示具体数据
-->
<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">
<text
v-if="sts===2"
class="deadline-price"
>
<!--
根据"sts"的值来决定是否显示这个类名为"deadline-price"的text元素"sts === 2"显示"限时价"文本用于提示限时优惠价格情况
-->
<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>
@ -43,31 +74,40 @@
</view>
</template>
<script setup>
const wxs = number()
// eslint-disable-next-line no-unused-vars
const props = defineProps({
item: {
type: Object,
default: () => {
return null
// "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
}
}
},
sts: {
type: Number,
default: () => {
return 2
}
}
})
const toProdPage = (e) => {
const prodid = e.currentTarget.dataset.prodid
uni.navigateTo({
url: '/pages/prod/prod?prodid=' + prodid
})
}
// "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
})
}
</script>
<style scoped lang="scss">
@use './production.scss';
// "./production.scss"使scoped
@use './production.scss';
</style>

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

@ -1,99 +1,106 @@
{
"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" ]
}
}
"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
},
/* */
"mp-weixin" : {
"appid" : "wx6fa71e69231a4fa4",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true,
"permission" : {
"scope.userLocation" : {
"desc" : "你的位置信息将用于地址信息新增、修改以及获取附近门店"
}
},
"requiredPrivateInfos" : [ "getLocation", "chooseLocation" ]
"usingComponents": true, // true
"nvueStyleCompiler": "uni-app", // nvue 使 uni-app
"compilerVersion": 3, //
"splashscreen": {
"alwaysShowBeforeRender": true, // true
"waiting": true, // true
"autoclose": true, // true
"delay": 0 // 0
},
"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" : ""
}
}
}
/* */
"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": "你的位置信息将用于地址信息新增、修改以及获取附近门店" //
}
},
"uniStatistics" : {
"enable" : false
"requiredPrivateInfos": [ "getLocation", "chooseLocation" ] //
},
"h5": {
"title": "", // H5
"domain": "https://mini-h5.mall4j.com", // H5
"router": {
"mode": "history" // 使 history
},
"vueVersion" : "3"
"uniStatistics": {
"enable": false // Uni-App false
},
"optimization": {
"treeShaking": {
"enable": false // Tree Shaking false
}
},
"template": "index.html", // H5
"devServer": {
"disableHostCheck": true, // true
"port": 80 //
},
"sdkConfigs": {
"maps": {
"qqmap": {
"key": "" // QQ API Key H5 IP
}
}
}
},
"uniStatistics": {
"enable": false // Uni-App false
},
"vueVersion": "3" // 使 Vue Vue 3
}

@ -1,196 +1,200 @@
{
"pages": [
{
"path": "pages/index/index",
"path": "pages/index/index", //
"style": {
"backgroundTextStyle": "dark",
"navigationBarBackgroundColor": "#fff",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": true,
"navigationBarTitleText": "mall4j"
"backgroundTextStyle": "dark", // "dark"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTextStyle": "black", //
"enablePullDownRefresh": true, // 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",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "购物车",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "购物车", //
"navigationBarTextStyle": "black" //
}
},
{
"path": "pages/category/category",
"path": "pages/category/category", //
"style": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "分类商品",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "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",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "搜索",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "搜索", //
"navigationBarTextStyle": "black" //
}
},
{
"path": "pages/delivery-address/delivery-address",
"path": "pages/delivery-address/delivery-address", //
"style": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "收货地址",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "收货地址", //
"navigationBarTextStyle": "black" //
}
},
{
"path": "pages/editAddress/editAddress",
"path": "pages/editAddress/editAddress", //
"style": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "编辑收货地址",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "编辑收货地址", //
"navigationBarTextStyle": "black" //
}
},
{
"path": "pages/orderList/orderList",
"path": "pages/orderList/orderList", //
"style": {
"backgroundTextStyle": "light",
"navigationBarTitleText": "订单列表",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#fafafa"
"backgroundTextStyle": "light", // "light"
"navigationBarTitleText": "订单列表", //
"navigationBarTextStyle": "black", //
"navigationBarBackgroundColor": "#fafafa" //
}
},
{
"path": "pages/order-detail/order-detail",
"path": "pages/order-detail/order-detail", //
"style": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "订单详情",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "订单详情", //
"navigationBarTextStyle": "black" //
}
},
{
"path": "pages/submit-order/submit-order",
"path": "pages/submit-order/submit-order", //
"style": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "提交订单",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "提交订单", //
"navigationBarTextStyle": "black" //
}
},
{
"path": "pages/express-delivery/express-delivery",
"path": "pages/express-delivery/express-delivery", //
"style": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "物流查询",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "物流查询", //
"navigationBarTextStyle": "black" //
}
},
{
"path": "pages/pay-result/pay-result",
"path": "pages/pay-result/pay-result", //
"style": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "支付结果",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "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",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "搜索结果",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "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
"onReachBottomDistance": 0 // 0
}
},
{
"path": "pages/recent-news/recent-news",
"path": "pages/recent-news/recent-news", //
"style": {
"backgroundTextStyle": "light",
"navigationBarTitleText": "最新公告",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#fafafa"
"backgroundTextStyle": "light", // "light"
"navigationBarTitleText": "最新公告", //
"navigationBarTextStyle": "black", //
"navigationBarBackgroundColor": "#fafafa" //
}
},
{
"path": "pages/news-detail/news-detail",
"path": "pages/news-detail/news-detail", //
"style": {
"backgroundTextStyle": "light",
"navigationBarTitleText": "最新公告",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#fafafa"
"backgroundTextStyle": "light", // "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",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
"backgroundTextStyle": "light", // "light"
"navigationBarBackgroundColor": "#fff", //
"navigationBarTitleText": "WeChat", //
"navigationBarTextStyle": "black" //
},
"subPackages": []
"subPackages": [] //
}

@ -1,8 +1,18 @@
/*
.con 100%
50px
*/
.con {
background: #fff;
height: 100%;
margin-top: 50px;
}
/*
image 使便
150rpx margin: auto 50%使
8%
*/
image {
display: block;
width: 150rpx;
@ -13,11 +23,22 @@ 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;
@ -35,6 +56,12 @@ image {
padding: 8rpx;
margin-top: 80rpx;
}
/*
to-idx-btn 90%
#eeeeee#3336rpx26rpx8rpx
30rpx
*/
.to-idx-btn {
width: 90%;
margin: 0 auto;
@ -50,6 +77,11 @@ image {
padding: 8rpx;
margin-top: 30rpx;
}
/*
form-title 100%50rpx
32rpx#00a0e9便
*/
.form-title {
width: 100%;
margin-bottom: 50rpx;
@ -59,11 +91,22 @@ image {
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;
@ -71,28 +114,51 @@ image {
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;
@ -103,7 +169,13 @@ button {
margin-top: 10rpx;
font-size: 28rpx;
margin-top: 10rpx;
.warning-icon {
/*
.warning-icon 使
26rpx26rpx#e4313050%使
12rpx
*/
display: inline-block;
color: #fff;
width: 26rpx;

@ -1,172 +1,183 @@
<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>
<input
type="text"
data-type="account"
placeholder-class="inp-palcehoder"
placeholder="请输入用户名"
@input="getInputVal"
>
<!-- 账号输入框类型为文本输入框type="text"绑定了自定义数据属性"data-type""account"
设置了占位符样式类和具体的占位符文本同时监听输入事件当输入时会触发"getInputVal"方法 -->
<input type="text"
data-type="account"
placeholder-class="inp-palcehoder"
placeholder="请输入用户名"
@input="getInputVal">
</view>
<view
v-if="errorTips==1"
class="error-text"
>
<!-- "errorTips"等于1时显示这个错误提示文本元素类名为"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>
<button
class="authorized-btn"
@tap="login"
>
登录
</button>
<button
class="to-idx-btn"
@tap="toIndex"
>
回到首页
</button>
</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>
</template>
<script setup>
import { encrypt } from '@/utils/crypto.js'
// "@/utils/crypto.js""encrypt"
import { encrypt } from '@/utils/crypto.js'
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
//
uni.setNavigationBarTitle({
title: '用户登录'
/**
* 生命周期函数--监听页面显示当页面显示时执行以下代码
* 设置页面头部导航栏的标题为"用户登录"
*/
onShow(() => {
//
uni.setNavigationBarTitle({
title: '用户登录'
})
})
})
const principal = ref('') //
const errorTips = ref(0) //
watch(
() => principal.value,
() => {
errorTips.value = 0
}
)
// 使Vueref"principal"
const principal = ref('') //
// 使Vueref"errorTips"0
const errorTips = ref(0) //
// "principal""errorTips"0
watch(
() => principal.value,
() => {
errorTips.value = 0
}
)
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
// 使Vueref"credentials"
const credentials = ref('') //
/**
* 输入框的值发生变化时触发的方法用于获取输入框输入的值并根据输入框的类型账号或密码分别存储到对应的响应式数据中
* @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 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)
}
/**
* 登录按钮点击时触发的方法用于处理登录逻辑
* 首先判断账号和密码是否为空如果为空则设置相应的错误提示如果都不为空则发起登录请求
* 将加密后的密码和账号信息作为请求数据发送到服务器的"/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 toRegitser = () => {
uni.navigateTo({
url: '/pages/register/register'
})
}
/**
* 跳转到注册页面的方法使用uni-app的导航函数跳转到"/pages/register/register"页面
*/
const toRegitser = () => {
uni.navigateTo({
url: '/pages/register/register'
})
}
/**
* 回到首页
*/
const toIndex = () => {
wx.switchTab({
url: '/pages/index/index'
})
}
/**
* 回到首页的方法使用微信小程序相关的切换页面函数这里可能是在uni-app中兼容微信小程序的写法切换到"/pages/index/index"页面
*/
const toIndex = () => {
wx.switchTab({
url: '/pages/index/index'
})
}
</script>
<style scoped lang="scss">
@import "./accountLogin.scss";
// "./accountLogin.scss"
// "scoped"
@import "./accountLogin.scss";
</style>

@ -1,365 +1,391 @@
/* 容器样式 */
.container {
width: 100%;
background: #f4f4f4;
min-height: calc(100vh - 118rpx);
width: 100%; /* 设置容器宽度为100% */
background: #f4f4f4; /* 设置背景颜色 */
min-height: calc(100vh - 118rpx); /* 设置最小高度为视窗高度减去底部导航栏高度 */
}
/* 产品列表样式 */
.prod-list {
padding-bottom: 118rpx;
width: 100%;
padding-bottom: 118rpx; /* 底部填充以防止内容被固定底部遮挡 */
width: 100%; /* 设置宽度为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; /* 左内边距 */
}
.pic {
text-align: center;
width: 180rpx;
height: 180rpx;
line-height: 180rpx;
font-size: 0;
}
}
&:last-child {
.prodinfo {
/* 最后一个子元素去除底线 */
&:last-child {
&::after {
height: 0;
height: 0; /* 高度设为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;
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; /* WebKit行盒模型 */
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 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; /* 内边距 */
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 {
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;
.minus,
.plus {
float: left; /* 向左浮动 */
box-sizing: border-box; /* 盒模型 */
height: 56rpx; /* 高度 */
border: 2rpx solid #d9d9d9; /* 边框 */
position: relative; /* 相对定位 */
width: 56rpx; /* 宽度 */
&::before,
&::after {
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 {
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;
}
border-left: 0; /* 左边框 */
border-top-right-radius: 4rpx; /* 右上圆角 */
border-bottom-right-radius: 4rpx; /* 右下圆角 */
&::after {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
content: ' ';
width: 22rpx;
height: 3rpx;
background-color: #7f7f7f;
transform: rotate(90deg);
transform: rotate(90deg); /* 旋转90度 */
}
}
float: right;
float: right; /* 向右浮动 */
/* 非禁用状态下的交互样式 */
&:not(.disabled) {
.minus {
&:not(.disabled) {
&:active {
background-color: #f4f4f4;
}
}
}
.minus,
.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);
text-align: center; /* 文本居中 */
font-size: 22rpx; /* 字体大小 */
color: #fff; /* 文字颜色 */
background: transparent; /* 透明背景 */
transform: translate(-50%, -50%) scale(1); /* 居中缩放 */
-webkit-transform: translate(-50%, -50%) scale(1); /* WebKit浏览器居中缩放 */
}
}
}
/* 空状态样式 */
.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,82 +1,86 @@
<template>
<!-- 页面的最外层容器用于包裹整个购物车页面内容 -->
<view class="container">
<!-- 商品列表区域用于展示购物车中的商品信息 -->
<view class="prod-list">
<block
v-for="(item, scIndex) in shopCartItemDiscounts"
:key="scIndex"
>
<!-- 使用v-for循环遍历shopCartItemDiscounts数组scIndex为当前项的索引每个元素对应一组商品相关的数据可能是不同优惠规则下的商品集合等情况 -->
<block v-for="(item, scIndex) in shopCartItemDiscounts" :key="scIndex">
<view class="prod-block">
<view
v-if="item.chooseDiscountItemDto"
class="discount-tips"
>
<!-- 如果当前商品组有对应的优惠信息chooseDiscountItemDto存在则展示优惠提示信息 -->
<view v-if="item.chooseDiscountItemDto" class="discount-tips">
<!-- 展示优惠规则文本通过wxs.parseDiscount函数对优惠规则进行解析处理后展示 -->
<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>
<block
v-for="(prod, index) in item.shopCartItems"
:key="index"
>
<!-- 内层v-for循环遍历当前商品组中的每个具体商品prod代表每个商品对象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"
:checked="prod.checked"
color="#105c3e"
@tap="onSelectedItem"
<!-- 单个商品的选择框绑定了一些自定义属性和事件 -->
<checkbox :data-scindex="scIndex" <!-- -->
:data-index="index" <!-- 传递当前商品在组内的索引 -->
:value="prod.prodId" <!-- 绑定商品的唯一标识产品ID -->
: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">
<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"
/>
<!-- 数量减少按钮绑定了点击事件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>
</view>
</view>
@ -87,314 +91,359 @@
</block>
</view>
<view
v-if="!shopCartItemDiscounts.length"
class="empty"
>
<!-- 当购物车中没有商品shopCartItemDiscounts数组长度为0展示此区域提示用户购物车为空 -->
<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>
<!-- 底部按钮 -->
<view
v-if="shopCartItemDiscounts.length>0"
class="cart-footer"
>
<!-- 底部按钮区域当购物车中有商品shopCartItemDiscounts长度大于0时展示 -->
<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>
<view
class="btn del"
@tap="onDelBasket"
>
<text>删除</text>
<!-- 删除按钮所在容器点击触发onDelBasket事件 -->
<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>
<view
v-if="subtractMoney>0"
class="total-msg"
>
<!-- 当有立减金额subtractMoney大于0展示总额和立减金额信息 -->
<view v-if="subtractMoney>0" class="total-msg">
总额:{{ wxs.toPrice(totalMoney) }} 立减:{{ wxs.toPrice(subtractMoney) }}
</view>
</view>
<view
class="btn settle"
@tap="toFirmOrder"
>
<text>结算</text>
<!-- 结算按钮所在容器点击触发toFirmOrder事件 -->
<view class="btn settle" @tap="toFirmOrder">
<text>结算</text> <!-- 按钮显示文字 -->
</view>
</view>
<!-- end 底部按钮 -->
<!-- 结束底部按钮区域 -->
</view>
</template>
<script setup>
const wxs = number()
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
loadBasketData()
http.getCartCount() //
})
const allChecked = ref(false)
const shopCartItemDiscounts = ref([])
const loadBasketData = () => {
uni.showLoading() //
http.request({
url: '/p/shopCart/info',
method: 'POST',
data: {}
// wxsnumber()
const wxs = number()
// -
onShow(() => {
//
loadBasketData()
//
http.getCartCount()
})
.then(({ data }) => {
if (data && data.length > 0) {
//
const shopCartItemDiscountsParam = data[0].shopCartItemDiscounts
shopCartItemDiscountsParam.forEach(shopCartItemDiscount => {
shopCartItemDiscount.shopCartItems.forEach(shopCartItem => {
shopCartItem.checked = false
})
})
shopCartItemDiscounts.value = shopCartItemDiscountsParam
allChecked.value = false
} else {
shopCartItemDiscounts.value = []
}
calTotalPrice() //
uni.hideLoading()
// false
const allChecked = ref(false)
//
const shopCartItemDiscounts = ref([])
//
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
})
})
// 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 => {
if (shopCartItem.checked) {
basketIds.push(shopCartItem.basketId)
}
//
const toFirmOrder = () => {
//
const shopCartItemDiscountsParam = shopCartItemDiscounts.value
const basketIds = []
//
shopCartItemDiscountsParam.forEach(shopCartItemDiscount => {
//
shopCartItemDiscount.shopCartItems.forEach(shopCartItem => {
// basketIdbasketIds
if (shopCartItem.checked) {
basketIds.push(shopCartItem.basketId)
}
})
})
})
if (!basketIds.length) {
uni.showToast({
title: '请选择商品',
icon: 'none'
// basketIds0
if (!basketIds.length) {
// Toastnone
uni.showToast({
title: '请选择商品',
icon: 'none'
})
return
}
// basketIdsJSON
uni.setStorageSync('basketIds', JSON.stringify(basketIds))
// orderEntry=0
uni.navigateTo({
url: '/pages/submit-order/submit-order?orderEntry=0'
})
return
}
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
//
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 // 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 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 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
//
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
}
}
//
if (flag) break
}
if (flag) break
//
allChecked.value = allCheckedParam
}
allChecked.value = allCheckedParam
}
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)
// 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)
}
}
}
}
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()
//
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()
})
}
/**
* 减少数量
*/
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 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 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
}
})
.then(() => {
shopCartItemDiscountsParam[scindex].shopCartItems[index].prodCount += prodCount
shopCartItemDiscounts.value = shopCartItemDiscountsParam
calTotalPrice() //
uni.hideLoading()
http.getCartCount() //
/**
* 改变购物车数量接口
*/
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() //
})
}
/**
* 删除购物车商品
*/
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
})
.then(() => {
uni.hideLoading()
loadBasketData()
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()
})
}
}
}
})
})
}
}
}
</script>
<style scoped lang="scss">

@ -1,14 +1,31 @@
/*
.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;
@ -19,6 +36,11 @@
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;
@ -29,6 +51,11 @@
left: 30rpx;
top: 41rpx;
}
/*
.search-box 使便
60rpx#f7f7f7z-index 999
92%border-radius使 margin: auto 使search-bar
*/
.search-box {
display: flex;
justify-content: center;
@ -40,12 +67,18 @@
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;
@ -53,9 +86,18 @@
top: 32rpx;
}
}
/*
.sear-input
*/
.sear-input {
font-size: 28rpx;
}
/*
.leftmenu box-sizing#f5f6f7
overflow: scrollz-index 2使
*/
.leftmenu {
width: 200rpx;
height: 100%;
@ -63,6 +105,9 @@
background-color: #f5f6f7;
overflow: scroll;
z-index: 2;
/*
.ca-empty leftmenu
*/
.ca-empty {
padding-top: 400rpx;
text-align: center;
@ -70,6 +115,11 @@
font-size: 24rpx;
}
}
/*
.menu-item
.tips-num
*/
.menu-item {
line-height: 90rpx;
height: 90rpx;
@ -78,6 +128,7 @@
position: relative;
color: #777;
font-size: 28rpx;
text.tips-num {
position: absolute;
top: 20rpx;
@ -91,12 +142,18 @@
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;
@ -107,30 +164,48 @@
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;
@ -138,6 +213,7 @@
height: 150rpx;
line-height: 150rpx;
font-size: 0;
.more-pic {
max-width: 100%;
max-height: 100%;
@ -145,10 +221,12 @@
vertical-align: middle;
}
}
position: relative;
display: flex;
justify-content: flex-start;
padding: 20rpx 0;
&::after {
content: '';
background-color: #f4f4f4;
@ -161,9 +239,13 @@
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;
@ -173,6 +255,9 @@
-webkit-line-clamp: 1;
overflow: hidden;
}
/*
.prod-text.more -webkit-box #000
*/
.prod-text.more {
margin: 0;
font-size: 28rpx;
@ -185,6 +270,9 @@
-webkit-box-orient: vertical;
color: #000;
}
/*
.prod-price.more #eb2444arial
*/
.prod-price.more {
font-size: 28rpx;
color: #eb2444;
@ -194,10 +282,19 @@
}
}
}
/*
.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;
@ -206,17 +303,27 @@
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,14 +2,10 @@
<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>
@ -18,156 +14,138 @@
<!-- 滚动内容区 -->
<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>
<!-- 子分类 -->
<view
v-if="subCategoryList.length"
class="th-cate-con"
>
<block
v-for="(thCateItem, index) in subCategoryList"
:key="index"
>
<!-- 这是一个条件渲染的视图元素只有当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 class="sub-category">
<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"
/>
<!-- 子分类具体项的视图容器绑定了点击事件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属性来显示具体的名称文本 -->
<text>{{ thCateItem.categoryName }}</text>
</view>
</view>
</block>
</view>
<view
v-else
class="cont-item empty"
>
<!-- 当subCategoryList数组长度为0时显示这个视图元素用于给用户提示当前分类下暂无子分类的信息 -->
<view v-else
class="cont-item empty">
该分类下暂无子分类~
</view>
</scroll-view>
<!-- 右侧内容end -->
</view>
</view>
</template>
<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
})
})
<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
})
})
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 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,30 +1,53 @@
/*
.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;
@ -36,21 +59,31 @@
}
}
}
/*
.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;
@ -59,6 +92,10 @@
}
}
}
/*
.footer position: fixed使bottom: 0100% 100rpx使text-align: center#ffftext#eb2444
*/
.footer {
position: fixed;
bottom: 0;
@ -68,15 +105,24 @@
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;
@ -84,6 +130,9 @@
margin: auto;
}
}
/*
.txt 30rpx#999
*/
.txt {
margin-top: 30rpx;
font-size: 24rpx;

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

@ -1,10 +1,22 @@
/*
.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;
@ -15,26 +27,42 @@
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%;
@ -43,14 +71,23 @@
}
}
}
/*
.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;
@ -64,11 +101,18 @@
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;
@ -79,12 +123,20 @@
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;
@ -92,6 +144,7 @@ picker-view {
height: 380rpx;
bottom: 0;
position: fixed;
text {
color: #999;
display: inline-flex;
@ -104,6 +157,10 @@ 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;
@ -115,6 +172,10 @@ 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;
@ -125,6 +186,10 @@ 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;
@ -133,21 +198,37 @@ 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,82 +1,77 @@
<template>
<!-- 页面的最外层容器用于包裹整个页面内容 -->
<view class="container">
<!--input列表 -->
<!-- input列表区域用于展示多个输入项相关的视图 -->
<view class="input-box">
<!-- 每个输入项的整体视图容器这里用于展示收货人相关的输入项 -->
<view class="section">
<!-- 输入项的文本标签显示 字样用于提示用户此处应输入的内容 -->
<text> </text>
<input
placeholder="姓名"
type="text"
maxlength="15"
:value="receiver"
@input="onReceiverInput"
>
<!-- 文本输入框用于用户输入收货人姓名设置了占位符提示placeholder姓名输入类型为文本type="text"最大输入长度为15个字符maxlength="15"通过 :value 绑定了名为 receiver 的响应式变量来显示和更新输入框中的值并且绑定了 @input 事件onReceiverInput当用户输入内容时会触发该事件进行相应处理 -->
<input placeholder="姓名"
type="text"
maxlength="15"
:value="receiver"
@input="onReceiverInput">
</view>
<!-- 用于展示手机号码相关的输入项的视图容器 -->
<view class="section">
<!-- 输入项的文本标签显示手机号码字样提示用户此处应输入手机号码 -->
<text>手机号码</text>
<input
placeholder="11位手机号码"
type="number"
maxlength="11"
:value="mobile"
@input="onMobileInput"
>
<!-- 文本输入框用于用户输入手机号码设置了占位符提示为11位手机号码输入类型为数字type="number"最大输入长度为11位对应手机号码的长度要求通过 :value 绑定了名为 mobile 的响应式变量来显示和更新输入框中的值同时绑定了 @input 事件onMobileInput用于在输入时进行相关逻辑处理 -->
<input placeholder="11位手机号码"
type="number"
maxlength="11"
:value="mobile"
@input="onMobileInput">
</view>
<view
class="section"
@tap="translate"
>
<!-- 用于展示所在地区相关的输入项及操作的视图容器绑定了点击事件 translate点击可触发地区选择相关的操作如弹出地区选择器等 -->
<view class="section"
@tap="translate">
<!-- 输入项的文本标签显示所在地区字样提示用户此处应选择所在地区信息 -->
<text>所在地区</text>
<!-- 用于展示已选择的地区信息省份城市区县的视图元素通过双括号插值表达式绑定对应的响应式变量provincecityarea来显示具体的地区名称 -->
<view class="pca">
{{ province }} {{ city }} {{ area }}
</view>
<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"
>
<!-- 这是一个用于实现地区选择器弹出效果的外层包装元素通过绑定 :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">
确定
</text>
<!-- 水平分割线元素用于在视觉上区分不同区域可能是按钮与选择器部分的区分等 -->
<view class="line" />
<picker-view
indicator-style="height: 50rpx;"
:value="valArr"
@change="bindChange"
@tap.stop="nono"
>
<!---->
<!-- 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-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>
@ -84,350 +79,343 @@
</view>
</view>
<!-- 箭头图标元素用于提示用户可点击展开地区选择器等操作内部通过指定的路径引入图片资源一般是一个向右的箭头图标 -->
<view class="arrow">
<image src="@/static/images/icon/more.png" />
</view>
</view>
<!-- 用于展示详细地址相关的输入项的视图容器 -->
<view class="section">
<!-- 输入项的文本标签显示详细地址字样提示用户此处应输入详细的收货地址信息 -->
<text>详细地址</text>
<input
placeholder="如楼号/单元/门牌号"
type="text"
:value="addr"
@input="onAddrInput"
>
<!-- 文本输入框用于用户输入详细地址设置了占位符提示为如楼号/单元/门牌号通过 :value 绑定了名为 addr 的响应式变量来显示和更新输入框中的值并且绑定了 @input 事件onAddrInput用于在输入时进行相关逻辑处理 -->
<input placeholder="如楼号/单元/门牌号"
type="text"
:value="addr"
@input="onAddrInput">
</view>
</view>
<!-- end input列表 -->
<!-- 功能按钮 -->
<!-- input列表区域结束 -->
<!-- 功能按钮区域包含保存地址删除地址等功能按钮 -->
<view class="btn-box">
<view
class="keep btn"
@tap="onSaveAddr"
>
<!-- 保存收货地址按钮设置了类名为 keep btn绑定了点击事件 onSaveAddr点击可触发保存当前输入的收货地址信息的操作 -->
<view class="keep btn"
@tap="onSaveAddr">
<text>保存收货地址</text>
</view>
<view
v-if="addrId!=0"
class="clear btn"
@tap="onDeleteAddr"
>
<!-- 条件渲染的删除收货地址按钮只有当 addrId 不等于0时才显示可能表示当前是在编辑已有地址的场景下才显示删除按钮设置了类名为 clear btn绑定了点击事件 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) {
uni.showLoading()
//
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) => {
uni.showLoading()
http.request({
url: '/p/address/addrInfo/' + options.addrId,
method: 'GET'
url: '/p/area/listByPid',
method: 'GET',
data: {
pid: 0 //
}
})
.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)
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
uni.hideLoading()
})
} else {
initCityData(provinceId.value, cityId.value, areaId.value)
}
})
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
//
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) //
}
})
.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()
})
}
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('')
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) //
const translate = () => {
if (t == 0) {
moveY = 0
show.value = true
t = 1
} else {
moveY = 200
show.value = false
t = 0
}
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
}
let t = 0
let moveY = 200
const show = ref('')
/**
* 移动按钮点击事件
*/
const translate = () => {
if (t == 0) {
moveY = 0
show.value = true
t = 1
} else {
//
const hiddenFloatView = () => {
moveY = 200
show.value = false
t = 0
animationEvents(moveY, show.value)
}
animationEvents(moveY, show.value)
}
/**
* 隐藏弹窗浮层
*/
const hiddenFloatView = () => {
moveY = 200
show.value = false
t = 0
animationEvents(moveY, show.value)
}
const animation = ref('')
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 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 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]]
}
}
// ID
const getCityArray = (provinceId, cityId, areaId) => {
http.request({
url: '/p/area/listByPid',
method: 'GET',
data: {
pid: provinceId // IDID
}
getAreaArray(cityId || data[0].areaId, areaId)
uni.hideLoading()
})
}
.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()
})
}
/**
* 根据城市ID获取 区数据
*/
const getAreaArray = (cityId, areaId) => {
http.request({
url: '/p/area/listByPid',
method: 'GET',
data: {
pid: cityId
}
}).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 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)] //
}
}
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
}
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()
})
}
/**
* 保存地址
*/
const onSaveAddr = () => {
const receiverParam = receiver.value
const mobileParam = mobile.value
const addrParam = addr.value
if (!receiverParam.trim()) {
receiver.value = ''
uni.showToast({
title: '请输入收货人姓名',
icon: 'none'
uni.hideLoading()
})
return
}
if (!mobileParam) {
uni.showToast({
title: '请输入手机号码',
icon: 'none'
})
return
}
//
const onSaveAddr = () => {
const receiverParam = receiver.value
const mobileParam = mobile.value
const addrParam = addr.value
if (mobileParam.length != 11) {
uni.showToast({
title: '请输入正确的手机号码',
icon: 'none'
})
return
}
//
if (!receiverParam.trim()) {
receiver.value = ''
uni.showToast({
title: '请输入收货人姓名',
icon: 'none'
})
return
}
if (!addrParam.trim()) {
receiver.value = ''
uni.showToast({
title: '请输入详细地址',
icon: 'none'
})
return
}
if (!mobileParam) {
uni.showToast({
title: '请输入手机号码',
icon: 'none'
})
return
}
if (mobileParam.length != 11) {
uni.showToast({
title: '请输入正确的手机号码',
icon: 'none'
})
return
}
uni.showLoading()
let url = '/p/address/addAddr'
let method = 'POST'
if (!addrParam.trim()) {
receiver.value = ''
uni.showToast({
title: '请输入详细地址',
icon: 'none'
})
return
}
if (addrId.value != 0) {
url = '/p/address/updateAddr'
method = 'PUT'
} //
uni.showLoading()
let url = '/p/address/addAddr'
let method = 'POST'
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 (addrId.value != 0) { // ID
url = '/p/address/updateAddr'
method = 'PUT'
}
})
.then(() => {
uni.hideLoading()
uni.navigateBack({
delta: 1
})
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
}
})
}
const onReceiverInput = (e) => {
receiver.value = e.detail.value
}
const onMobileInput = (e) => {
mobile.value = e.detail.value
}
const onAddrInput = (e) => {
addr.value = e.detail.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',
//
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
})
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,50 +1,67 @@
/* 设置页面背景颜色 */
page {
background: #f7f8fa;
}
/* 容器高度设置为100%,确保容器占据整个页面的高度 */
.container {
height: 100%;
}
/* 设置顶部内边距,以适应可能存在的导航栏或其他顶部组件 */
.padding20 {
padding-top: 88rpx;
padding-top: 88rpx; /* rpx 是相对于屏幕宽度的单位,适用于移动设备 */
}
/* 左浮动样式 */
.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;
transition: transform 0.3s, -webkit-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;
@ -52,14 +69,20 @@ page {
line-height: 34rpx;
}
}
/* 当前选中的导航项 */
.nav-item.active {
text {
color: #b4282d;
color: #b4282d; /* 改变文字颜色 */
}
}
/* 图标对齐方式 */
.u-icon {
vertical-align: middle;
}
/* 物流信息展示区域 */
.deliveryInfo {
height: 198rpx;
width: 100%;
@ -69,24 +92,23 @@ page {
display: table;
position: relative;
box-sizing: border-box;
.companyname {
.companyname, .expno { /* 快递公司名称和快递单号 */
line-height: 1;
margin-left: 136rpx;
font-size: 28rpx;
.key {
.key { /* 标签部分的颜色 */
color: #666;
}
}
.expno {
line-height: 1;
margin-left: 136rpx;
font-size: 28rpx;
.expno { /* 快递单号样式 */
margin-top: 16rpx;
.key {
color: #666;
}
}
}
/* 物流图标 */
.icon-express {
width: 104rpx;
height: 104rpx;
@ -95,22 +117,30 @@ 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;
@ -121,29 +151,37 @@ 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;
@ -151,11 +189,14 @@ 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;
@ -165,6 +206,8 @@ page {
line-height: 80rpx;
margin-bottom: 20rpx;
}
/* 空白占位符 */
.empty-space {
margin-top: 20rpx;
background: #fff;

@ -1,16 +1,21 @@
<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">
物流公司
@ -19,6 +24,7 @@
{{ companyName }}
</text>
</view>
<!-- 运单编号 -->
<view class="expno">
<text class="key">
运单编号
@ -29,30 +35,39 @@
</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"
@ -64,30 +79,39 @@
</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',
method: 'GET',
url: '/delivery/check', // URL
method: 'GET', // HTTP
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,3 +1,4 @@
/* 容器背景颜色设置为浅灰色,并且高度根据内容自动调整 */
.container {
background: #f7f7f7;
height: auto;
@ -5,409 +6,443 @@
/* 轮播图及搜索框 */
swiper {
width: 100%;
height: 350rpx;
overflow: hidden;
width: 100%; /* 占满整个宽度 */
height: 350rpx; /* 高度为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-line-clamp: 1;
-webkit-box-orient: vertical;
text-align: left;
text-overflow: ellipsis; /* 超出文本显示省略号 */
display: -webkit-box; /* 使用Webkit盒模型 */
-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);
position: absolute;
right: 30rpx;
top: 34rpx;
width: 15rpx; /* 宽度 */
height: 15rpx; /* 高度 */
border-top: 3rpx solid #686868; /* 上边框 */
border-right: 3rpx solid #686868; /* 右边框 */
transform: rotate(45deg); /* 旋转45度形成箭头 */
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+IEmuOgAAAZBJREFUeJzt1DEBwCAAwLAxYfhEGXJABkcTBb065trnAwj6XwcAvGKAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGQZIJBlgECWAQJZBghkGSCQZYBAlgECWQYIZBkgkGWAQJYBAlkGCGRdKykDj9OUNYkAAAAASUVORK5CYII=");
background-position: top;
background-size: 100% 332rpx;
background-repeat: no-repeat;
background-color: #fff;
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; /* 白色背景 */
.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;
word-break: break-all;
display: -webkit-box;
-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; /* 使用Webkit盒模型 */
word-break: break-all; /* 单词换行 */
display: -webkit-box; /* 使用Webkit盒模型 */
-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;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
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; /* 垂直排列 */
}
.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,15 +1,17 @@
<template>
<!-- 页面的最外层容器用于包裹整个页面内容 -->
<view class="container">
<!-- 搜索栏背景区域用于设置搜索相关部分的背景样式 -->
<view class="bg-sear">
<!-- 可滚动到顶部的区域通常用于放置一些操作元素方便用户能快速回到页面顶部 -->
<view class="scrolltop">
<view
class="section"
@tap="toSearchPage"
>
<image
src="@/static/images/icon/search.png"
class="search-img"
/>
<!-- 搜索栏的具体视图容器绑定了点击事件 `toSearchPage`点击此处可触发跳转到搜索页面的操作 -->
<view class="section"
@tap="toSearchPage">
<!-- 搜索图标元素通过指定路径引入图片资源设置类名为 `search-img`用于在搜索栏中显示搜索图标提示用户此处可进行搜索操作 -->
<image src="@/static/images/icon/search.png"
class="search-img" />
<!-- 搜索栏的占位文本显示搜索字样提示用户在此处输入想要搜索的内容 -->
<text class="placeholder">
搜索
</text>
@ -17,156 +19,151 @@
</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"
>
<block
v-for="(item, index) in indexImgs"
:key="index"
>
<!-- 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-item class="banner-item">
<!-- 图片的外层视图容器方便对图片进行样式设置和布局调整 -->
<view class="img-box">
<image
:src="item.imgUrl"
:data-prodid="item.relation"
class="banner"
@tap="toProdPage"
/>
<!-- 轮播图中的图片元素通过 `:src` 绑定图片的 URL 地址来自 `item.imgUrl`同时利用自定义属性 `:data-prodid` 传递商品相关的 ID 信息并且绑定了点击事件 `toProdPage`点击图片可触发跳转到对应商品页面的操作 -->
<image :src="item.imgUrl"
:data-prodid="item.relation"
class="banner"
@tap="toProdPage" />
</view>
</swiper-item>
</block>
</swiper>
<!-- end swiper -->
<!-- 轮播图部分结束 -->
<!-- 分类导航区域展示多个不同分类的导航项每个项可点击跳转到对应的页面 -->
<view class="cat-item">
<view
class="item"
data-sts="1"
@tap="toClassifyPage"
>
<!-- 单个分类导航项的视图容器设置了自定义属性 `data-sts="1"`并绑定点击事件 `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"
>
<view class="item"
@tap="toCouponCenter">
<!-- 该图标表示领优惠券分类相关标识文本标签显示对应操作名称点击此项会触发领优惠券相关的业务逻辑具体由 `toCouponCenter` 函数处理 -->
<image src="@/static/images/icon/newprods.png" />
<text>领优惠券</text>
</view>
</view>
<!-- 消息播放 -->
<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"
>
<!-- 消息播放区域只有当 `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` -->
<swiper-item class="items">
{{ item.title }}
</swiper-item>
</block>
</swiper>
<!-- 箭头图标元素可能用于视觉上提示用户可点击查看更多消息等操作具体功能要结合样式和交互设计来看 -->
<text class="arrow" />
</view>
</view>
<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"
>
<!-- 商品展示相关区域通过条件判断 `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 class="title">
<!-- 通过双括号插值表达式展示商品组的标题数据来源于 `item.title` 属性 -->
<text>{{ item.title }}</text>
<view
class="more-prod-cont"
data-sts="0"
:data-id="item.id"
:data-title="item.title"
@tap="toClassifyPage"
>
<!-- 查看更多操作按钮的视图容器设置了多个自定义属性`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">
<!-- 查看更多的文本标签 -->
<text class="more">
查看更多
</text>
</view>
</view>
<!-- 商品列表展示区域通过循环遍历 `item.prods` 数组展示每个商品的详细信息 -->
<view class="item-cont">
<block
v-for="(prod, index2) in item.prods"
:key="index2"
>
<view
class="prod-item"
:data-prodid="prod.prodId"
@tap="toProdPage"
>
<block v-for="(prod, index2) in item.prods"
:key="index2">
<!-- 单个商品的视图容器绑定点击事件 `toProdPage`点击可触发跳转到对应商品页面的操作通过自定义属性 `:data-prodid` 传递商品的 ID 信息 -->
<view class="prod-item"
:data-prodid="prod.prodId"
@tap="toProdPage">
<view>
<!-- 商品图片的外层视图容器用于包裹图片展示相关组件 -->
<view class="imagecont">
<img-show
:src="prod.pic"
:class-list="['prodimg']"
/>
<!-- `img-show` 组件用于展示商品图片可能是自定义的图片展示组件具备特定功能或样式处理通过 `:src` 绑定商品图片的 URL 地址`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>
@ -177,50 +174,51 @@
</view>
</view>
<!-- 商城热卖 -->
<view
v-if="item.style==='1' && item.prods && item.prods.length"
class="hot-sale"
>
<!-- 商城热卖类型商品展示区域只有当商品组的 `style` 属性为 `'1'` 且包含商品数据`item.prods` 存在且长度大于 0时才显示 -->
<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"
>
<view
class="prod-items"
:data-prodid="prod.prodId"
@tap="toProdPage"
>
<block v-for="(prod, index2) in item.prods"
:key="index2">
<!-- 单个商品的视图容器绑定点击事件 `toProdPage`点击可触发跳转到对应商品页面的操作通过自定义属性 `:data-prodid` 传递商品的 ID 信息 -->
<view class="prod-items"
:data-prodid="prod.prodId"
@tap="toProdPage">
<view class="hot-imagecont">
<img-show
:src="prod.pic"
:class-list="['hotsaleimg']"
/>
<!-- 商品图片展示的外层视图容器内部使用 `img-show` 组件展示商品图片原理与前面类似 -->
<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">
@ -232,10 +230,9 @@
.{{ 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>
@ -243,38 +240,39 @@
</view>
</view>
<!-- 更多宝贝 -->
<view
v-if="item.style==='0' && item.prods && item.prods.length"
class="more-prod"
>
<!-- 更多宝贝类型商品展示区域只有当商品组的 `style` 属性为 `'0'` 且包含商品数据`item.prods` 存在且长度大于 0时才显示 -->
<view v-if="item.style==='0' && item.prods && item.prods.length"
class="more-prod">
<!-- 商品组标题区域直接通过双括号插值表达式展示商品组的标题来源于 `item.title` 属性 -->
<view class="title">
{{ item.title }}
</view>
<!-- 商品列表展示区域通过循环遍历 `item.prods` 数组展示每个商品的详细信息 -->
<view class="prod-show">
<block
v-for="(prod, index2) in item.prods"
:key="index2"
>
<view
class="show-item"
:data-prodid="prod.prodId"
@tap="toProdPage"
>
<block v-for="(prod, index2) in item.prods"
:key="index2">
<!-- 单个商品的视图容器绑定点击事件 `toProdPage`点击可触发跳转到对应商品页面的操作通过自定义属性 `:data-prodid` 传递商品的 ID 信息 -->
<view class="show-item"
:data-prodid="prod.prodId"
@tap="toProdPage">
<view class="more-prod-pic">
<img-show
:src="prod.pic"
:class-list="['more-pic']"
/>
<!-- 商品图片展示的外层视图容器内部使用 `img-show` 组件展示商品图片方式与前面一致 -->
<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">
@ -286,11 +284,10 @@
.{{ wxs.parsePrice(prod.price)[1] }}
</text>
</view>
<image
src="@/static/images/tabbar/basket-sel.png"
class="basket-img"
@tap.stop="addToCart(prod)"
/>
<!-- 购物车图标元素通过指定路径引入图片资源绑定点击事件 `addToCart(prod)`点击可触发将该商品加入购物车的操作 -->
<image src="@/static/images/tabbar/basket-sel.png"
class="basket-img"
@tap.stop="addToCart(prod)" />
</view>
</view>
</view>

@ -1,23 +1,42 @@
/* 定义新闻详情页的整体样式 */
.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,41 +1,59 @@
<template>
<view class="container">
<!-- 新闻详情容器 -->
<view class="news-detail">
<!-- 动态绑定新闻标题使用双大括号语法插值显示 news 对象中的 title 属性 -->
<view class="news-detail-title">
{{ news.title }}
</view>
<rich-text
class="content"
:nodes="news.content"
<!-- 使用 rich-text 组件来解析并渲染 HTML 内容动态绑定 news 对象中的 content 属性 -->
<rich-text class="content"
:nodes="news.content" <!-- :nodes v-bind:nodes 的缩写用于绑定 HTML 字符串 -->
/>
</view>
</view>
</template>
<script setup>
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
// 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' //
})
})
.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">
@use './news-detail.scss';
/* 引入外部的 SCSS 文件,以定义组件的样式 */
@use './news-detail.scss';
</style>

@ -1,247 +1,284 @@
/* 定义容器的基本样式 */
.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;
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;
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; /* 作为行内块级元素显示 */
}
}
.addr {
font-size: 26rpx;
line-height: 36rpx;
color: #999;
word-wrap: break-word;
.addr { /* 地址的样式 */
font-size: 26rpx; /* 设置字体大小 */
line-height: 36rpx; /* 设置行高 */
color: #999; /* 设置字体颜色为浅灰色 */
word-wrap: break-word; /* 允许长单词或URL地址换行到下一行 */
}
}
}
/* 商品项的样式 */
.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%;
position: relative;
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; /* 设置相对定位 */
height: 80px;
-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;
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; /* 在任何字符间断开 */
}
.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%;
.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%; /* 设置宽度 */
}
.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%;
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;
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; /* 文本居中 */
}
.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,65 +1,35 @@
<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.area
}}{{ userAddrDto.addr }}
{{ userAddrDto.province }}{{ userAddrDto.city }}{{ 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>
<text class="number">数量{{ item.prodCount }}</text> <!-- 显示商品数量 -->
<text class="info-item">{{ item.skuName }}</text> <!-- 显示SKU信息 -->
</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>
@ -72,228 +42,176 @@
<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">
<text
v-if="!!remarks"
class="item-tit"
>
订单备注
</text>
<text class="item-txt remarks">
{{ remarks }}
</text>
<view class="item" v-if="!!remarks">
<text 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>
const wxs = number()
import { ref } from 'vue';
import { onLoad, uni } from '@dcloudio/uni-app'; //
//
const wxs = number();
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
loadOrderDetail(options.orderNum)
})
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
loadOrderDetail(options.orderNum); //
});
/**
* 跳转商品详情页
* @param e
*/
const toProdPage = (e) => {
const prodid = e.currentTarget.dataset.prodid
uni.navigateTo({
url: '/pages/prod/prod?prodid=' + prodid
})
}
/**
* 跳转商品详情页
* @param e - 触发事件对象
*/
const toProdPage = (e) => {
const prodid = e.currentTarget.dataset.prodid; // ID
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) //
/**
* 加载订单数据
*/
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()
//
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 //
}
})
}
.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,
method: 'DELETE'
})
.then(() => {
uni.hideLoading()
uni.showToast({
title: res || '删除成功',
icon: 'none'
})
setTimeout(() => {
uni.redirectTo({
url: '/pages/orderList/orderList'
})
}, 1000)
/**
* 删除已完成||已取消的订单
*/
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' //
})
.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';
@use './order-detail.scss'; /* 引入外部的 SCSS 文件,以定义组件的样式 */
</style>

@ -1,40 +1,50 @@
<style scoped lang="scss" >
/* 定义全局容器 */
.container {
position: absolute;
width: 100%;
height: 100%;
background-color: #f4f4f4;
color: #333;
position: absolute; /* 绝对定位相对于最近的非static祖先元素 */
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;
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; /* 高度为100个rpx根据设备宽度自适应 */
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 {
@ -43,172 +53,199 @@
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;
display: -webkit-flex;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
background: #fafafa;
display: flex; /* 使用弹性布局 */
align-items: center; /* 交叉轴上居中对齐 */
padding: 20rpx 30rpx; /* 内边距 */
border-radius: 10rpx; /* 圆角 */
background: #fafafa; /* 背景颜色 */
.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;
margin-left: 10rpx; /* 左侧内边距 */
font-size: 28rpx; /* 字体大小 */
width: 100%; /* 宽度占满剩余空间 */
position: relative; /* 相对定位 */
height: 160rpx; /* 高度 */
flex: 1; /* 占用剩余空间 */
.prodname {
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;
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; /* 允许在单词内部换行 */
}
.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;
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; /* 允许在单词内部换行 */
}
}
}
.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: -10px;
left: -10rpx;
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,142 +2,89 @@
<view class="container">
<!-- 头部菜单 -->
<view class="order-tit">
<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 v-for="(status, index) in statuses"
:key="index"
:data-sts="status.value"
:class="sts === status.value ? 'on' : ''"
@tap="onStsTap">
{{ status.label }}
</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' : ''
]">
{{
item.status == 1 ? '待支付' : (item.status == 2 ? '待发货' : (item.status == 3 ? '待收货' : (item.status == 5 ? '已完成' : '已取消')))
getStatusLabel(item.status)
}}
</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>
<text class="prodcount">
x{{ prod.prodCount }}
<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>
</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>
@ -146,60 +93,47 @@
</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>
@ -208,217 +142,202 @@
</block>
</view>
</view>
<!-- end 订单列表 -->
</template>
<script setup>
const wxs = number()
import { ref, onMounted, onReachBottom } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import http from '@/utils/http'
import number from '@/wxs/number.wxs'
const sts = ref(0)
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
if (options.sts) {
sts.value = options.sts
loadOrderData(options.sts, 1)
} else {
loadOrderData(0, 1)
}
})
//
const statuses = [
{ label: '全部', value: 0 },
{ label: '待支付', value: 1 },
{ label: '待发货', value: 2 },
{ label: '待收货', value: 3 },
{ label: '已完成', value: 5 }
]
const current = ref(1)
const pages = ref(0)
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom(() => {
if (current.value < pages.value) {
loadOrderData(sts.value, current.value + 1)
}
})
//
const sts = ref(0)
const list = ref([])
/**
* 加载订单数据
*/
const loadOrderData = (sts, currentParam) => {
uni.showLoading() //
http.request({
url: '/p/myOrder/myOrder',
method: 'GET',
data: {
current: currentParam,
size: 10,
status: sts
//
onLoad((options) => {
if (options.sts) {
sts.value = parseInt(options.sts)
}
loadOrderData(sts.value, 1)
})
.then(({ data }) => {
let listParam = []
if (data.current === 1) {
listParam = data.records
} else {
listParam = list.value
Array.prototype.push.apply(listParam, data.records)
//
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
}
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) => {
sts.value = e.currentTarget.dataset.sts
loadOrderData(sts.value, 1)
}
//
const onStsTap = (e) => {
const selectedStatus = e.currentTarget.dataset.sts
sts.value = parseInt(selectedStatus)
loadOrderData(sts.value, 1)
}
/**
* 查看物流
*/
const toDeliveryPage = (e) => {
uni.navigateTo({
url: '/pages/express-delivery/express-delivery?orderNum=' + e.currentTarget.dataset.ordernum
})
}
//
const getStatusLabel = (status) => {
return statuses.find(s => s.value === status)?.label || '未知状态'
}
/**
* 取消订单
*/
const onCancelOrder = (e) => {
const ordernum = e.currentTarget.dataset.ordernum
uni.showModal({
title: '',
content: '要取消此订单?',
confirmColor: '#3e62ad',
cancelColor: '#3e62ad',
cancelText: '否',
confirmText: '是',
//
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: '是',
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()
success(res) {
if (res.confirm) {
uni.showLoading({ mask: true })
http.request({
url: `/p/myOrder/cancel/${ordernum}`,
method: 'PUT'
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
})
}
}
}
})
}
})
}
/**
* 模拟支付直接提交成功
* @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'
})
//
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'
})
}
})
}
/**
* 查看订单详情
*/
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'
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
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()
})
}
}
}
})
}
})
}
/**
* 删除已完成||已取消的订单
* @param e
*/
const delOrderList = (e) => {
uni.showModal({
title: '',
content: '确定要删除此订单吗?',
confirmColor: '#eb2444',
//
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'
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
http.request({
url: `/p/myOrder/${ordernum}`,
method: 'DELETE'
})
.then(() => {
loadOrderData(sts.value, 1)
uni.hideLoading()
})
}
}
}
})
}
})
}
</script>
<style scoped lang="scss">
@use './orderList.scss';
@use './orderList.scss';
</style>

@ -1,50 +1,92 @@
/* 定义支付状态信息的样式 */
.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,52 +1,61 @@
<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>
@ -55,56 +64,87 @@
</template>
<script setup>
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
})
}
})
// 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);
//
}
});
})
.catch(err => {
console.error('发起支付请求失败:', err);
//
});
};
</script>
<style scoped lang="scss">
@use './pay-result.scss';
/* 使用本地的SCSS文件来定义样式 */
@use './pay-result.scss';
</style>

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

@ -1,16 +1,13 @@
<template>
<view class="container">
<view>
<block
v-for="(item, index) in prodList"
:key="index"
>
<!-- 使用v-for循环渲染商品列表 -->
<block v-for="(item, index) in prodList" :key="index">
<!-- 将每个商品项传递给production组件 -->
<production :item="item" />
</block>
<view
v-if="!prodList.length"
class="empty"
>
<!-- 如果没有商品数据则显示暂无数据的提示信息 -->
<view v-if="!prodList.length" class="empty">
暂无数据
</view>
</view>
@ -18,215 +15,241 @@
</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)
/**
* 生命周期函数--监听页面加载
*/
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
}
//
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 : ''
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: '更多宝贝'
})
// tagidID
if (options.tagid) {
tagid.value = options.tagid
}
} 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)
})
// 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 })
}
}
/**
* 页面上拉触底事件的处理函数
*/
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
//
loadProdData(options)
})
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom(() => {
//
if (current.value < pages.value) {
current.value++
loadProdData()
}
})
.then(({ data }) => {
/**
* 加载商品数据
*/
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 }) => {
let list
//
if (data.current === 1) {
list = data.records
} else {
list = prodList.value
list = list.concat(data.records)
list = prodList.value.concat(data.records)
}
//
prodList.value = list
pages.value = data.pages
//
uni.hideLoading()
})
}
/**
* 获取我的收藏商品
*/
const getCollectionProd = () => {
uni.showLoading()
http.request({
url: '/p/user/collection/prods',
method: 'GET',
data: {
current: current.value,
size: size.value
}
})
.then(({ data }) => {
}
/**
* 获取我的收藏商品
*/
const getCollectionProd = () => {
//
uni.showLoading()
// HTTP
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
list = list.concat(data.records)
list = prodList.value.concat(data.records)
}
//
prodList.value = list
pages.value = data.pages
//
uni.hideLoading()
})
}
/**
* 获取标签列表
*/
const getTagProd = () => {
uni.showLoading()
http.request({
url: '/prod/prodListByTagId',
method: 'GET',
data: {
tagId: tagid.value,
current: current.value,
size: size.value
}
})
.then(({ data }) => {
}
/**
* 获取标签商品列表
*/
const getTagProd = () => {
//
uni.showLoading()
// HTTP
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.request({
url: '/coupon/prodListByCouponId',
method: 'GET',
data: {
couponId: id,
current: current.value,
size: size.value
}
})
.then(({ data }) => {
}
/**
* 获取优惠券商品列表
*/
const getProdByCouponId = (id) => {
//
uni.showLoading()
// HTTP
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,26 +1,35 @@
.container {
.c /* 100%150rpx */
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;
@ -30,12 +39,14 @@ swiper {
font-size: 20rpx;
padding-left: 20rpx;
text-align: center;
/* 列内的图标,设置为块级元素并居中显示 */
image {
display: block;
margin: auto;
width: 40rpx;
height: 40rpx;
}
/* 在列元素左侧添加一条竖线装饰 */
&::after {
content: "";
display: block;
@ -49,11 +60,15 @@ swiper {
}
}
}
/* 产品标题,设定字体大小、颜色和右边距 */
.prod-tit {
font-size: 32rpx;
color: #333;
padding-right: 20rpx;
}
/* 销量段落,设置了背景色、行高、颜色、字体大小、上边距和右边距 */
.sales-p {
background: #fff;
line-height: 40rpx;
@ -62,29 +77,38 @@ 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;
@ -95,243 +119,280 @@ 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-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-tit {
font-size: 32rpx;
position: relative;
border-bottom: 1px solid #ddd;
padding: 20rpx;
}
text.selected {
color: #fff;
background: #e93b3d;
/* 评论区的好评率,红色字体,稍小字号 */
.cmt-good {
color: #eb2444;
font-size: 24rpx;
}
}
.cmt-item {
position: relative;
padding: 10px 0;
&::after {
content: "";
height: 0;
display: block;
border-bottom: 1px solid #ddd;
/* 评论数量,绝对定位在右上角,设置了字体大小和颜色 */
.cmt-count {
position: absolute;
left: 0;
bottom: 0;
right: -10px;
border-bottom-color: #e5e5e5;
right: 20rpx;
top: 20rpx;
font-size: 24rpx;
color: #666;
}
}
.cmt-items {
.empty {
/* 展示更多评论的箭头图标,通过旋转创建 */
.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;
color: #666;
/* 当标签被选中时,改变背景色和字体颜色 */
&.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-items .empty {
display: block;
font-size: 24rpx;
text-align: center;
color: #aaa;
margin-top: 5vh;
}
}
.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;
/* 用户信息,设置了行高、底边距、字体大小 */
.cmt-user {
line-height: 25px;
margin-bottom: 8px;
font-size: 12px;
text-align: center;
color: #333;
padding: 0px 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 40px;
display: inline-block;
/* 用户头像,圆形,设置了宽度、高度、圆角和垂直对齐方式 */
.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-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;
/* 用户信息容器使用flex布局对齐项目中心设置了宽度 */
.cmt-user-info {
display: flex;
align-items: center;
width: 400rpx;
}
/* 评论内容,设置了溢出隐藏、文本溢出处理、弹性盒子模型属性、相对定位、行高、字体大小、边距、单词断开和最大高度 */
.cmt-cnt {
-webkit-line-clamp: 20;
max-height: 500px;
}
.load-more {
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;
padding: 20px;
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;
margin-bottom: 10px;
background-color: #fff;
font-size: 12px;
/* 按钮文本,设置了高度、行高、字体大小、文本对齐、颜色、内边距、边框、圆角 */
text {
border: 1px solid #ddd;
padding: 5px 10px;
border-radius: 10px;
color: #666;
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;
}
}
}
}
. /* 线 */
.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;
@ -342,6 +403,7 @@ rich-text {
height: 98rpx;
z-index: 999;
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.05);
/* 按钮,设置了相对定位、弹性增长、内容居中、宽度、背景色、字体大小和列式布局 */
.btn {
position: relative;
display: flex;
@ -352,6 +414,7 @@ rich-text {
background-color: #fff;
font-size: 28rpx;
flex-flow: column;
/* 徽章,绝对定位的小圆圈,用于展示数字,如购物车商品数量 */
.badge {
position: absolute;
top: 20rpx;
@ -366,30 +429,37 @@ 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;
@ -402,15 +472,20 @@ 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;
@ -420,6 +495,8 @@ rich-text {
z-index: 999;
background-color: rgba(0, 0, 0, 0.3);
}
/* SKU弹出层主要内容绝对定位在屏幕底部设置了宽度、最小和最大高度、白色背景 */
.pup-sku-main {
position: absolute;
bottom: 0;
@ -428,6 +505,8 @@ rich-text {
max-height: 475px;
background-color: #fff;
}
/* SKU弹出层头部设置了相对定位、行高、字体大小、颜色、高度、内边距和背景色 */
.pup-sku-header {
position: relative;
line-height: 46px;
@ -437,6 +516,8 @@ rich-text {
padding: 0 0 10px 110px;
background-color: #fff;
}
/* SKU弹出层中的产品图片设置了绝对定位、左上偏移、圆角、宽度、高度和垂直对齐 */
.pup-sku-img {
position: absolute;
left: 10px;
@ -447,6 +528,8 @@ rich-text {
border: 0 none;
vertical-align: top;
}
/* SKU弹出层价格设置了行内块显示、高度、行高、颜色和字体大小 */
.pup-sku-price {
display: inline-block;
height: 40px;
@ -454,9 +537,13 @@ 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;
@ -468,17 +555,22 @@ 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;
@ -487,11 +579,14 @@ 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;
@ -508,23 +603,31 @@ 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;
@ -533,11 +636,13 @@ 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%;
@ -550,8 +655,11 @@ rich-text {
}
}
}
/* 数量选择器的减号和加号按钮,设置了相对定位、最大最小宽度、高度、行高、背景色、文本对齐、圆角 */
.num-wrap {
.minus {
.minus,
.plus {
position: relative;
max-width: 30px;
min-width: 30px;
@ -559,20 +667,18 @@ 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;
@ -584,6 +690,7 @@ rich-text {
height: 2px;
background-color: #ccc;
}
/* 列元素,创建加号的竖线,设置了圆角、绝对定位、顶部和左侧偏移、宽度、高度和背景色 */
.col {
border-radius: 20px;
position: absolute;
@ -596,6 +703,8 @@ rich-text {
background-color: #999;
}
}
/* SKU弹出层底部固定栏设置了固定位置、底部对齐、宽度、弹性布局、高度、z-index和阴影效果 */
.pup-sku-footer {
position: fixed;
bottom: 0;
@ -606,6 +715,7 @@ rich-text {
height: 98rpx;
z-index: 999;
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.05);
/* 按钮,设置了相对定位、弹性增长、内容居中、宽度、背景色、字体大小和列式布局 */
.btn {
position: relative;
display: flex;
@ -617,10 +727,12 @@ 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,34 +1,40 @@
.recent-news {
background: #fff;
background: #fff; /* 设置最近新闻容器的背景颜色为白色 */
.news-item {
padding: 20rpx 20rpx 0 20rpx;
position: relative;
padding: 20rpx 20rpx 0 20rpx; /* 内边距上20rpx左右各20rpx下0rpx */
position: relative; /* 使子元素可以相对于此元素进行绝对定位 */
&::after {
content: " ";
width: 100%;
height: 2rpx;
background-color: #e1e1e1;
left: 20rpx;
display: block;
position: absolute;
content: " "; /* 生成一个伪元素 */
width: 100%; /* 伪元素宽度为100% */
height: 2rpx; /* 伪元素高度为2rpx作为分隔线 */
background-color: #e1e1e1; /* 分隔线的颜色为浅灰色 */
left: 20rpx; /* 从左边开始20rpx与父元素内边距对齐 */
display: block; /* 伪元素作为块级元素显示 */
position: absolute; /* 伪元素绝对定位,相对于父元素 */
bottom: 0; /* 伪元素位于父元素底部 */
}
.news-item-title {
font-size: 28rpx;
text-align: left;
font-size: 28rpx; /* 新闻标题字体大小为28rpx */
text-align: left; /* 文字左对齐 */
}
.news-item-date {
font-size: 24rpx;
color: #999;
text-align: right;
margin-top: 10rpx;
margin-bottom: 20rpx;
font-size: 24rpx; /* 日期字体大小为24rpx */
color: #999; /* 日期颜色为浅灰色 */
text-align: right; /* 文字右对齐 */
margin-top: 10rpx; /* 上边距为10rpx */
margin-bottom: 20rpx; /* 下边距为20rpx */
}
}
.empty {
display: block;
padding-top: 200rpx;
color: #999;
font-size: 26rpx;
text-align: center;
display: block; /* 空状态提示作为块级元素显示 */
padding-top: 200rpx; /* 上内边距为200rpx用于居中显示空状态提示 */
color: #999; /* 文字颜色为浅灰色 */
font-size: 26rpx; /* 字体大小为26rpx */
text-align: center; /* 文字水平居中 */
}
}

@ -1,60 +1,62 @@
<template>
<view class="container">
<!-- 最近新闻容器 -->
<view class="recent-news">
<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>
<!-- 使用 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>
</view>
</block>
<view
v-if="!news || !news.length"
class="empty"
>
暂无数据
</view>
<!-- news 数组为空或不存在时显示的空状态提示 -->
<view v-if="!news || !news.length" class="empty"></view>
</view>
</view>
</template>
<script setup>
const news = ref([])
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
//
http.request({
url: '/shop/notice/noticeList',
method: 'GET'
})
.then(({ data }) => {
news.value = data.records
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' //
})
})
.then(({ data }) => {
// news
news.value = data.records;
})
.catch((error) => {
console.error('Failed to fetch news:', error); //
});
});
/**
* 跳转公告详情页
* @param e
*/
const toNewsDetail = (e) => {
uni.navigateTo({
url: '/pages/news-detail/news-detail?id=' + e.currentTarget.dataset.id
})
}
/**
* 跳转公告详情页
* @param e - 触发事件对象包含当前元素的数据集dataset
*/
const toNewsDetail = (e) => {
// 使 uni.navigateTo ID
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,124 +1,146 @@
/* 容器样式 */
.con {
background: #fff;
height: 100%;
margin-top: 50px;
background: #fff; /* 设置容器的背景颜色为白色 */
height: 100%; /* 容器的高度占满父元素的100% */
margin-top: 50px; /* 容器与页面顶部的距离为50像素 */
}
/* 图像样式 */
image {
display: block;
width: 150rpx;
height: 150rpx;
margin: auto;
border-radius: 50%;
width: 150rpx;
height: 150rpx;
margin-bottom: 8%;
display: block; /* 确保图像作为块级元素显示,避免与其他元素在同一行 */
width: 150rpx; /* 图像宽度为150响应式像素 */
height: 150rpx; /* 图像高度为150响应式像素 */
margin: auto; /* 使图像水平居中 */
border-radius: 50%; /* 将图像设置为圆形50%创建一个圆形) */
width: 150rpx; /* 重复定义图像宽度为150响应式像素 */
height: 150rpx; /* 重复定义图像高度为150响应式像素 */
margin-bottom: 8%; /* 图像底部外边距为父元素高度的8% */
}
/* 登录表单样式 */
.login-form {
width: 90%;
margin: 0 auto;
margin-bottom: 20%;
width: 90%; /* 表单宽度为父元素宽度的90% */
margin: 0 auto; /* 表单左右外边距自动,实现水平居中 */
margin-bottom: 20%; /* 表单底部外边距为父元素高度的20%,确保与下方内容有足够的间距 */
}
/* 授权按钮样式 */
.authorized-btn {
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;
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; /* 重复定义按钮顶部外边距 */
}
/* 返回首页按钮样式 */
.to-idx-btn {
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;
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; /* 重复定义按钮顶部外边距 */
}
/* 表单标题样式 */
.form-title {
width: 100%;
margin-bottom: 50rpx;
font-size: 32rpx;
text-align: center;
color: #00a0e9;
margin-bottom: 50rpx;
font-size: 32rpx;
width: 100%; /* 标题宽度为100%,即占满整个父元素的宽度 */
margin-bottom: 50rpx; /* 标题底部外边距为50响应式像素 */
font-size: 32rpx; /* 标题字体大小设置为32响应式像素 */
text-align: center; /* 标题文本居中对齐 */
color: #00a0e9; /* 标题文字颜色设置为蓝色 */
margin-bottom: 50rpx; /* 重复定义标题底部外边距为50响应式像素 */
font-size: 32rpx; /* 重复定义标题字体大小为32响应式像素 */
}
/* 表单项样式 */
.item {
display: block;
margin-bottom: 30rpx;
margin-bottom: 30rpx;
display: block; /* 确保每个表单项作为一个单独的块级元素显示 */
margin-bottom: 30rpx; /* 每个表单项底部外边距为30响应式像素 */
margin-bottom: 30rpx; /* 重复定义每个表单项底部外边距为30响应式像素 */
}
/* 账号输入框样式 */
.account {
display: flex;
background: #f8f8f8;
padding: 15rpx;
box-sizing: border-box;
font-size: 26rpx;
align-items: center;
display: flex; /* 使用弹性布局来排列子元素 */
background: #f8f8f8; /* 输入框背景颜色设置为浅灰色 */
padding: 15rpx; /* 输入框内边距为15响应式像素 */
box-sizing: border-box; /* 确保padding和border包含在元素的width和height之内 */
font-size: 26rpx; /* 输入框内的字体大小设置为26响应式像素 */
align-items: center; /* 子元素垂直居中对齐 */
input {
padding-left: 20rpx;
width: 75%;
padding-left: 20rpx;
padding-left: 20rpx; /* 输入框内部左侧留出20响应式像素的空间 */
width: 75%; /* 输入框宽度为父元素宽度的75% */
padding-left: 20rpx; /* 重复定义输入框内部左侧留出20响应式像素的空间 */
}
}
button {
&::after {
border: 0 !important;
}
/* 移除按钮默认边框 */
button::after {
border: 0 !important; /* 移除按钮点击时出现的默认边框,使用!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;
color: #00AAFF;
font-size: 28rpx;
font-size: 28rpx; /* 链接字体大小设置为28响应式像素 */
color: #00AAFF; /* 链接文字颜色设置为蓝色 */
font-size: 28rpx; /* 重复定义链接字体大小为28响应式像素 */
}
/* 错误提示样式 */
.error {
.error-text {
display: block;
width: 100%;
font-size: 28rpx;
color: #e43130;
text-align: left;
margin-top: 10rpx;
font-size: 28rpx;
margin-top: 10rpx;
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; /* 重复定义错误信息顶部外边距 */
.warning-icon {
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;
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; /* 重复定义警告图标内的字体大小 */
}
}
}

@ -1,77 +1,68 @@
<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>
<view
v-if="errorTips==1"
class="error-text"
>
<text class="warning-icon">
!
</text>
<!-- 错误提示信息仅当 errorTips 1 时显示 -->
<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>
<view
v-if="errorTips==2"
class="error-text"
>
<text class="warning-icon">
!
</text>
<!-- 错误提示信息仅当 errorTips 2 时显示 -->
<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>
@ -80,87 +71,114 @@
</template>
<script setup>
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
})
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 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) //
}
})
}
}
/**
* 去登陆
*/
const toLogin = () => {
uni.navigateTo({
url: '/pages/accountLogin/accountLogin'
})
}
/**
* 回到首页
*/
const toIndex = () => {
uni.switchTab({
url: '/pages/index/index'
})
}
.then(() => {
//
uni.hideLoading();
//
uni.showToast({
title: '注册成功,请登录',
icon: 'none',
duration: 1500
});
// 1.8
setTimeout(() => {
uni.navigateTo({
url: '/pages/accountLogin/accountLogin'
});
}, 1800);
})
.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'
});
};
</script>
<style lang="scss" scoped>
@import "./register.scss";
@import "./register.scss"; /* 引入外部样式文件 */
</style>

@ -1,119 +1,139 @@
/* 搜索栏样式 */
.search-bar {
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;
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; /* 设置堆叠顺序,确保搜索栏在其他内容之上 */
.search-box {
position: relative;
height: 60rpx;
background: #f7f7f7;
z-index: 999;
width: 80%;
margin-left: 70rpx;
border-radius: 50rpx;
margin: 20rpx 0 20rpx 20rpx;
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响应式像素 */
.search-img {
width: 32rpx;
height: 32rpx;
position: absolute;
left: 20rpx;
top: 14rpx;
display: block;
width: 32rpx; /* 搜索图标宽度为32响应式像素 */
height: 32rpx; /* 搜索图标高度为32响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
left: 20rpx; /* 搜索图标距离左侧20响应式像素 */
top: 14rpx; /* 搜索图标距离顶部14响应式像素 */
display: block; /* 确保图标作为块级元素显示 */
}
}
.search-hint {
font-size: 28rpx;
position: absolute;
right: 30rpx;
top: 31rpx;
color: #eb2444;
font-size: 28rpx; /* 提示文本字体大小为28响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 30rpx; /* 提示文本距离右侧30响应式像素 */
top: 31rpx; /* 提示文本距离顶部31响应式像素 */
color: #eb2444; /* 提示文本颜色为红色 */
}
}
/* 搜索输入框样式 */
.sear-input {
height: 60rpx;
border-radius: 50rpx;
border: 0;
margin: 0 30rpx 0 64rpx;
line-height: 48rpx;
vertical-align: top;
background: #f7f7f7;
font-size: 28rpx;
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响应式像素 */
}
/* 搜索结果显示区域样式 */
.search-display {
background: #fff;
padding: 20rpx;
margin-top: 100rpx;
background: #fff; /* 背景颜色为白色 */
padding: 20rpx; /* 内边距为20响应式像素 */
margin-top: 100rpx; /* 顶部外边距为100响应式像素确保与搜索栏有足够的间距 */
.title-text {
padding: 30rpx 0;
font-size: 30rpx;
color: #666;
padding: 30rpx 0; /* 上下内边距为30响应式像素 */
font-size: 30rpx; /* 标题文本字体大小为30响应式像素 */
color: #666; /* 标题文本颜色为深灰色 */
}
}
/* 热门搜索样式 */
.hot-search {
.hot-search-tags {
overflow: hidden;
font-size: 26rpx;
text-align: center;
padding-bottom: 30rpx;
overflow: hidden; /* 隐藏超出容器的内容 */
font-size: 26rpx; /* 标签字体大小为26响应式像素 */
text-align: center; /* 标签文本居中对齐 */
padding-bottom: 30rpx; /* 底部内边距为30响应式像素 */
.tags {
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;
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响应式像素 */
}
}
}
/* 历史搜索样式 */
.history-search {
.title-text.history-line {
position: relative;
border-top: 2rpx solid #e1e1e1;
position: relative; /* 相对定位,用于内部元素的绝对定位 */
border-top: 2rpx solid #e1e1e1; /* 顶部边框为2响应式像素宽颜色为浅灰色 */
}
.his-search-tags {
overflow: hidden;
font-size: 26rpx;
text-align: center;
display: inline-block;
overflow: hidden; /* 隐藏超出容器的内容 */
font-size: 26rpx; /* 标签字体大小为26响应式像素 */
text-align: center; /* 标签文本居中对齐 */
display: inline-block; /* 作为行内块级元素显示 */
.tags {
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;
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响应式像素 */
}
}
}
/* 清除历史记录按钮样式 */
.clear-history {
image {
width: 32rpx;
height: 32rpx;
position: absolute;
right: 10rpx;
top: 30rpx;
width: 32rpx; /* 图标宽度为32响应式像素 */
height: 32rpx; /* 图标高度为32响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 10rpx; /* 图标距离右侧10响应式像素 */
top: 30rpx; /* 图标距离顶部30响应式像素 */
}
}
/* 搜索结果为空时的提示样式 */
.search-tit-empty {
display: block;
margin: 0 auto;
text-align: center;
width: 100%;
font-size: 24rpx;
color: #aaa;
display: block; /* 作为块级元素显示 */
margin: 0 auto; /* 水平居中 */
text-align: center; /* 文本居中对齐 */
width: 100%; /* 宽度为100%,即占满整个父元素 */
font-size: 24rpx; /* 提示文本字体大小为24响应式像素 */
color: #aaa; /* 提示文本颜色为浅灰色 */
}

@ -2,84 +2,86 @@
<view class="container">
<!-- 搜索框 -->
<view class="search-bar">
<!-- 搜索输入框 -->
<view class="search-box">
<input
placeholder="输入关键字搜索"
class="sear-input"
confirm-type="search"
:value="prodName"
@confirm="toSearchProdPage"
@input="getSearchContent"
>
<image
src="@/static/images/icon/search.png"
class="search-img"
<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 -->
/>
</view>
<text
class="search-hint"
@tap="goBackIndex"
<!-- 取消按钮 -->
<text class="search-hint" <!-- 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"
<!-- 如果有热门搜索数据则显示标签 -->
<view v-if="hotSearchList && hotSearchList.length" <!-- hotSearchList -->
class="hot-search-tags"
>
<block
v-for="(item, index) in hotSearchList"
:key="index"
v-for="(item, index) in hotSearchList" <!-- 遍历热门搜索列表 -->
:key="index" <!-- 使用索引作为唯一标识 -->
>
<text
class="tags"
:data-name="item.content"
@tap="onHistSearch"
class="tags" <!-- 应用样式类 tags -->
:data-name="item.content" <!-- 绑定 data-name 属性用于传递搜索关键词 -->
@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"
<view v-if="recentSearch && recentSearch.length" <!-- recentSearch -->
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"
:data-name="item"
@tap="onHistSearch"
class="tags" <!-- 应用样式类 tags -->
:data-name="item" <!-- 绑定 data-name 属性用于传递搜索关键词 -->
@tap="onHistSearch" <!-- 点击标签时触发搜索 -->
>
{{ item }}
{{ item }} <!-- 显示历史记录文本 -->
</text>
</view>
</block>
@ -89,95 +91,114 @@
</template>
<script setup>
const hotSearchList = ref([])
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
http.request({
url: '/search/hotSearchByShopId',
method: 'GET',
data: {
number: 10,
shopId: 1,
sort: 1
}
})
.then(({ data }) => {
hotSearchList.value = data
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 //
}
})
//
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()
.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)}`
});
}
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()
}
};
/**
* 清空搜索历史
*/
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();
};
</script>
<style scoped lang="scss">
@use './search-page.scss';
@use './search-page.scss'; /* 引入外部样式文件 */
</style>

@ -1,184 +1,218 @@
/* 容器样式 */
.container {
background: #f4f4f4;
background: #f4f4f4; /* 设置背景颜色为浅灰色 */
.empty {
text-align: center;
color: #999;
font-size: 26rpx;
text-align: center; /* 文本居中对齐 */
color: #999; /* 文本颜色为灰色 */
font-size: 26rpx; /* 字体大小为26响应式像素 */
}
.empty.empty-top {
margin-top: 300rpx;
margin-top: 300rpx; /* 顶部外边距为300响应式像素用于在页面顶部显示空状态时有足够的间距 */
}
}
/* 固定定位的盒子样式 */
.fixed-box {
position: fixed;
width: 100%;
top: 0;
z-index: 999;
background: #fff;
position: fixed; /* 固定定位,使盒子始终位于页面顶部 */
width: 100%; /* 宽度占满整个父元素 */
top: 0; /* 距离页面顶部0像素 */
z-index: 999; /* 设置较高的堆叠顺序,确保盒子在其他内容之上 */
background: #fff; /* 背景颜色为白色 */
.tabs {
width: 100%;
height: 80rpx;
line-height: 80rpx;
padding: 10rpx 0;
z-index: 999;
background: #fff;
width: 100%; /* 宽度占满整个父元素 */
height: 80rpx; /* 高度为80响应式像素 */
line-height: 80rpx; /* 行高设置为80响应式像素确保文本垂直居中 */
padding: 10rpx 0; /* 上下内边距为10响应式像素 */
z-index: 999; /* 设置较高的堆叠顺序,确保标签栏在其他内容之上 */
background: #fff; /* 背景颜色为白色 */
&::after {
content: '';
background-color: #e1e1e1;
left: 0;
height: 1px;
transform-origin: 50% 100% 0;
bottom: 0;
position: absolute;
display: block;
width: 100%;
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%,即占满整个父元素 */
}
.tab-item {
display: inline-block;
width: 33.33%;
text-align: center;
font-size: 28rpx;
display: inline-block; /* 标签项作为行内块级元素显示 */
width: 33.33%; /* 每个标签项占据父元素宽度的33.33%,实现三等分 */
text-align: center; /* 文本居中对齐 */
font-size: 28rpx; /* 字体大小为28响应式像素 */
}
.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;
box-sizing: border-box;
margin: 30rpx 0;
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响应式像素 */
.search-box {
position: relative;
height: 60rpx;
background: #f7f7f7;
z-index: 999;
width: 80%;
border-radius: 50rpx;
margin-right: 30rpx;
flex: 1;
position: relative; /* 相对定位,用于内部元素的绝对定位 */
height: 60rpx; /* 搜索框高度为60响应式像素 */
background: #f7f7f7; /* 搜索框背景颜色为浅灰色 */
z-index: 999; /* 设置较高的堆叠顺序,确保搜索框在其他内容之上 */
width: 80%; /* 搜索框宽度为父元素宽度的80% */
border-radius: 50rpx; /* 圆角边框半径为50响应式像素 */
margin-right: 30rpx; /* 右侧外边距为30响应式像素 */
flex: 1; /* 搜索框占据剩余空间 */
.search-img {
width: 32rpx;
height: 32rpx;
position: absolute;
left: 20rpx;
top: 14rpx;
display: block;
width: 32rpx; /* 搜索图标宽度为32响应式像素 */
height: 32rpx; /* 搜索图标高度为32响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
left: 20rpx; /* 搜索图标距离左侧20响应式像素 */
top: 14rpx; /* 搜索图标距离顶部14响应式像素 */
display: block; /* 确保图标作为块级元素显示 */
}
}
.search-hint {
font-size: 28rpx;
position: absolute;
right: 30rpx;
top: 31rpx;
color: #eb2444;
font-size: 28rpx; /* 提示文本字体大小为28响应式像素 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
right: 30rpx; /* 提示文本距离右侧30响应式像素 */
top: 31rpx; /* 提示文本距离顶部31响应式像素 */
color: #eb2444; /* 提示文本颜色为红色 */
}
.search-list-img {
width: 40rpx;
height: 40rpx;
font-size: 0;
width: 40rpx; /* 图标宽度为40响应式像素 */
height: 40rpx; /* 图标高度为40响应式像素 */
font-size: 0; /* 移除默认字体大小,防止影响图标显示 */
image {
width: 100%;
height: 100%;
width: 100%; /* 图片宽度为100%,即占满父元素 */
height: 100%; /* 图片高度为100%,即占满父元素 */
}
}
}
/* 搜索输入框样式 */
.sear-input {
height: 60rpx;
border-radius: 50rpx;
border: 0;
margin: 0 30rpx 0 64rpx;
line-height: 48rpx;
vertical-align: top;
background: #f7f7f7;
font-size: 28rpx;
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响应式像素 */
}
/* 商品展示区域样式 */
.prod-show {
background: #fff;
background: #fff; /* 背景颜色为白色 */
.prod-items {
float: left;
background: #fff;
padding-bottom: 20rpx;
box-sizing: border-box;
float: left; /* 商品项左浮动,实现水平排列 */
background: #fff; /* 商品项背景颜色为白色 */
padding-bottom: 20rpx; /* 底部内边距为20响应式像素 */
box-sizing: border-box; /* 确保padding和border包含在元素的width和height之内 */
}
}
.prod-items {
margin: 0 20rpx;
margin: 0 20rpx; /* 左右外边距为20响应式像素 */
}
/* 热销商品容器样式 */
.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;
min-height: calc(100vh - 150rpx);
padding-top: 150rpx; /* 顶部内边距为150响应式像素确保与固定头部有足够的间距 */
min-height: calc(100vh - 150rpx); /* 最小高度为视口高度减去150响应式像素 */
.cont-item {
padding: 0 20rpx 20rpx 20rpx;
padding: 0 20rpx 20rpx 20rpx; /* 内边距为上下20响应式像素左右20响应式像素 */
.show-item {
.more-prod-pic {
text-align: center;
width: 170rpx;
height: 170rpx;
font-size: 0;
text-align: center; /* 图片文本居中对齐 */
width: 170rpx; /* 图片容器宽度为170响应式像素 */
height: 170rpx; /* 图片容器高度为170响应式像素 */
font-size: 0; /* 移除默认字体大小,防止影响图片显示 */
.more-pic {
width: 100%;
height: 100%;
vertical-align: middle;
width: 100%; /* 图片宽度为100%,即占满父元素 */
height: 100%; /* 图片高度为100%,即占满父元素 */
vertical-align: middle; /* 图片垂直居中对齐 */
}
}
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);
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); /* 添加轻微的阴影效果,增强立体感 */
.prod-text-right {
margin-left: 20rpx;
width: 75%;
margin-left: 20rpx; /* 左侧外边距为20响应式像素 */
width: 75%; /* 占据父元素宽度的75% */
.cate-prod-info {
font-size: 22rpx;
color: #999;
margin: 10rpx 0 20rpx 0;
font-size: 22rpx; /* 字体大小为22响应式像素 */
color: #999; /* 文本颜色为灰色 */
margin: 10rpx 0 20rpx 0; /* 上下外边距分别为10和20响应式像素 */
}
.go-to-buy {
font-size: 26rpx;
background: #eb2444;
color: #fff;
border-radius: 50rpx;
width: 150rpx;
text-align: center;
padding: 8rpx 3rpx;
position: absolute;
right: 20rpx;
bottom: 20rpx;
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响应式像素 */
}
.prod-text.more {
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;
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; /* 文本颜色为黑色 */
}
.prod-price.more {
font-size: 28rpx;
color: #eb2444;
font-family: arial;
font-size: 28rpx; /* 字体大小为28响应式像素 */
color: #eb2444; /* 文本颜色为红色 */
font-family: arial; /* 字体系列为Arial */
}
}
}

@ -2,54 +2,52 @@
<view class="container">
<!-- 搜索框 -->
<view class="fixed-box">
<!-- 固定定位的盒子通常用于页面顶部导航栏或标签栏 -->
<view class="search-bar">
<!-- 搜索栏容器 -->
<view class="search-box">
<input
placeholder="输入关键字搜索"
class="sear-input"
:value="prodName"
confirm-type="search"
@input="getSearchContent"
@confirm="toSearchConfirm"
>
<image
src="@/static/images/icon/search.png"
class="search-img"
<!-- 搜索输入框容器 -->
<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 -->
/>
</view>
<view
class="search-list-img"
@tap="changeShowType"
<view class="search-list-img" <!-- -->
@tap="changeShowType" <!-- 点击切换显示类型 -->
>
<image
v-if="showType==1"
src="@/static/images/icon/search-col.png"
v-if="showType == 1" <!-- showType 1 时显示此图标 -->
src="@/static/images/icon/search-col.png" <!-- 图标图片路径 -->
/>
<image
v-if="showType==2"
src="@/static/images/icon/search-col2.png"
v-if="showType == 2" <!-- showType 2 时显示此图标 -->
src="@/static/images/icon/search-col2.png" <!-- 图标图片路径 -->
/>
</view>
</view>
<view class="tabs">
<text
:class="'tab-item complete ' + (sts==0?'on':'')"
data-sts="0"
@tap="onStsTap"
<!-- 标签栏容器 -->
<text :class="'tab-item complete ' + (sts == 0 ? 'on' : '')" <!-- sts 'on' -->
data-sts="0" <!-- 数据属性用于标识当前标签 -->
@tap="onStsTap" <!-- 点击标签时触发排序方式切换 -->
>
综合
</text>
<text
:class="'tab-item ' + (sts==1?'on':'')"
data-sts="1"
@tap="onStsTap"
<text :class="'tab-item ' + (sts == 1 ? 'on' : '')" <!-- sts 'on' -->
data-sts="1" <!-- 数据属性用于标识当前标签 -->
@tap="onStsTap" <!-- 点击标签时触发排序方式切换 -->
>
销量
</text>
<text
:class="'tab-item ' + (sts==2?'on':'')"
data-sts="2"
@tap="onStsTap"
<text :class="'tab-item ' + (sts == 2 ? 'on' : '')" <!-- sts 'on' -->
data-sts="2" <!-- 数据属性用于标识当前标签 -->
@tap="onStsTap" <!-- 点击标签时触发排序方式切换 -->
>
价格
</text>
@ -58,61 +56,54 @@
<!-- 商品列表 -->
<view class="prod-list">
<!-- 商品列表容器 -->
<!-- 横向列表 -->
<view
v-if="showType==1"
<view v-if="showType == 1" <!-- 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"
<view v-if="showType == 2" <!-- 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"
@tap="toProdPage"
class="show-item" <!-- 商品项容器 -->
:data-prodid="item.prodId" <!-- 传递商品ID -->
@tap="toProdPage" <!-- 点击商品项时跳转到商品详情页 -->
>
<view class="more-prod-pic">
<view class="more-prod-pic"> <!-- 商品图片容器 -->
<image
:src="item.pic"
class="more-pic"
:src="item.pic" <!-- 动态绑定图片路径 -->
class="more-pic" <!-- 应用样式类 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>
@ -120,9 +111,8 @@
</view>
<!-- 空占位 -->
<view
v-if="!searchProdList.length"
:class="['empty',showType==1? 'empty-top':'']"
<view v-if="!searchProdList.length" <!-- -->
:class="['empty', showType == 1 ? 'empty-top' : '']" <!-- 动态绑定类名根据 showType 决定是否添加 'empty-top' -->
>
暂无结果
</view>
@ -131,93 +121,128 @@
</template>
<script setup>
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
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;
}
})
.then(({ data }) => {
searchProdList.value = data.records
};
/**
* 输入商品获取数据
* @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 //
}
})
}
/**
* 当前搜索页二次搜索商品
*/
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()
.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);
}
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
})
}
//
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}`
});
};
</script>
<style scoped lang="scss">
@use './search-prod-show.scss';
@use './search-prod-show.scss'; /* 引入外部样式文件 */
</style>

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

@ -1,71 +1,65 @@
<template>
<view class="Mall4j container">
<!-- 顶部子分类tab -->
<scroll-view
scroll-x="true"
class="category-tit"
:scroll-into-view="intoView"
:scroll-with-animation="true"
<scroll-view scroll-x="true" <!-- -->
class="category-tit" <!-- 应用样式类 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"
:class="'category-item ' + (item.categoryId==categoryId? 'on':'')"
:data-id="item.categoryId"
@tap="onSubCategoryTap"
:id="'sw' + item.categoryId" <!-- 动态设置每个分类项的ID -->
:class="'category-item ' + (item.categoryId == categoryId ? 'on' : '')" <!-- 动态绑定类名当前选中的分类项添加 'on' -->
:data-id="item.categoryId" <!-- 传递分类ID -->
@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"
@tap="toProdPage"
class="prod-items" <!-- 商品项容器 -->
:data-prodid="prod.prodId" <!-- 传递商品ID -->
@tap="toProdPage" <!-- 点击商品项时跳转到商品详情页 -->
>
<view class="hot-imagecont">
<view class="hot-imagecont"> <!-- 商品图片容器 -->
<image
:src="prod.pic"
class="hotsaleimg"
:src="prod.pic" <!-- 动态绑定商品图片路径 -->
class="hotsaleimg" <!-- 应用样式类 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"
<view v-else <!-- -->
class="empty-wrap" <!-- 应用样式类 empty-wrap -->
>
暂无商品数据~
</view>
@ -74,104 +68,147 @@
</template>
<script setup>
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
}
})
.then(({ data }) => {
subCategoryList.value = data
nextTick(() => {
intoView.value = 'sw' + categoryId.value
})
})
}
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
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();
}
})
.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 intoView = ref('');
//
const subCategoryList = ref([]);
/**
* 获取顶栏子分类数据
*/
const getSubCategory = () => {
//
http.request({
url: '/category/categoryInfo', // API
method: 'GET', // GET
data: {
parentId: parentId.value // ID
}
})
}
/**
* 切换顶部分类
*/
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
.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 }) => {
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}`
});
}
};
</script>
<style lang="scss" scoped>
@import "./sub-category.scss";
@import "./sub-category.scss"; /* 引入外部样式文件 */
</style>

@ -1,429 +1,489 @@
/* 容器样式 */
.container {
background: #f4f4f4;
background: #f4f4f4; /* 设置背景颜色为浅灰色 */
}
/* 提交订单区域样式 */
.submit-order {
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%;
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%; /* 高度占满父元素 */
}
}
.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;
.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; /* 作为行内块级元素显示 */
}
}
.addr {
font-size: 26rpx;
line-height: 36rpx;
color: #999;
width: 90%;
padding-bottom: 20rpx;
margin-top: 15rpx;
word-wrap: break-word;
.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地址换行到下一行 */
}
.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 { /* 箭头样式 */
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.empty {
top: 39rpx;
.arrow.empty { /* 空状态下的箭头样式 */
top: 39rpx; /* 距离顶部39响应式像素 */
}
}
}
.delivery-addr {
.addr-bg {
.add-addr {
.plus-sign {
color: #eb2444;
border: 2rpx solid #eb2444;
padding: 0rpx 6rpx;
margin-right: 10rpx;
.delivery-addr { /* 配送地址样式(重复定义,可能用于覆盖或扩展) */
.addr-bg { /* 地址背景样式 */
.add-addr { /* 添加地址按钮样式 */
.plus-sign { /* 加号样式 */
color: #eb2444; /* 文本颜色为红色 */
border: 2rpx solid #eb2444; /* 边框为2响应式像素宽的红色实线 */
padding: 0rpx 6rpx; /* 内边距为上下0响应式像素左右6响应式像素 */
margin-right: 10rpx; /* 右侧外边距为10响应式像素 */
}
font-size: 28rpx;
color: #666;
display: flex;
align-items: center;
padding: 30rpx 0;
font-size: 28rpx; /* 文字大小为28响应式像素 */
color: #666; /* 文本颜色为深灰色 */
display: flex; /* 使用Flex布局 */
align-items: center; /* 垂直居中对齐 */
padding: 30rpx 0; /* 上下内边距为30响应式像素 */
}
padding: 0 30rpx;
padding: 0 30rpx; /* 左右内边距为30响应式像素 */
}
.addr-bg.whole {
padding: 0 39rpx 0 77rpx;
.addr-bg.whole { /* 整个地址背景样式 */
padding: 0 39rpx 0 77rpx; /* 左右内边距分别为39响应式像素和77响应式像素 */
}
}
.addr-bg {
.add-addr {
.plus-sign-img {
width: 32rpx;
height: 32rpx;
font-size: 0;
margin-right: 10rpx;
image {
width: 100%;
height: 100%;
.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%; /* 高度占满父元素 */
}
}
}
}
.prod-item {
background-color: #fff;
margin-top: 15rpx;
font-size: 28rpx;
.item-cont {
.prod-pic {
image {
width: 180rpx;
height: 180rpx;
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%; /* 高度占满父元素 */
}
font-size: 0;
display: block;
width: 160rpx;
height: 160rpx;
overflow: hidden;
background: #fff;
margin-right: 16rpx;
font-size: 0; /* 移除默认字体大小,防止影响图片显示 */
display: block; /* 作为块级元素显示 */
width: 160rpx; /* 宽度为160响应式像素 */
height: 160rpx; /* 高度为160响应式像素 */
overflow: hidden; /* 隐藏超出容器的内容 */
background: #fff; /* 背景颜色为白色 */
margin-right: 16rpx; /* 右侧外边距为16响应式像素 */
}
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;
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; /* 强制文本在任意字符处换行 */
}
.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;
.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; /* 强制文本在任意字符处换行 */
}
}
}
.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;
.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; /* 背景颜色为浅灰色 */
}
.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;
font-size: 28rpx;
.prodprice {
display: inline-block;
color: #333;
.total-num { /* 总数量样式 */
text-align: right; /* 文本右对齐 */
padding: 20rpx 30rpx; /* 内边距为上下20响应式像素左右30响应式像素 */
font-size: 28rpx; /* 文字大小为28响应式像素 */
.prodprice { /* 商品价格样式 */
display: inline-block; /* 作为行内块级元素显示 */
color: #333; /* 文本颜色为深灰色 */
}
.prodcount {
margin-right: 20rpx;
.prodcount { /* 商品数量样式 */
margin-right: 20rpx; /* 右侧外边距为20响应式像素 */
}
}
.price-nums {
.prodprice {
position: absolute;
bottom: 0;
.price-nums { /* 价格和数量样式 */
.prodprice { /* 商品价格样式 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
bottom: 0; /* 距离底部0响应式像素 */
}
.prodcount {
position: absolute;
bottom: 5rpx;
right: 0;
color: #999;
font-family: verdana;
.prodcount { /* 商品数量样式 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
bottom: 5rpx; /* 距离底部5响应式像素 */
right: 0; /* 距离右侧0响应式像素 */
color: #999; /* 文本颜色为灰色 */
font-family: verdana; /* 字体系列为Verdana */
}
}
}
.order-state {
display: flex;
align-items: center;
.order-state { /* 订单状态样式 */
display: flex; /* 使用Flex布局 */
align-items: center; /* 垂直居中对齐 */
}
.order-msg {
background: #fff;
margin-top: 15rpx;
padding: 0 30rpx;
font-size: 28rpx;
.msg-item {
border-top: 2rpx solid #f1f1f1;
&:first-child {
border: 0;
.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; /* 移除边框 */
}
.item {
position: relative;
display: flex;
padding: 16rpx 0;
align-items: center;
.item-tit {
line-height: 48rpx;
.item { /* 消息项内容样式 */
position: relative; /* 相对定位,用于内部元素的绝对定位 */
display: flex; /* 使用Flex布局 */
padding: 16rpx 0; /* 上下内边距为16响应式像素 */
align-items: center; /* 垂直居中对齐 */
.item-tit { /* 消息标题样式 */
line-height: 48rpx; /* 行高设置为48响应式像素 */
}
.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 { /* 消息文本样式 */
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.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;
line-height: 28rpx;
color: #999;
.coupon-btn { /* 优惠券按钮样式 */
display: block; /* 作为块级元素显示 */
margin: 0 30rpx; /* 左右外边距为30响应式像素 */
line-height: 28rpx; /* 行高设置为28响应式像素 */
color: #999; /* 文本颜色为灰色 */
}
.arrow {
width: 15rpx;
height: 15rpx;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
position: absolute;
right: 0rpx;
.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响应式像素 */
}
}
.item.payment {
border-top: 2rpx solid #f1f1f1;
color: #eb2444;
.item.payment { /* 支付项样式 */
border-top: 2rpx solid #f1f1f1; /* 顶部边框为2响应式像素宽的浅灰色实线 */
color: #eb2444; /* 文本颜色为红色 */
}
.item.coupon {
border-bottom: 2rpx solid #e1e1e1;
.item.coupon { /* 优惠券项样式 */
border-bottom: 2rpx solid #e1e1e1; /* 底部边框为2响应式像素宽的浅灰色实线 */
}
}
}
.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;
.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响应式像素 */
}
}
}
.footer-box {
padding: 0 10rpx;
width: 200rpx;
background: #eb2444;
text-align: center;
line-height: 100rpx;
color: #fff;
.footer-box { /* 页脚按钮样式 */
padding: 0 10rpx; /* 左右内边距为10响应式像素 */
width: 200rpx; /* 宽度为200响应式像素 */
background: #eb2444; /* 背景颜色为红色 */
text-align: center; /* 文本居中对齐 */
line-height: 100rpx; /* 行高设置为100响应式像素 */
color: #fff; /* 文本颜色为白色 */
}
}
.clearfix {
&:after {
content: " ";
display: table;
clear: both;
.clearfix { /* 清除浮动样式 */
&:after { /* 生成伪元素清除浮动 */
content: " "; /* 生成空内容 */
display: table; /* 作为表格元素显示 */
clear: both; /* 清除浮动 */
}
}
.popup-hide {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 999;
background-color: rgba(0, 0, 0, 0.3);
.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-box {
position: absolute;
bottom: 0;
width: 100%;
height: 80%;
overflow: hidden;
background-color: #fff;
.popup-box { /* 弹出层盒子样式 */
position: absolute; /* 绝对定位,相对于最近的相对定位祖先元素 */
bottom: 0; /* 距离页面底部0像素 */
width: 100%; /* 宽度占满整个屏幕 */
height: 80%; /* 高度为屏幕高度的80% */
overflow: hidden; /* 隐藏超出容器的内容 */
background-color: #fff; /* 背景颜色为白色 */
}
.popup-tit {
position: relative;
height: 46px;
line-height: 46px;
padding-left: 10px;
font-size: 16px;
color: #333;
font-weight: bold;
.popup-tit { /* 弹出层标题样式 */
position: relative; /* 相对定位,用于内部元素的绝对定位 */
height: 46px; /* 高度为46像素 */
line-height: 46px; /* 行高设置为46像素 */
padding-left: 10px; /* 左侧内边距为10像素 */
font-size: 16px; /* 文字大小为16像素 */
color: #333; /* 文本颜色为深灰色 */
font-weight: bold; /* 文本加粗 */
}
.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";
.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字符表示关闭符号 */
}
}
.coupon-tabs {
display: flex;
font-size: 14px;
justify-content: space-around;
border-bottom: 1px solid #f2f2f2;
.coupon-tabs { /* 优惠券标签样式 */
display: flex; /* 使用Flex布局 */
font-size: 14px; /* 文字大小为14像素 */
justify-content: space-around; /* 水平均匀分布 */
border-bottom: 1px solid #f2f2f2; /* 底部边框为1像素宽的浅灰色实线 */
}
.coupon-tab {
padding: 10px 0;
.coupon-tab { /* 优惠券标签项样式 */
padding: 10px 0; /* 上下内边距为10像素 */
}
.coupon-tab.on {
border-bottom: 2px solid #eb2444;
font-weight: 600;
.coupon-tab.on { /* 选中的优惠券标签项样式 */
border-bottom: 2px solid #eb2444; /* 底部边框为2像素宽的红色实线 */
font-weight: 600; /* 文本加粗 */
}
.popup-cnt {
height: calc(100% - 88px);
overflow: auto;
padding: 0 10px;
background: #f4f4f4;
.popup-cnt { /* 弹出层内容样式 */
height: calc(100% - 88px); /* 高度为弹出层盒子高度减去88像素 */
overflow: auto; /* 允许滚动 */
padding: 0 10px; /* 左右内边距为10像素 */
background: #f4f4f4; /* 背景颜色为浅灰色 */
}
.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;
.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; /* 添加轻微的阴影效果 */
}
}
.botm-empty {
height: 60px;
.botm-empty { /* 底部空白样式 */
height: 60px; /* 高度为60像素 */
}
checkbox {
.wx-checkbox-input {
border-radius: 50%;
width: 35rpx;
height: 35rpx;
checkbox { /* 复选框样式 */
.wx-checkbox-input { /* 复选框输入框样式 */
border-radius: 50%; /* 圆形边框 */
width: 35rpx; /* 宽度为35响应式像素 */
height: 35rpx; /* 高度为35响应式像素 */
}
.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);
.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浏览器兼容 */
}
}
}

@ -3,100 +3,82 @@
<view class="container">
<view class="submit-order">
<!-- 收货地址 -->
<view
class="delivery-addr "
@tap="toAddrListPage"
<view class="delivery-addr"
@tap="toAddrListPage" <!-- 点击时跳转到收货地址列表页 -->
>
<!-- 如果没有选择收货地址 -->
<view
v-if="!userAddr"
class="addr-bg "
v-if="!userAddr" <!-- 条件渲染只有当 userAddr 为空时显示 -->
class="addr-bg"
>
<view class="add-addr">
<view class="plus-sign-img">
<image src="@/static/images/icon/plus-sign.png" />
<image src="@/static/images/icon/plus-sign.png" /> <!-- 加号图标 -->
</view>
<text>新增收货地址</text>
<text>新增收货地址</text> <!-- 提示文本 -->
</view>
<view class="arrow empty" />
<view class="arrow empty" /> <!-- 空箭头图标 -->
</view>
<!-- 如果已经选择了收货地址 -->
<view
v-if="userAddr"
v-if="userAddr" <!-- 条件渲染只有当 userAddr 不为空时显示 -->
class="addr-bg whole"
>
<view class="addr-icon">
<image src="@/static/images/icon/addr.png" />
<image src="@/static/images/icon/addr.png" /> <!-- 地址图标 -->
</view>
<view class="user-info">
<text class="item">
{{ userAddr.receiver }}
</text>
<text class="item">
{{ userAddr.mobile }}
</text>
<text class="item">{{ userAddr.receiver }}</text> <!-- 收货人姓名 -->
<text class="item">{{ userAddr.mobile }}</text> <!-- 收货人电话 -->
</view>
<view class="addr">
{{ userAddr.province }}{{ userAddr.city }}{{ userAddr.area }}{{ userAddr.addr }}
{{ userAddr.province }}{{ userAddr.city }}{{ userAddr.area }}{{ userAddr.addr }} <!-- 详细地址 -->
</view>
<view class="arrow" />
<view class="arrow" /> <!-- 箭头图标 -->
</view>
</view>
<!-- 商品详情 -->
<view class="prod-item">
<block
v-for="(item, index) in orderItems"
:key="index"
<block v-for="(item, index) in orderItems" <!-- -->
:key="index" <!-- 使用索引作为唯一标识 -->
>
<view
class="item-cont"
:data-ordernum="item.primaryOrderNo"
@tap="toOrderDetailPage"
:data-ordernum="item.primaryOrderNo" <!-- 传递订单编号 -->
@tap="toOrderDetailPage" <!-- 点击时跳转到订单详情页 -->
>
<view class="prod-pic">
<image :src="item.pic" />
<image :src="item.pic" /> <!-- 动态绑定商品图片路径 -->
</view>
<view class="prod-info">
<view class="prodname">
{{ item.prodName }}
{{ item.prodName }} <!-- 显示商品名称 -->
</view>
<view class="prod-info-cont">
{{ item.skuName }}
{{ item.skuName }} <!-- 显示商品规格信息 -->
</view>
<view class="price-nums">
<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>
<text class="prodcount">
x{{ item.prodCount }}
<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>
<text class="prodcount">x{{ item.prodCount }}</text> <!-- 显示商品数量 -->
</view>
</view>
</view>
</block>
<!-- 商品总数和合计金额 -->
<view class="total-num">
<text class="prodcount">
{{ totalCount }}件商品
</text>
<text class="prodcount">{{ totalCount }}件商品</text> <!-- 显示商品总数 -->
<view class="prodprice">
合计
<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>
@ -104,96 +86,58 @@
<!-- 订单详情 -->
<view class="order-msg">
<view class="msg-item">
<view
class="item coupon"
@tap="showCouponPopup"
<!-- 优惠券选择 -->
<view class="item coupon"
@tap="showCouponPopup" <!-- 点击时显示优惠券弹窗 -->
>
<text class="item-tit">
优惠券
</text>
<text
v-if="!coupons.canUseCoupons"
class="item-txt"
>
暂无可用
</text>
<text class="coupon-btn">
{{ coupons.totalLength ? coupons.totalLength : 0 }}
</text>
<text class="arrow" />
<text class="item-tit">优惠券</text>
<text v-if="!coupons.canUseCoupons" class="item-txt"></text> <!-- -->
<text class="coupon-btn">{{ coupons.totalLength ? coupons.totalLength : 0 }}</text> <!-- 显示优惠券总数 -->
<text class="arrow" /> <!-- 箭头图标 -->
</view>
<!-- 买家留言 -->
<view class="item">
<text>买家留言</text>
<input
v-model="remarks"
placeholder="给卖家留言"
>
<input v-model="remarks" <!-- -->
placeholder="给卖家留言" <!-- 占位符提示 -->
/>
</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(shopReduce)[0] }}
</text>
<text class="small-num">
.{{ wxs.parsePrice(shopReduce)[1] }}
</text>
<text class="symbol">-</text> <!-- 负号和人民币符号 -->
<text class="big-num">{{ wxs.parsePrice(shopReduce)[0] }}</text> <!-- 显示整数部分优惠金额 -->
<text class="small-num">.{{ wxs.parsePrice(shopReduce)[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>
@ -206,21 +150,14 @@
<view class="item-txt">
合计
<view class="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
class="footer-box"
@tap="toPay"
<view class="footer-box"
@tap="toPay" <!-- 点击时提交订单并支付 -->
>
提交订单
</view>
@ -228,69 +165,69 @@
</view>
<!-- 选择优惠券弹窗 -->
<view
v-if="popupShow"
<view v-if="popupShow" <!-- popupShow true -->
class="popup-hide"
>
<view class="popup-box">
<view class="popup-tit">
<text>优惠券</text>
<text>优惠券</text> <!-- 弹窗标题 -->
<text
class="close"
@tap="closePopup"
@tap="closePopup" <!-- 点击时关闭弹窗 -->
/>
</view>
<!-- 优惠券选项卡 -->
<view class="coupon-tabs">
<view
:class="'coupon-tab ' + (couponSts==1?'on':'')"
:class="'coupon-tab ' + (couponSts == 1 ? 'on' : '')" <!-- 动态绑定类名选中时添加 'on' -->
data-sts="1"
@tap="changeCouponSts"
@tap="changeCouponSts" <!-- 点击时切换优惠券状态 -->
>
可用优惠券({{ coupons.canUseCoupons.length ? coupons.canUseCoupons.length : 0 }})
可用优惠券({{ coupons.canUseCoupons.length ? coupons.canUseCoupons.length : 0 }}) <!-- 显示可用优惠券数量 -->
</view>
<view
:class="'coupon-tab ' + (couponSts==2?'on':'')"
:class="'coupon-tab ' + (couponSts == 2 ? 'on' : '')" <!-- 动态绑定类名选中时添加 'on' -->
data-sts="2"
@tap="changeCouponSts"
@tap="changeCouponSts" <!-- 点击时切换优惠券状态 -->
>
不可用优惠券({{ coupons.unCanUseCoupons.length ? coupons.unCanUseCoupons.length : 0 }})
不可用优惠券({{ coupons.unCanUseCoupons.length ? coupons.unCanUseCoupons.length : 0 }}) <!-- 显示不可用优惠券数量 -->
</view>
</view>
<!-- 优惠券列表 -->
<view class="popup-cnt">
<block v-if="couponSts == 1">
<block v-if="couponSts == 1"> <!-- couponSts 1 -->
<view
v-for="(item, index) in coupons.canUseCoupons"
:key="index"
v-for="(item, index) in coupons.canUseCoupons" <!-- 遍历可用优惠券列表动态生成每个优惠券项 -->
:key="index" <!-- 使用索引作为唯一标识 -->
>
<coupon
:item="item"
order="true"
can-use="true"
@check-coupon="checkCoupon"
:item="item" <!-- 传递优惠券数据 -->
order="true" <!-- 标记为订单页面使用 -->
can-use="true" <!-- 标记为可用优惠券 -->
@check-coupon="checkCoupon" <!-- 点击时触发选择优惠券的方法 -->
/>
</view>
</block>
<block v-if="couponSts == 2">
<block v-if="couponSts == 2"> <!-- couponSts 2 -->
<view
v-for="(item, index) in coupons.unCanUseCoupons"
:key="index"
v-for="(item, index) in coupons.unCanUseCoupons" <!-- 遍历不可用优惠券列表动态生成每个优惠券项 -->
:key="index" <!-- 使用索引作为唯一标识 -->
>
<coupon
:item="item"
order="true"
can-use="false"
:item="item" <!-- 传递优惠券数据 -->
order="true" <!-- 标记为订单页面使用 -->
can-use="false" <!-- 标记为不可用优惠券 -->
/>
</view>
</block>
<view class="botm-empty" />
<view class="botm-empty" /> <!-- 空占位符 -->
</view>
<!-- 确认按钮 -->
<view
v-if="couponSts==1"
v-if="couponSts == 1" <!-- 条件渲染只有当 couponSts 1 时显示确认按钮 -->
class="coupon-ok"
>
<text @tap="choosedCoupon">
确定
</text>
<text @tap="choosedCoupon"></text> <!-- -->
</view>
</view>
</view>
@ -298,230 +235,309 @@
</template>
<script setup>
const wxs = number()
let orderEntry = '0' // 0 1
/**
* 生命周期函数--监听页面加载
*/
onLoad((options) => {
orderEntry = options.orderEntry
})
const userAddr = ref(null)
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
const pages = getCurrentPages()
const currPage = pages[pages.length - 1]
if (currPage.selAddress === 'yes') {
//
userAddr.value = currPage.item
}
//
loadOrderData()
})
let couponIds = []
const coupons = ref({})
const total = ref(0)
const actualTotal = ref(0)
const orderItems = ref([])
const totalCount = ref(0)
const transfee = ref(0)
const shopReduce = ref('')
/**
* 加载订单数据
*/
const loadOrderData = () => {
let addrId = 0
if (userAddr.value != null) {
addrId = userAddr.value.addrId
}
uni.showLoading({
mask: true
// number
const wxs = number()
// 0 1
let orderEntry = '0'
/**
* 生命周期函数--监听页面加载
* @param options - 页面加载时传递的参数
*/
onLoad((options) => {
//
orderEntry = options.orderEntry
})
http.request({
url: '/p/order/confirm',
method: 'POST',
data: {
addrId,
orderItem: orderEntry === '1' ? JSON.parse(uni.getStorageSync('orderItem')) : undefined,
basketIds: orderEntry === '0' ? JSON.parse(uni.getStorageSync('basketIds')) : undefined,
couponIds,
userChangeCoupon: 1
//
const userAddr = ref(null)
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
//
const pages = getCurrentPages()
//
const currPage = pages[pages.length - 1]
// userAddr
if (currPage.selAddress === 'yes') {
userAddr.value = currPage.item
}
//
loadOrderData()
})
.then(({ data }) => {
uni.hideLoading()
let orderItemsData = []
data.shopCartOrders[0].shopCartItemDiscounts?.forEach(itemDiscount => {
orderItemsData = orderItems.value?.concat(itemDiscount.shopCartItems)
})
if (data.shopCartOrders[0].coupons) {
const canUseCoupons = []
const unCanUseCoupons = []
data.shopCartOrders[0].coupons?.forEach(coupon => {
if (coupon.canUse) {
canUseCoupons.push(coupon)
} else {
unCanUseCoupons.push(coupon)
}
// ID
let couponIds = []
//
const coupons = ref({})
const total = ref(0)
const actualTotal = ref(0)
const orderItems = ref([])
const totalCount = ref(0)
const transfee = ref(0)
const shopReduce = ref('')
/**
* 加载订单数据
*/
const loadOrderData = () => {
let addrId = 0
// ID
if (userAddr.value != null) {
addrId = userAddr.value.addrId
}
//
uni.showLoading({
mask: true
})
//
http.request({
url: '/p/order/confirm',
method: 'POST',
data: {
addrId,
//
orderItem: orderEntry === '1' ? JSON.parse(uni.getStorageSync('orderItem')) : undefined,
basketIds: orderEntry === '0' ? JSON.parse(uni.getStorageSync('basketIds')) : undefined,
couponIds, // ID
userChangeCoupon: 1 //
}
})
.then(({ data }) => {
//
uni.hideLoading()
//
let orderItemsData = []
//
data.shopCartOrders[0].shopCartItemDiscounts?.forEach(itemDiscount => {
orderItemsData = orderItems.value?.concat(itemDiscount.shopCartItems)
})
coupons.value = {
totalLength: data.shopCartOrders[0].coupons.length,
canUseCoupons,
unCanUseCoupons
//
if (data.shopCartOrders[0].coupons) {
const canUseCoupons = [] //
const unCanUseCoupons = [] //
//
data.shopCartOrders[0].coupons?.forEach(coupon => {
if (coupon.canUse) {
canUseCoupons.push(coupon)
} else {
unCanUseCoupons.push(coupon)
}
})
//
coupons.value = {
totalLength: data.shopCartOrders[0].coupons.length, //
canUseCoupons, //
unCanUseCoupons //
}
}
//
orderItems.value = orderItemsData
actualTotal.value = data.actualTotal
total.value = data.total
totalCount.value = data.totalCount
userAddr.value = data.userAddr
transfee.value = data.shopCartOrders[0].transfee
shopReduce.value = data.shopCartOrders[0].shopReduce
})
.catch(err => {
//
uni.hideLoading()
//
chooseCouponErrHandle(err)
})
}
/**
* 优惠券选择出错处理方法
* @param res - 错误响应对象
*/
const chooseCouponErrHandle = (res) => {
//
if (res.statusCode == 601) {
//
uni.showToast({
title: res.data,
icon: 'none',
duration: 3000,
success: () => {
// ID
couponIds = []
}
})
//
setTimeout(() => {
loadOrderData()
}, 2500)
}
}
/**
* 提交订单
*/
const toPay = () => {
//
if (!userAddr.value) {
uni.showToast({
title: '请选择地址',
icon: 'none'
})
return
}
//
submitOrder()
}
//
const remarks = ref('')
/**
* 提交订单
*/
const submitOrder = () => {
//
uni.showLoading({
mask: true
})
//
http.request({
url: '/p/order/submit',
method: 'POST',
data: {
orderShopParam: [{
remarks: remarks.value, //
shopId: 1 // ID1
}]
}
orderItems.value = orderItemsData
actualTotal.value = data.actualTotal
total.value = data.total
totalCount.value = data.totalCount
userAddr.value = data.userAddr
transfee.value = data.shopCartOrders[0].transfee
shopReduce.value = data.shopCartOrders[0].shopReduce
})
.catch(err => {
uni.hideLoading()
chooseCouponErrHandle(err)
.then(({ data }) => {
//
uni.hideLoading()
//
normalPay(data.orderNumbers)
})
}
/**
* 模拟支付直接提交成功
* @param orderNumbers - 订单编号列表
*/
const normalPay = (orderNumbers) => {
//
uni.showLoading({
mask: true
})
}
/**
* 优惠券选择出错处理方法
*/
const chooseCouponErrHandle = (res) => {
//
if (res.statusCode == 601) {
uni.showToast({
title: res.data,
icon: 'none',
duration: 3000,
success: () => {
couponIds = []
//
http.request({
url: '/p/order/normalPay',
method: 'POST',
data: {
orderNumbers
}
})
setTimeout(() => {
loadOrderData()
}, 2500)
.then(({ data }) => {
//
uni.hideLoading()
if (data) {
//
uni.showToast({
title: '模拟支付成功',
icon: 'none'
})
setTimeout(() => {
uni.navigateTo({
url: '/pages/pay-result/pay-result?sts=1&orderNumbers=' + orderNumbers
})
}, 1200)
} else {
//
uni.showToast({
title: '支付失败!',
icon: 'none'
})
}
})
}
}
/**
* 提交订单
*/
const toPay = () => {
if (!userAddr.value) {
uni.showToast({
title: '请选择地址',
icon: 'none'
})
return
// 12
const couponSts = ref(1)
/**
* 切换优惠券状态
* @param e - 事件对象
*/
const changeCouponSts = (e) => {
//
couponSts.value = e.currentTarget.dataset.sts
}
submitOrder()
}
const remarks = ref('')
const submitOrder = () => {
uni.showLoading({
mask: true
})
http.request({
url: '/p/order/submit',
method: 'POST',
data: {
orderShopParam: [{
remarks: remarks.value,
shopId: 1
}]
}
})
.then(({ data }) => {
uni.hideLoading()
normalPay(data.orderNumbers)
//
const popupShow = ref(false)
/**
* 显示优惠券弹窗
*/
const showCouponPopup = () => {
popupShow.value = true
}
/**
* 关闭优惠券弹窗
*/
const closePopup = () => {
popupShow.value = false
}
/**
* 跳转到地址选择页面
*/
const toAddrListPage = () => {
//
uni.navigateTo({
url: '/pages/delivery-address/delivery-address?order=0'
})
}
/**
* 模拟支付直接提交成功
* @param orderNumbers
*/
const normalPay = (orderNumbers) => {
uni.showLoading({
mask: true
})
http.request({
url: '/p/order/normalPay',
method: 'POST',
data: {
orderNumbers
}
/**
* 确认选择好的优惠券
*/
const choosedCoupon = () => {
//
loadOrderData()
//
popupShow.value = false
}
/**
* 处理优惠券子组件发过来的选择事件
* @param e - 事件对象
*/
const checkCoupon = (e) => {
// ID
const couponId = e.detail.couponId
// ID
if (couponIds.indexOf(couponId) === -1) {
couponIds.push(couponId)
} else {
// ID
couponIds.splice(couponIds.indexOf(couponId), 1)
}
})
.then(({ data }) => {
uni.hideLoading()
if (data) {
uni.showToast({
title: '模拟支付成功',
icon: 'none'
})
setTimeout(() => {
uni.navigateTo({
url: '/pages/pay-result/pay-result?sts=1&orderNumbers=' + orderNumbers
})
}, 1200)
} else {
uni.showToast({
title: '支付失败!',
icon: 'none'
})
}
})
}
const couponSts = ref(1)
const changeCouponSts = (e) => {
couponSts.value = e.currentTarget.dataset.sts
}
const popupShow = ref(false)
const showCouponPopup = () => {
popupShow.value = true
}
const closePopup = () => {
popupShow.value = false
}
/**
* 去地址页面
*/
const toAddrListPage = () => {
uni.navigateTo({
url: '/pages/delivery-address/delivery-address?order=0'
})
}
/**
* 确定选择好的优惠券
*/
const choosedCoupon = () => {
loadOrderData()
popupShow.value = false
}
/**
* 优惠券子组件发过来
*/
const checkCoupon = (e) => {
const index = couponIds.indexOf(e.detail.couponId)
if (index === -1) {
couponIds.push(e.detail.couponId)
} else {
couponIds.splice(index, 1)
}
}
</script>
<style scoped lang="scss">
@use './submit-order.scss';
@use './submit-order.scss'; //
</style>

@ -1,222 +1,400 @@
/* 定义容器样式 */
.container {
/* 设置背景颜色 */
background-color: #f7f7f7;
/* 设置底部内边距,以确保内容与底部有适当的空间 */
padding-bottom: 60rpx;
}
/* 用户信息模块样式 */
.userinfo {
/* 设置相对定位,以便其子元素可以使用绝对定位 */
position: relative;
/* 占满整个宽度 */
width: 100%;
/* 设置白色背景 */
background: #fff;
/* 文本居中显示 */
text-align: center;
/* 上下内边距,左右无内边距 */
padding: 30rpx 0;
.userinfo-con {
/* 设置固定宽度,使用户信息框在页面中心对齐 */
width: 240rpx;
/* 水平居中 */
margin: auto;
.userinfo-avatar {
/* 确保超出部分不显示 */
overflow: hidden;
/* 块级显示 */
display: block;
/* 设置头像的尺寸 */
width: 160rpx;
height: 160rpx;
/* 圆形头像 */
border-radius: 50%;
/* 添加阴影效果 */
box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);
/* 水平居中 */
margin: auto;
image {
/* 设置图像的尺寸 */
width: 160rpx;
height: 160rpx;
}
}
.userinfo-name {
/* 设置字体大小 */
font-size: 30rpx;
/* 加粗字体 */
font-weight: bold;
/* 与头像保持一定间距 */
margin-top: 20rpx;
/* 处理文本溢出 */
overflow: hidden;
/* 使用 WebKit 独有的多行文本截断 */
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
/* 当文本溢出时用省略号表示 */
text-overflow: ellipsis;
/* 允许单词内部断行 */
word-break: break-all;
}
}
}
/* 未登录用户的提示信息样式 */
.userinfo-none {
/* 使用 Flexbox 布局,使子元素垂直和水平居中 */
display: flex;
/* 内边距 */
padding: 30rpx;
/* 白色背景 */
background: #fff;
/* 子元素垂直居中 */
align-items: center;
.default-pic {
/* 右侧内边距 */
padding-right: 30rpx;
image {
/* 设置图像的尺寸 */
width: 160rpx;
height: 160rpx;
}
}
}
/* 未登录状态下的按钮和文本样式 */
.none-login {
button {
/* 按钮背景色为白色 */
background: #fff;
&::after {
/* 移除默认的按钮边框 */
border: 0;
}
}
.unlogin {
/* 设置字体大小 */
font-size: 30rpx;
/* 文本左对齐 */
text-align: left;
/* 移除上下内边距 */
padding: 0;
}
.click-login {
/* 设置较小的字体大小 */
font-size: 26rpx;
/* 颜色较深 */
color: #777;
/* 文本左对齐 */
text-align: left;
/* 移除上下内边距 */
padding: 0;
}
}
/* 绑定手机号的样式 */
.binding-phone {
/* 相对定位 */
position: relative;
/* 白色背景 */
background: #fff;
/* 固定高度 */
height: 80rpx;
/* 行高与高度相同,使文本垂直居中 */
line-height: 80rpx;
/* 内边距 */
padding: 0 30rpx;
/* 上下边框 */
border-top: 2rpx solid #f7f7f7;
border-bottom: 2rpx solid #f7f7f7;
.show-tip {
/* 设置字体大小 */
font-size: 26rpx;
}
.gotobinding {
/* 设置圆角 */
border-radius: 8rpx;
/* 字体大小 */
font-size: 28rpx;
color: #e24b4b;
/* 颜色为红色 */
color: #e24b42;
/* 加粗字体 */
font-weight: bold;
}
}
/* 列表内容样式 */
.list-cont {
/* 占满整个宽度 */
width: 100%;
/* 背景颜色 */
background: #f7f7f7;
/* 顶部外边距 */
margin-top: 20rpx;
.total-order {
/* 白色背景 */
background: #fff;
.order-tit {
/* 使用 Flexbox 布局 */
display: flex;
/* 两端对齐 */
justify-content: space-between;
/* 固定高度 */
height: 80rpx;
/* 行高与高度相同,使文本垂直居中 */
line-height: 80rpx;
/* 字体大小 */
font-size: 30rpx;
/* 下边框 */
border-bottom: 1px solid #f7f7f7;
/* 内边距 */
padding: 0 30rpx;
.checkmore {
/* 较小的字体大小 */
font-size: 22rpx;
/* 颜色较暗 */
color: #80848f;
/* 使用 Flexbox 布局 */
display: flex;
/* 垂直居中 */
align-items: center;
}
}
.procedure {
/* 使用 Flexbox 布局 */
display: flex;
/* 项目均匀分布 */
justify-content: space-around;
/* 项目垂直居中 */
align-items: center;
/* 字体大小 */
font-size: 25rpx;
/* 固定高度 */
height: 160rpx;
.items {
/* 相对定位 */
position: relative;
/* 使用 Flexbox 布局 */
display: flex;
/* 项目垂直排列 */
flex-direction: column;
/* 项目水平居中 */
align-items: center;
image {
/* 设置图像的尺寸 */
width: 70rpx;
height: 70rpx;
/* 与下方文字保持间距 */
margin-bottom: 20rpx;
}
.num-badge {
/* 绝对定位 */
position: absolute;
/* 设置徽章位置 */
top: -15rpx;
right: -12rpx;
/* 颜色为红色 */
color: #eb2444;
/* 边框颜色为红色 */
border: 3rpx solid #eb2444;
/* 圆形 */
border-radius: 50rpx;
/* 白色背景 */
background: #fff;
/* 最小宽度 */
min-width: 30rpx;
/* 固定高度 */
height: 30rpx;
/* 行高与高度相同,使文本垂直居中 */
line-height: 30rpx;
/* 文本水平居中 */
text-align: center;
/* 内边距 */
padding: 2rpx;
/* 块级显示 */
display: block;
}
}
}
}
.my-menu {
/* 白色背景 */
background-color: #fff;
/* 顶部外边距 */
margin-top: 20rpx;
.memu-item {
/* 使用 Flexbox 布局 */
display: flex;
/* 项目垂直居中 */
align-items: center;
/* 项目两端对齐 */
justify-content: space-between;
/* 固定高度 */
height: 100rpx;
/* 下边框 */
border-bottom: 2rpx solid #f7f7f7;
/* 内边距 */
padding: 0 30rpx;
&:nth-child(1) {
/* 第一个项目的上边框 */
border-top: 2rpx solid #f7f7f7;
}
&:last-child {
/* 最后一个项目的下边框移除 */
border-bottom: none;
}
text {
/* 字体大小 */
font-size: 28rpx;
}
image {
/* 设置图像的尺寸 */
width: 50rpx;
height: 50rpx;
/* 与右侧文本保持间距 */
margin-right: 20rpx;
}
.i-name {
/* 使用 Flexbox 布局 */
display: flex;
/* 项目垂直居中 */
align-items: center;
}
}
}
}
/* 箭头样式 */
.arrowhead {
/* 设置箭头的尺寸 */
width: 15rpx;
height: 15rpx;
/* 上边框 */
border-top: 2rpx solid #999;
/* 右边框 */
border-right: 2rpx solid #999;
/* 旋转45度形成箭头形状 */
transform: rotate(45deg);
/* 左侧外边距 */
margin-left: 10rpx;
}
/* 产品栏样式 */
.prod-col {
/* 顶部外边距 */
margin-top: 20rpx;
/* 白色背景 */
background: #fff;
/* 使用 Flexbox 布局 */
display: flex;
/* 项目均匀分布 */
justify-content: space-around;
/* 内边距 */
padding: 30rpx 0 10rpx 0;
/* 字体大小 */
font-size: 12px;
.col-item {
/* 文本居中 */
text-align: center;
.num {
/* 字体较大且加粗 */
font-size: 16px;
font-weight: 700;
/* 颜色为蓝色 */
color: #3a86b9;
}
.tit {
/* 行高 */
line-height: 34px;
}
}
}
/* 登出按钮样式 */
.log-out {
/* 内边距 */
padding: 20rpx;
/* 文本居中 */
text-align: center;
/* 顶部外边距 */
margin-top: 20rpx;
}
.log-out-n {
/* 字体大小 */
font-size: 30rpx;
/* 水平居中 */
margin: auto;
/* 固定宽度 */
width: 200rpx;
/* 内边距 */
padding: 20rpx;
/* 圆角 */
border-radius: 10rpx;
/* 红色背景 */
background: #e43130;
/* 白色文字 */
color: #ffffff;
}
/* 自定义菜单按钮样式 */
button.memu-btn.memu-item {
/* 白色背景 */
background-color: #fff;
&:after {
/* 移除默认的按钮边框 */
border: 0;
}
}

@ -1,221 +1,131 @@
<template>
<view class="container">
<!-- 用户信息 -->
<view
v-if="isAuthInfo"
class="userinfo"
>
<!-- 用户信息模块 -->
<!-- 如果用户已授权并登录则显示用户信息 -->
<view v-if="isAuthInfo" class="userinfo">
<view class="userinfo-con">
<!-- 用户头像 -->
<view class="userinfo-avatar">
<image
:src="
loginResult.pic
?
(loginResult.pic.indexOf('http') === -1 ? picDomain + loginResult.pic : loginResult.pic)
:
'/static/images/icon/head04.png'
? (loginResult.pic.indexOf('http') === -1 ? picDomain + loginResult.pic : loginResult.pic)
: '/static/images/icon/head04.png'
"
/>
</view>
<!-- 用户昵称 -->
<view class="userinfo-name">
<view>{{ loginResult.nickName ? loginResult.nickName : "用户昵称" }}</view>
</view>
</view>
</view>
<view
v-if="!isAuthInfo"
class="userinfo-none"
>
<view
class="default-pic"
@tap="toLogin"
>
<!-- 如果用户未登录则显示默认的登录提示 -->
<view v-if="!isAuthInfo" class="userinfo-none">
<view class="default-pic" @tap="toLogin">
<image src="@/static/images/icon/head04.png" />
</view>
<view
class="none-login"
@tap="toLogin"
>
<button class="unlogin">
未登录
</button>
<button class="click-login">
点击登录账号
</button>
<view class="none-login" @tap="toLogin">
<button class="unlogin">未登录</button>
<button class="click-login">点击登录账号</button>
</view>
</view>
<!-- end 用户信息 -->
<!-- end 用户信息模块 -->
<view class="list-cont">
<!-- 订单状态 -->
<!-- 订单状态模块 -->
<view class="total-order">
<view class="order-tit">
<text style="font-weight:bold">
我的订单
</text>
<view
class="checkmore"
data-sts="0"
@tap="toOrderListPage"
>
<text style="font-weight:bold">我的订单</text>
<!-- 查看全部订单按钮 -->
<view class="checkmore" data-sts="0" @tap="toOrderListPage">
<text>查看全部</text>
<text class="arrowhead" />
</view>
</view>
<!-- 订单流程条 -->
<view class="procedure">
<view
class="items"
data-sts="1"
@tap="toOrderListPage"
>
<!-- 待支付订单 -->
<view class="items" data-sts="1" @tap="toOrderListPage">
<image src="@/static/images/icon/toPay.png" />
<text>待支付</text>
<text
v-if="orderAmount.unPay>0"
class="num-badge"
>
{{ orderAmount.unPay }}
</text>
<!-- 如果有待支付订单显示数量 -->
<text v-if="orderAmount.unPay > 0" class="num-badge">{{ orderAmount.unPay }}</text>
</view>
<view
class="items"
data-sts="2"
@tap="toOrderListPage"
>
<!-- 待发货订单 -->
<view class="items" data-sts="2" @tap="toOrderListPage">
<image src="@/static/images/icon/toDelivery.png" />
<text>待发货</text>
<text
v-if="orderAmount.payed>0"
class="num-badge"
>
{{ orderAmount.payed }}
</text>
<!-- 如果有待发货订单显示数量 -->
<text v-if="orderAmount.payed > 0" class="num-badge">{{ orderAmount.payed }}</text>
</view>
<view
class="items"
data-sts="3"
@tap="toOrderListPage"
>
<!-- 待收货订单 -->
<view class="items" data-sts="3" @tap="toOrderListPage">
<image src="@/static/images/icon/toTake.png" />
<text>待收货</text>
<text
v-if="orderAmount.consignment>0"
class="num-badge"
>
{{ orderAmount.consignment }}
</text>
<!-- 如果有待收货订单显示数量 -->
<text v-if="orderAmount.consignment > 0" class="num-badge">{{ orderAmount.consignment }}</text>
</view>
<view
class="items"
data-sts="5"
@tap="toOrderListPage"
>
<!-- 已完成订单 -->
<view class="items" data-sts="5" @tap="toOrderListPage">
<image src="@/static/images/icon/toComment.png" />
<text>已完成</text>
</view>
</view>
</view>
<!--end 订单状态 -->
<!-- end 订单状态模块 -->
<!-- 产品栏 -->
<view class="prod-col">
<view
class="col-item"
@tap="myCollectionHandle"
>
<view
v-if="loginResult"
class="num"
>
{{ collectionCount }}
</view>
<view
v-else
class="num"
>
--
</view>
<view class="tit">
我的收藏
</view>
<!-- 我的收藏 -->
<view class="col-item" @tap="myCollectionHandle">
<view v-if="loginResult" class="num">{{ collectionCount }}</view>
<view v-else class="num">--</view>
<view class="tit">我的收藏</view>
</view>
<view
class="col-item"
@tap="handleTips"
>
<view
v-if="loginResult"
class="num"
>
5
</view>
<view
v-else
class="num"
>
--
</view>
<view class="tit">
我的消息
</view>
<!-- 我的消息 -->
<view class="col-item" @tap="handleTips">
<view v-if="loginResult" class="num">5</view>
<view v-else class="num">--</view>
<view class="tit">我的消息</view>
</view>
<view
class="col-item"
@tap="handleTips"
>
<view
v-if="loginResult"
class="num"
>
3
</view>
<view
v-else
class="num"
>
--
</view>
<view class="tit">
我的足迹
</view>
<!-- 我的足迹 -->
<view class="col-item" @tap="handleTips">
<view v-if="loginResult" class="num">3</view>
<view v-else class="num">--</view>
<view class="tit">我的足迹</view>
</view>
</view>
<!-- 菜单列表 -->
<view class="my-menu">
<view
class="memu-item"
@tap="toDistCenter"
>
<!-- 分销中心 -->
<view class="memu-item" @tap="toDistCenter">
<view class="i-name">
<image src="@/static/images/icon/promotion.png" />
<text>分销中心</text>
</view>
<view class="arrowhead" />
</view>
<view
class="memu-item"
@tap="toCouponCenter"
>
<!-- 领券中心 -->
<view class="memu-item" @tap="toCouponCenter">
<view class="i-name">
<image src="@/static/images/icon/getCoupon.png" />
<text>领券中心</text>
</view>
<view class="arrowhead" />
</view>
<view
class="memu-item"
@tap="toMyCouponPage"
>
<!-- 我的优惠券 -->
<view class="memu-item" @tap="toMyCouponPage">
<view class="i-name">
<image src="@/static/images/icon/myCoupon.png" />
<text>我的优惠券</text>
</view>
<view class="arrowhead" />
</view>
<view
class="memu-item"
@tap="toAddressList"
>
<!-- 收货地址 -->
<view class="memu-item" @tap="toAddressList">
<view class="i-name">
<image src="@/static/images/icon/myAddr.png" />
<text>收货地址</text>
@ -223,13 +133,10 @@
<view class="arrowhead" />
</view>
</view>
<!--end 列表 -->
<!-- end 菜单列表 -->
<view
v-if="isAuthInfo"
class="log-out"
@tap="logout"
>
<!-- 登出按钮仅在用户已登录时显示 -->
<view v-if="isAuthInfo" class="log-out" @tap="logout">
<view class="log-out-n">
<text>退出登录</text>
</view>
@ -239,84 +146,100 @@
</template>
<script setup>
// URL
const picDomain = import.meta.env.VITE_APP_RESOURCES_URL
const isAuthInfo = ref(false)
const loginResult = ref('')
const orderAmount = ref('')
//
const isAuthInfo = ref(false) //
const loginResult = ref('') //
const orderAmount = ref('') //
/**
* 生命周期函数--监听页面显示
*/
onShow(() => {
//
loginResult.value = uni.getStorageSync('loginResult')
isAuthInfo.value = !!loginResult.value
//
//
if (isAuthInfo.value) {
uni.showLoading()
uni.showLoading() //
http.request({
url: '/p/myOrder/orderCount',
method: 'GET',
data: {}
})
.then(({ data }) => {
uni.hideLoading()
orderAmount.value = data
uni.hideLoading() //
orderAmount.value = data //
})
showCollectionCount()
showCollectionCount() //
}
})
//
const toDistCenter = () => {
uni.showToast({
icon: 'none',
title: '该功能未开源'
})
}
//
const toCouponCenter = () => {
uni.showToast({
icon: 'none',
title: '该功能未开源'
})
}
//
const toMyCouponPage = () => {
uni.showToast({
icon: 'none',
title: '该功能未开源'
})
}
//
const handleTips = () => {
uni.showToast({
icon: 'none',
title: '该功能未开源'
})
}
//
const toAddressList = () => {
uni.navigateTo({
url: '/pages/delivery-address/delivery-address'
})
}
//
const toOrderListPage = (e) => {
const sts = e.currentTarget.dataset.sts
const sts = e.currentTarget.dataset.sts //
uni.navigateTo({
url: '/pages/orderList/orderList?sts=' + sts
url: '/pages/orderList/orderList?sts=' + sts //
})
}
//
const collectionCount = ref(0)
/**
* 查询所有的收藏量
*/
const showCollectionCount = () => {
uni.showLoading()
uni.showLoading() //
http.request({
url: '/p/user/collection/count',
method: 'GET',
data: {}
})
.then(({ data }) => {
uni.hideLoading()
collectionCount.value = data
uni.hideLoading() //
collectionCount.value = data //
})
}
@ -324,14 +247,14 @@ const showCollectionCount = () => {
* 我的收藏跳转
*/
const myCollectionHandle = () => {
let url = '/pages/prod-classify/prod-classify?sts=5'
const id = 0
const title = '我的收藏商品'
let url = '/pages/prod-classify/prod-classify?sts=5' //
const id = 0 // ID
const title = '我的收藏商品' //
if (id) {
url += '&tagid=' + id + '&title=' + title
url += '&tagid=' + id + '&title=' + title // ID
}
uni.navigateTo({
url
url //
})
}
@ -340,7 +263,7 @@ const myCollectionHandle = () => {
*/
const toLogin = () => {
uni.navigateTo({
url: '/pages/accountLogin/accountLogin'
url: '/pages/accountLogin/accountLogin' //
})
}
@ -353,23 +276,23 @@ const logout = () => {
method: 'post'
})
.then(() => {
util.removeTabBadge()
uni.removeStorageSync('loginResult')
uni.removeStorageSync('token')
util.removeTabBadge() //
uni.removeStorageSync('loginResult') //
uni.removeStorageSync('token') // token
uni.showToast({
title: '退出成功',
icon: 'none'
})
orderAmount.value = ''
orderAmount.value = '' //
setTimeout(() => {
uni.switchTab({
url: '/pages/index/index'
url: '/pages/index/index' //
})
}, 1000)
}, 1000) // 1
})
}
</script>
<style scoped lang="scss">
@use './user.scss';
@use './user.scss'; //
</style>

@ -3,102 +3,108 @@
import loginMethods from './login'
const http = {
/**
* 发起HTTP请求
* @param {Object} params - 请求参数对象包含urldatamethod等
* @returns {Promise} - 返回一个Promise对象解析或拒绝根据请求结果
*/
request: async function (params) {
// 请求参数处理
// 处理请求数据类型,确保数组和数字被正确序列化为字符串
if (Object.prototype.toString.call(params.data) === '[object Array]') {
params.data = JSON.stringify(params.data)
} else if (Object.prototype.toString.call(params.data) === '[object Number]') {
params.data = params.data + ''
}
// 刷新token
// 如果不是登录请求且应用未完全启动即未完成首次加载并且不是刷新token的请求则尝试刷新token
if (!params.login && !getApp()?.globalData.isLanding && !params.isRefreshing) {
await loginMethods.refreshToken()
}
// 发起请求
// 发起请求并返回Promise对象处理异步操作
return new Promise((resolve, reject) => {
uni.request({
dataType: 'json',
responseType: params.responseType === undefined ? 'text' : params.responseType,
header: {
Authorization: uni.getStorageSync('Token')
Authorization: uni.getStorageSync('Token') // 设置请求头中的Authorization字段
},
url: (params.domain ? params.domain : import.meta.env.VITE_APP_BASE_API) + params.url,
data: params.data,
method: params.method === undefined ? 'POST' : params.method,
url: (params.domain ? params.domain : import.meta.env.VITE_APP_BASE_API) + params.url, // 拼接完整的请求URL
data: params.data, // 请求体数据
method: params.method === undefined ? 'POST' : params.method, // 默认请求方法为POST
success: (res) => {
const responseData = res.data
// 请求小程序码
// 特殊处理小程序码请求
if (params.responseType === 'arraybuffer' && res.statusCode === 200) {
return resolve(responseData)
}
// 00000 请求成功
// A00002 用于直接显示提示系统的成功,内容由输入决定
if (responseData.code === '00000' || responseData.code === 'A00002') {
resolve(responseData)
}
// A00004 未授权
if (responseData.code === 'A00004') {
// 重设登录后跳转地址
loginMethods.setRouteUrlAfterLogin()
uni.removeStorageSync('expiresTimeStamp')
uni.removeStorageSync('loginResult')
uni.removeStorageSync('Token')
if (!params.dontTrunLogin) {
uni.showModal({
title: '提示',
content: uni.getStorageSync('hadLogin') ? '登录已过期,请重新登陆!' : '请先进行登录!',
cancelText: '取消',
confirmText: '确定',
success: res => {
if (res.confirm) {
uni.navigateTo({
url: '/pages/accountLogin/accountLogin'
})
} else {
const router = getCurrentPages()
if (router[0].route === 'pages/basket/basket') {
uni.switchTab({
url: '/pages/index/index'
// 根据响应码处理不同情况
switch (responseData.code) {
case '00000': // 请求成功
case 'A00002': // 系统成功的提示信息
resolve(responseData)
break;
case 'A00004': // 未授权
// 重设登录后跳转地址,并清除相关存储
loginMethods.setRouteUrlAfterLogin()
uni.removeStorageSync('expiresTimeStamp')
uni.removeStorageSync('loginResult')
uni.removeStorageSync('Token')
// 如果允许转向登录页面,则显示模态框提示用户重新登录
if (!params.dontTrunLogin) {
uni.showModal({
title: '提示',
content: uni.getStorageSync('hadLogin') ? '登录已过期,请重新登陆!' : '请先进行登录!',
cancelText: '取消',
confirmText: '确定',
success: res => {
if (res.confirm) {
uni.navigateTo({
url: '/pages/accountLogin/accountLogin'
})
} else {
const router = getCurrentPages()
if (router[0].route === 'pages/basket/basket') {
uni.switchTab({
url: '/pages/index/index'
})
}
}
}
}
})
}
resolve(responseData)
}
// A00005 服务器出了点小差
if (responseData.code === 'A00005') {
this.onRequestFail(params, responseData)
uni.showToast({
title: '服务器出了点小差~',
icon: 'none'
})
}
// 其他异常码
// A00001 用于直接显示提示用户的错误,内容由输入内容决定
// A04001 社交账号未绑定
// A00012 tempUid错误
// A00006 验证码错误
if (responseData.code === 'A04001' || responseData.code === 'A00001' || responseData.code === 'A00012' || responseData.code === 'A00006') {
if (!params.hasCatch) {
})
}
resolve(responseData)
break;
case 'A00005': // 服务器错误
this.onRequestFail(params, responseData)
uni.showToast({
title: responseData.msg || responseData.data || 'Error',
title: '服务器出了点小差~',
icon: 'none'
})
}
}
if (responseData.code !== '00000') {
reject(responseData)
break;
case 'A04001': // 社交账号未绑定
case 'A00001': // 直接显示提示用户的错误
case 'A00012': // tempUid错误
case 'A00006': // 验证码错误
if (!params.hasCatch) {
uni.showToast({
title: responseData.msg || responseData.data || 'Error',
icon: 'none'
})
}
reject(responseData)
break;
default:
if (responseData.code !== '00000') {
reject(responseData)
}
}
},
fail: (err) => {
// 请求失败时显示提示并拒绝Promise
uni.showToast({
title: '请求失败'
})
@ -107,56 +113,70 @@ const http = {
})
})
},
/**
* 获取购物车商品数量并更新tabBar徽章
*/
getCartCount: () => {
if (!uni.getStorageSync('Token')) {
util.removeTabBadge()
util.removeTabBadge() // 如果没有token移除购物车徽章
return
}
http.request({
url: '/p/shopCart/prodCount',
method: 'GET',
dontTrunLogin: true,
dontTrunLogin: true, // 不需要转向登录
data: {}
})
.then(({ data }) => {
if (data > 0) {
uni.setTabBarBadge({
index: 2,
text: data + ''
index: 2, // 假设购物车位于第三个tab
text: data + '' // 显示商品数量
})
getApp().globalData.totalCartCount = data
getApp().globalData.totalCartCount = data // 更新全局购物车数量
} else {
uni.removeTabBarBadge({
index: 2
index: 2 // 移除购物车徽章
})
getApp().globalData.totalCartCount = 0
}
})
},
/**
* 请求失败时的日志记录
* @param {Object} params - 请求参数
* @param {Object} responseData - 响应数据
*/
onRequestFail: (params, responseData) => {
console.error('============== 请求异常 ==============')
console.log('接口地址: ', params.url)
console.log('异常信息: ', responseData)
console.error('============== 请求异常 end ==========')
},
/**
* 登录成功后执行
* @param {Object} result 登录成功返回的数据
* @param {Object} fn 登录成功后的回调
* 登录成功后的处理逻辑
* @param {Object} result - 登录成功返回的数据
* @param {Function} fn - 登录成功后的回调函数
*/
loginSuccess: (result, fn) => {
// 保存登陆信息
// 保存登录信息到本地存储
wx.setStorageSync('loginResult', result)
// 保存成功登录标识,token过期判断
// 保存成功登录标识用于判断token是否过期
wx.setStorageSync('hadLogin', true)
const expiresTimeStamp = result.expiresIn * 1000 / 2 + new Date().getTime()
const expiresTimeStamp = result.expiresIn * 1000 / 2 + new Date().getTime() // 计算token过期时间
// 缓存token的过期时间
uni.setStorageSync('expiresTimeStamp', expiresTimeStamp)
wx.setStorageSync('Token', result.accessToken) // 把token存入缓存请求接口数据时要用
// 将accessToken保存到本地存储以便后续请求使用
wx.setStorageSync('Token', result.accessToken)
// 执行登录成功后的回调函数
if (fn) {
fn()
}
}
}
export default http

@ -1,37 +1,58 @@
const loginMethods = {
/**
* 刷新token
* 当用户的Token即将过期时自动发起请求以获取新的Token
* 这个过程会检查当前Token是否有效以及是否正在进行刷新操作以避免重复请求
*/
refreshToken: () => {
// 从本地存储中获取Token的过期时间戳
const expiresTimeStamp = uni.getStorageSync('expiresTimeStamp')
// 如果没有有效的Token或过期时间戳则直接返回不进行刷新
if (!uni.getStorageSync('Token') || !expiresTimeStamp) return
// 检查Token是否已经过期
const isExpires = expiresTimeStamp < new Date().getTime()
// 检查是否已经有刷新Token的操作正在进行
const isRefreshing = uni.getStorageSync('isRefreshingToken')
// 如果Token未过期或已有刷新操作正在进行则直接返回
if (!isExpires || isRefreshing) {
return
}
// 设置标志位表示正在刷新Token防止并发刷新
uni.setStorageSync('isRefreshingToken', true)
// 发起刷新Token的请求
http.request({
url: '/token/refresh',
method: 'POST',
login: true,
isRefreshing: true,
dontTrunLogin: true
url: '/token/refresh', // 刷新Token的API接口
method: 'POST', // 使用POST方法发送请求
login: true, // 标记这是一个与登录相关的请求
isRefreshing: true, // 标记这是刷新Token的请求
dontTrunLogin: true // 即使刷新失败也不跳转到登录页面
}).then(res => {
// 刷新成功后更新本地存储中的Token过期时间
uni.setStorageSync('isRefreshingToken', false)
uni.setStorageSync('expiresTimeStamp', res.data * 1000 / 2 + new Date().getTime())
}).catch(() => {
// 刷新失败后,重置标志位
uni.setStorageSync('isRefreshingToken', false)
})
},
/**
* 设置登录后的跳转地址
* 在用户登录之前记录当前页面的路径以便在登录成功后能够返回到该页面
*/
setRouteUrlAfterLogin: () => {
// 获取当前所有页面的栈
const pages = getCurrentPages()
// 登录后的回跳地址
// 确保当前页面不是登录页面(避免循环跳转)
if (pages[pages.length - 1].route.indexOf('user-login') === -1) {
// 记录当前页面的完整路径,用于登录成功后的跳转
uni.setStorageSync('routeUrlAfterLogin', pages[pages.length - 1].$page.fullPath)
}
}

@ -1,34 +1,71 @@
const util = {
/**
* 格式化日期时间
* 将传入的Date对象格式化为"YYYY/MM/DD HH:MM:SS"的字符串形式
* @param {Date} date - 要格式化的日期对象
* @returns {String} - 格式化后的日期时间字符串
*/
formatTime: date => {
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()
const year = date.getFullYear() // 获取年份
const month = date.getMonth() + 1 // 获取月份注意getMonth()返回的是0-11所以需要加1
const day = date.getDate() // 获取日期
const hour = date.getHours() // 获取小时
const minute = date.getMinutes() // 获取分钟
const second = date.getSeconds() // 获取秒
// 使用formatNumber方法确保所有时间部分都是两位数并用'/'连接日期部分,':'连接时间部分
return [year, month, day].map(util.formatNumber).join('/') + ' ' + [hour, minute, second].map(util.formatNumber).join(':')
},
/**
* 格式化数字
* 确保传入的数字是两位数如果不是则在前面补零
* @param {Number|String} n - 需要格式化的数字
* @returns {String} - 格式化后的两位数字符串
*/
formatNumber: n => {
n = n.toString()
return n[1] ? n : '0' + n
n = n.toString() // 将数字转换为字符串
return n[1] ? n : '0' + n // 如果长度大于1则直接返回否则在前面补零
},
/**
* 格式化HTML内容
* 对传入的HTML字符串进行样式调整以适应移动端显示特别是处理图片表格等内容的宽度和布局
* @param {String} content - 需要格式化的HTML字符串
* @returns {String} - 格式化后的HTML字符串
*/
formatHtml: content => {
if (!content) {
return
return // 如果没有内容,直接返回
}
// 替换<p>标签,设置最大宽度、单词换行等样式
content = content.replace(/<p/gi, '<p style="max-width:100% !important;word-wrap:break-word;word-break:break-word;" ')
// 替换<img>标签,设置最大宽度、高度自适应、边距等样式
content = content.replace(/<img/gi, '<img style="max-width:100% !important;height:auto !important;margin:0;display:flex;" ')
// 替换<style>标签中的样式,确保内部样式不会覆盖外部样式
content = content.replace(/style="/gi, 'style="max-width:100% !important;table-layout:fixed;word-wrap:break-word;word-break:break-word;')
// 替换<table>标签,设置表格布局和单词换行等样式
content = content.replace(/<table/gi, '<table style="table-layout:fixed;word-wrap:break-word;word-break:break-word;" ')
// 替换<td>标签,设置单元格间距、填充、边框等样式
content = content.replace(/<td/gi, '<td cellspacing="0" cellpadding="0" style="border-width:1px; border-style:solid; border-color:#666; margin: 0px; padding: 0px;"')
// 移除width和height属性防止图片和表格固定宽度或高度
content = content.replace(/width=/gi, 'sss=')
content = content.replace(/height=/gi, 'sss=')
// 确保自闭合标签如<img>有正确的样式
content = content.replace(/\/>/gi, ' style="max-width:100% !important;height:auto !important;margin:0;display:block;" />')
return content
},
return content // 返回格式化后的HTML字符串
}
}
export default util
/**
* 移除购物车Tabbar的数字
*/

@ -1,38 +1,42 @@
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import path from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import h5ProdEffectPlugin from 'uni-vite-plugin-h5-prod-effect'
import { defineConfig } from 'vite'; // 从 Vite 导入 defineConfig 函数,用于定义 Vite 配置
import uni from '@dcloudio/vite-plugin-uni'; // 导入 Uni-App 的 Vite 插件,用于支持 Uni-App 项目
import path from 'path'; // 导入 Node.js 的 path 模块,用于处理文件路径
import AutoImport from 'unplugin-auto-import/vite'; // 导入 unplugin-auto-import 插件,用于自动导入常用模块
import h5ProdEffectPlugin from 'uni-vite-plugin-h5-prod-effect'; // 导入 H5 生产环境打包时的特殊处理插件
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
uni(),
uni(), // 启用 Uni-App 的 Vite 插件,确保 Vite 能够正确处理 Uni-App 项目
AutoImport({
imports: [
'vue',
'uni-app'
'vue', // 自动导入 Vue 的全局 API
'uni-app' // 自动导入 Uni-App 的全局 API
],
dirs: [
'src/utils',
'src/wxs/**'
'src/utils', // 指定自动导入的目录,这里是 `src/utils` 目录下的所有文件
'src/wxs/**' // 指定自动导入的目录,这里是 `src/wxs` 目录及其子目录下的所有文件
],
dts: 'src/auto-imports.d.ts',
dts: 'src/auto-imports.d.ts', // 生成的类型声明文件路径,用于 TypeScript 支持
eslintrc: {
enabled: true
enabled: true // 启用 ESLint 配置文件的自动生成,确保 ESLint 知道哪些模块是自动导入的
}
}),
// 对h5 production环境打包时的特殊处理否则uni-crazy-router在这个环境会异常
// 对 H5 生产环境打包时的特殊处理,确保 `uni-crazy-router` 在生产环境中正常工作
h5ProdEffectPlugin()
],
server: {
host: true,
port: 80,
open: true
host: true, // 允许外部访问开发服务器,设置为 `true` 可以在局域网内访问
port: 80, // 设置开发服务器的端口号为 80
open: true // 启动开发服务器时自动打开浏览器
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
'@': path.resolve(__dirname, 'src') // 设置别名 `@` 指向项目的 `src` 目录,方便路径引用
}
}
})
});

Loading…
Cancel
Save