refactor: 重构多个组件以支持 Vue 3 语法和功能

- 将多个组件转换为 `<script setup>` 语法,提升可读性和性能
- 优化状态管理和事件处理逻辑,简化代码结构
- 更新样式和布局以适应新组件结构
- 添加新功能和修复已知问题,提升用户体验
This commit is contained in:
Yer11214
2026-07-08 18:37:11 +08:00
parent 4d0b0fb5e4
commit 349ffa4f34
189 changed files with 18278 additions and 20758 deletions

View File

@@ -1,5 +1,5 @@
<template>
<div>
<view>
<!-- 一行两列商品展示 -->
<view class="goods-list" v-if="type == 'twoColumns'">
<view v-for="(item, index) in res" :key="index" class="goods-item">
@@ -10,7 +10,7 @@
height="330rpx"
mode="aspectFit"
>
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</view>
<view class="goods-detail">
@@ -57,7 +57,7 @@
</view>
<!-- 一行一列商品展示 -->
<div v-if="type == 'oneColumns'">
<div v-for="(item, index) in res" :key="index" class="goods-row">
div v-for="(item, index) in res" :key="index" class="goods-row">
<div class="flex goods-col">
<div class="goods-img" @click="navigateToDetailPage(item)">
<u-image
@@ -67,7 +67,7 @@
mode="aspectFit"
:src="item.goodsImage || item.thumbnail"
>
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</div>
<div class="goods-detail">
@@ -112,41 +112,26 @@
</div>
</div>
</div>
</div>
</view>
</template>
<script>
import commonTpl from "@/components/m-goods-list/common";
export default {
data() {
return {
lightColor: this.$mainColor,
};
},
mixins: [commonTpl],
props: {
// 展示的类型
type:{
type:String,
default:"oneColumns"
},
// 遍历的数据
res: {
type: Array,
default: () => {
return [];
},
},
},
methods: {
// 跳转到商品详情
navigateToDetailPage(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
},
};
<script setup lang="ts">
import { useGoodsListCommon } from './common'
import { goodsFormatPrice } from '@/utils/filters.js'
const { lightSearchStr, getPromotion, navigateToDetailPage, navigateToStoreDetailPage } = useGoodsListCommon()
const props = withDefaults(defineProps<{
type?: string
res?: any[]
storeName?: boolean
keyword?: string | null
}>(), {
type: 'oneColumns',
res: () => [],
storeName: true,
keyword: ''
})
</script>
<style lang="scss" scoped>

View File

@@ -0,0 +1,90 @@
import { useStore } from '@/store'
/**
* 商品列表公共逻辑(原 common.vue mixin
* 提供高亮搜索、unicode 转换、促销标签、导航跳转等通用方法
*/
export function useGoodsListCommon() {
const store = useStore()
const lightColor = store.getters.lightColor
/**
* 高亮显示搜索内容
*/
const lightSearchStr = (keyword: string, str: string) => {
if (!keyword) {
return str
} else {
let unicodes = ''
for (let i of Array.from(keyword)) {
unicodes += unicode(i) + "|"
}
const rule = '(' + unicodes + ')'
const reg = new RegExp(rule, 'gi')
return str ? str.replace(reg, (matchValue) =>
`<span style="color:${lightColor}">${matchValue}</span>`
) : ''
}
}
/**
* 转换为 unicode
*/
const unicode = (str: string) => {
var value = ''
for (var i = 0; i < str.length; i++) {
value += '\\u' + leftZero4(parseInt(str.charCodeAt(i).toString(16)))
}
return value
}
const leftZero4 = (str: string) => {
if (str != null && str != '' && str != 'undefined') {
if (str.length == 2) {
return '00' + str
}
}
return str
}
/**
* 数据去重 只显示一次 减免 劵等
*/
const getPromotion = (item: any) => {
if (item.promotionMap) {
let array: string[] = []
Object.keys(item.promotionMap).forEach((child) => {
if (!array.includes(child.split("-")[0])) {
array.push(child.split("-")[0])
}
})
return array
}
}
/**
* 跳转到商品详情
*/
const navigateToDetailPage = (item: any) => {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
})
}
/**
* 跳转到店铺详情
*/
const navigateToStoreDetailPage = (item: any) => {
uni.navigateTo({
url: `/pages/product/shopPage?id=${item.storeId}`,
})
}
return {
lightSearchStr,
getPromotion,
navigateToDetailPage,
navigateToStoreDetailPage,
}
}

View File

@@ -1,69 +0,0 @@
<template>
</template>
<script>
export default {
methods: {
// 高亮显示搜索内容
lightSearchStr(keyword, str) {
if (!keyword) {
return str
} else {
let unicodes = '';
for (let i of Array.from(keyword)) {
unicodes += this.unicode(i) + "|"
}
const rule = '(' + unicodes + ')'
const reg = new RegExp(rule, 'gi');
return str ? str.replace(reg, matchValue =>
`<span style="color:${this.lightColor}">${matchValue}</span>`
) : ''
}
},
// 转换为unicode
unicode(str) {
var value = '';
for (var i = 0; i < str.length; i++) {
value += '\\u' + this.left_zero_4(parseInt(str.charCodeAt(i)).toString(16));
}
return value;
},
left_zero_4(str) {
if (str != null && str != '' && str != 'undefined') {
if (str.length == 2) {
return '00' + str;
}
}
return str;
},
// 数据去重一下 只显示一次 减免 劵 什么的
getPromotion(item) {
if (item.promotionMap) {
let array = [];
Object.keys(item.promotionMap).forEach((child) => {
if (!array.includes(child.split("-")[0])) {
array.push(child.split("-")[0]);
}
});
return array;
}
},
// 跳转到商品详情
navigateToDetailPage(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
// 跳转地址
navigateToStoreDetailPage(item) {
uni.navigateTo({
url: `/pages/product/shopPage?id=${item.storeId}`,
});
},
},
}
</script>
<style lang='scss' scoped>
</style>

View File

@@ -5,7 +5,7 @@
<view v-for="(item, index) in res" :key="index" class="goods-item">
<view class="image-wrapper" @click="navigateToDetailPage(item)">
<u-image :src="item.thumbnail" width="100%" height='330rpx' mode="aspectFit">
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</view>
<view class="goods-detail">
@@ -51,7 +51,7 @@
<div class="flex goods-col">
<div class="goods-img" @click="navigateToDetailPage(item)">
<u-image width="230rpx" mode="aspectFit" border-radius='16' height="230rpx" :src="item.thumbnail">
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</div>
<div class="goods-detail">
@@ -94,114 +94,32 @@
</view>
</template>
<script>
import commonTpl from '@/components/m-goods-list/common'
export default {
data() {
return {
lightColor: this.$mainColor
}
},
mixins: [commonTpl],
props: {
// 遍历的数据
res: {
type: Array,
default: () => {
return []
}
},
// 一行两列还是一行一列显示
type: {
type: String,
default: 'twoColumns',
validator() {
return ['twoColumns', 'oneColumns']
}
},
storeName: {
type: Boolean,
default: true
},
tabBarGap: {
type: Boolean,
default: true
},
keyword: {
type: null,
default: ''
}
<script setup lang="ts">
import { watch } from 'vue'
import { useGoodsListCommon } from './common'
import { goodsFormatPrice } from '@/utils/filters.js'
},
watch: {
keyword(val) {
if (val) {
this.lightSearchStr(val)
}
}
},
methods: {
const { lightSearchStr, getPromotion, navigateToDetailPage, navigateToStoreDetailPage } = useGoodsListCommon()
// 高亮显示搜索内容
lightSearchStr(keyword, str) {
if (!keyword) {
return str
} else {
let unicodes = '';
for (let i of Array.from(keyword)) {
unicodes += this.unicode(i) + "|"
}
const rule = '(' + unicodes + ')'
const reg = new RegExp(rule, 'gi');
return str ? str.replace(reg, matchValue =>
`<span style="color:${this.lightColor}">${matchValue}</span>`
) : ''
}
},
// 转换为unicode
unicode(str) {
var value = '';
for (var i = 0; i < str.length; i++) {
value += '\\u' + this.left_zero_4(parseInt(str.charCodeAt(i)).toString(16));
}
return value;
},
left_zero_4(str) {
if (str != null && str != '' && str != 'undefined') {
if (str.length == 2) {
return '00' + str;
}
}
return str;
},
// 数据去重一下 只显示一次 减免 劵 什么的
getPromotion(item) {
if (item ? item.promotionMap : item.promotionMap) {
const fieldList = item ? item.promotionMap : item.promotionMap
let array = [];
Object.keys(fieldList).forEach((child) => {
if (!array.includes(child.split("-")[0])) {
array.push(child.split("-")[0]);
}
});
return array;
}
},
// 跳转到商品详情
navigateToDetailPage(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
// 跳转地址
navigateToStoreDetailPage(item) {
uni.navigateTo({
url: `/pages/product/shopPage?id=${item.storeId}`,
});
},
}
const props = withDefaults(defineProps<{
res?: any[]
type?: string
storeName?: boolean
tabBarGap?: boolean
keyword?: string | null
}>(), {
res: () => [],
type: 'twoColumns',
storeName: true,
tabBarGap: true,
keyword: ''
})
watch(() => props.keyword, (val) => {
if (val) {
lightSearchStr(val as string, '')
}
})
</script>
<style lang='scss' scoped>
@@ -212,6 +130,7 @@
.goods-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin: 10rpx 20rpx 284rpx;
width: calc(100% - 40rpx);
box-sizing: border-box;
@@ -219,21 +138,17 @@
&.goods-list--embedded {
margin-bottom: 20rpx;
}
>.goods-item {
background-color: #ffffff;
display: flex;
border-radius: 16rpx;
flex-direction: column;
width: calc(50% - 30rpx);
width: calc(50% - 10rpx);
margin-bottom: 20rpx;
padding-bottom: 20rpx;
&:nth-child(2n + 1) {
margin-right: 20rpx;
}
.image-wrapper {
width: 100%;
height: 330rpx;
@@ -242,12 +157,12 @@
padding: 0;
}
}
.count-config,
.store-seller-name {
font-size: $font-sm;
}
.store-seller-name {
color: #666;
display: flex;
@@ -322,7 +237,7 @@
.goods-detail {
margin: 0 20rpx;
>.title {
font-size: $font-base;
color: $font-color-dark;
@@ -334,7 +249,7 @@
-webkit-line-clamp: 2;
overflow: hidden;
}
.promotion {
margin-top: 4rpx;
display: flex;
@@ -344,10 +259,10 @@
color: $light-color;
margin-right: 10rpx;
padding: 0 4rpx;
border-radius: 2rpx;
border-radius: 2px;
}
}
.store-seller-name {
color: #666;
display: flex;
@@ -409,7 +324,7 @@
padding-right: 10rpx;
font-size: 24rpx;
color: $font-color-light;
>.price {
font-size: 26rpx;
line-height: 1;
@@ -421,6 +336,5 @@
}
}
}
}
</style>

View File

@@ -4,7 +4,7 @@
<div class="flex goods-col">
<div class="goods-img">
<u-image width="230rpx" mode="aspectFit" border-radius='16' height="230rpx" :src="item.goodsImage || item.thumbnail">
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</div>
<div class="goods-detail">
@@ -43,43 +43,33 @@
</div>
</template>
<script>
import commonTpl from '@/components/m-goods-list/common'
export default {
data() {
return {
lightColor: this.$mainColor,
}
},
mixins: [commonTpl],
props: {
// 遍历的数据
res: {
type: Array,
default: () => {
return []
}
},
type:{
type:null,
default:""
}
},
methods: {
// 跳转到商品详情
navigateToDetailPage(item) {
if(this.type == 'kanJia'){
uni.navigateTo({
url: `/pages/promotion/bargain/detail?id=${item.id}`,
});
return
}
uni.navigateTo({
url: `/pages/product/goods?id=${item.skuId}&goodsId=${item.goodsId}`,
});
},
}
<script setup lang="ts">
import { useGoodsListCommon } from './common'
import { goodsFormatPrice } from '@/utils/filters.js'
const { navigateToDetailPage } = useGoodsListCommon()
const props = withDefaults(defineProps<{
res?: any[]
type?: string | null
}>(), {
res: () => [],
type: ''
})
// 跳转到商品详情(覆盖公共方法,增加砍价跳转)
const navigateToDetailPageOverride = (item: any) => {
if(props.type == 'kanJia'){
uni.navigateTo({
url: `/pages/promotion/bargain/detail?id=${item.id}`,
})
return
}
navigateToDetailPage({
id: item.skuId,
goodsId: item.goodsId,
})
}
</script>
<style lang='scss' scoped>
@@ -87,72 +77,72 @@
width: 152rpx;
height: 108rpx;
}
.flex-j-sb {
.flex-j-sb {
width: 100%;
}
.goods-row {
background: #fff;
padding: 16rpx;
>.goods-col {
display: flex;
>.goods-img {
overflow: hidden;
flex: 4;
}
>.goods-detail {
flex: 7;
}
}
}
.goods-detail {
margin: 0 20rpx;
>.title {
font-size: $font-base;
color: $font-color-dark;
line-height: 1.5;
height: 86rpx;
padding: 10rpx 0 0;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.count-config {
padding: 5rpx 0;
color: #666;
display: flex;
font-size: 24rpx;
letter-spacing:2rpx;
padding-left: 10rpx;
.count-config-text {
font-size: 24rpx;
}
}
}
.price-box {
margin-top: 10rpx;
.goods-row {
background: #fff;
padding: 16rpx;
>.goods-col {
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10rpx;
font-size: 24rpx;
color: $font-color-light;
>.price {
font-size: 26rpx;
line-height: 1;
color: $main-color;
font-weight: bold;
.price-int {
font-size: 48rpx;
}
>.goods-img {
overflow: hidden;
flex: 4;
}
>.goods-detail {
flex: 7;
}
}
}
.goods-detail {
margin: 0 20rpx;
>.title {
font-size: $font-base;
color: $font-color-dark;
line-height: 1.5;
height: 86rpx;
padding: 10rpx 0 0;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.count-config {
padding: 5rpx 0;
color: #666;
display: flex;
font-size: 24rpx;
letter-spacing:2rpx;
padding-left: 10rpx;
.count-config-text {
font-size: 24rpx;
}
}
}
.price-box {
margin-top: 10rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10rpx;
font-size: 24rpx;
color: $font-color-light;
>.price {
font-size: 26rpx;
line-height: 1;
color: $main-color;
font-weight: bold;
.price-int {
font-size: 48rpx;
}
}
}
</style>