国家标注,价格筛选
This commit is contained in:
@@ -31,9 +31,23 @@
|
|||||||
<view class="filter-group">
|
<view class="filter-group">
|
||||||
<text class="filter-group-title">价格区间</text>
|
<text class="filter-group-title">价格区间</text>
|
||||||
<view class="filter-price-range">
|
<view class="filter-price-range">
|
||||||
<input class="price-input" placeholder="最低价" type="number" />
|
<input
|
||||||
|
class="price-input"
|
||||||
|
:class="{ 'input-error': minError }"
|
||||||
|
placeholder="最低价"
|
||||||
|
type="number"
|
||||||
|
v-model="minPrice"
|
||||||
|
@blur="onMinBlur"
|
||||||
|
/>
|
||||||
<text class="price-divider">-</text>
|
<text class="price-divider">-</text>
|
||||||
<input class="price-input" placeholder="最高价" type="number" />
|
<input
|
||||||
|
class="price-input"
|
||||||
|
:class="{ 'input-error': maxError }"
|
||||||
|
placeholder="最高价"
|
||||||
|
type="number"
|
||||||
|
v-model="maxPrice"
|
||||||
|
@blur="onMaxBlur"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
@@ -58,7 +72,83 @@ export default {
|
|||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
minPrice: '',
|
||||||
|
maxPrice: '',
|
||||||
|
minError: false,
|
||||||
|
maxError: false
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* 清洗价格输入:只保留数字和小数点(仅一个)
|
||||||
|
*/
|
||||||
|
sanitizePrice(v) {
|
||||||
|
if (v === '' || v === null || v === undefined) return ''
|
||||||
|
var s = String(v).replace(/[^\d.]/g, '')
|
||||||
|
var i = s.indexOf('.')
|
||||||
|
if (i > -1) {
|
||||||
|
s = s.slice(0, i + 1) + s.slice(i + 1).replace(/\./g, '')
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 校验单个价格:非空时必须是 ≥0 的数字,否则提示并清空,返回 false
|
||||||
|
*/
|
||||||
|
validateSingle(key) {
|
||||||
|
var raw = this.sanitizePrice(this[key])
|
||||||
|
this[key] = raw
|
||||||
|
if (raw === '') return true
|
||||||
|
var num = parseFloat(raw)
|
||||||
|
if (isNaN(num) || num < 0) {
|
||||||
|
uni.showToast({ title: '价格不能小于0', icon: 'none' })
|
||||||
|
this[key] = ''
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
flashError(key) {
|
||||||
|
this[key] = true
|
||||||
|
setTimeout(() => { this[key] = false }, 1500)
|
||||||
|
},
|
||||||
|
onMinBlur() {
|
||||||
|
if (!this.validateSingle('minPrice')) return
|
||||||
|
// 最低价 > 已填的最高价 → 最低价钳制到最高价(反向同样不允许越界)
|
||||||
|
var min = parseFloat(this.minPrice)
|
||||||
|
var max = parseFloat(this.maxPrice)
|
||||||
|
if (this.minPrice !== '' && this.maxPrice !== '' && min > max) {
|
||||||
|
this.minPrice = this.maxPrice
|
||||||
|
this.flashError('minError')
|
||||||
|
uni.showToast({ title: '最低价不能大于最高价,已自动调整', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMaxBlur() {
|
||||||
|
if (!this.validateSingle('maxPrice')) return
|
||||||
|
// 最高价 < 已填的最低价 → 冒红并改成最低价
|
||||||
|
var min = parseFloat(this.minPrice)
|
||||||
|
var max = parseFloat(this.maxPrice)
|
||||||
|
if (this.maxPrice !== '' && this.minPrice !== '' && max < min) {
|
||||||
|
this.maxPrice = this.minPrice
|
||||||
|
this.flashError('maxError')
|
||||||
|
uni.showToast({ title: '最高价不能小于最低价,已自动调整', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 点击确定前的价格校验,校验不过则阻止请求
|
||||||
|
*/
|
||||||
|
validatePrices() {
|
||||||
|
if (!this.validateSingle('minPrice')) return false
|
||||||
|
if (!this.validateSingle('maxPrice')) return false
|
||||||
|
var min = parseFloat(this.minPrice)
|
||||||
|
var max = parseFloat(this.maxPrice)
|
||||||
|
if (this.minPrice !== '' && this.maxPrice !== '' && max < min) {
|
||||||
|
this.flashError('maxError')
|
||||||
|
uni.showToast({ title: '最高价不能小于最低价', icon: 'none' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
isSelected(group, tagId) {
|
isSelected(group, tagId) {
|
||||||
return (group.selected || []).indexOf(tagId) > -1
|
return (group.selected || []).indexOf(tagId) > -1
|
||||||
},
|
},
|
||||||
@@ -66,10 +156,18 @@ export default {
|
|||||||
this.$emit('toggle-tag', { groupIndex: gIdx, tagId })
|
this.$emit('toggle-tag', { groupIndex: gIdx, tagId })
|
||||||
},
|
},
|
||||||
onReset() {
|
onReset() {
|
||||||
|
this.minPrice = ''
|
||||||
|
this.maxPrice = ''
|
||||||
|
this.minError = false
|
||||||
|
this.maxError = false
|
||||||
this.$emit('reset')
|
this.$emit('reset')
|
||||||
},
|
},
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
this.$emit('confirm')
|
if (!this.validatePrices()) return
|
||||||
|
this.$emit('confirm', {
|
||||||
|
minPrice: this.minPrice,
|
||||||
|
maxPrice: this.maxPrice
|
||||||
|
})
|
||||||
},
|
},
|
||||||
onClose() {
|
onClose() {
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
@@ -128,6 +226,10 @@ export default {
|
|||||||
}
|
}
|
||||||
.filter-drawer-body {
|
.filter-drawer-body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
/* 框架默认 width:100% + padding 时 content-box 会超宽 64rpx,
|
||||||
|
导致右侧"最高价"输入框被挤出屏幕,必须 border-box */
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
padding: 0 32rpx;
|
padding: 0 32rpx;
|
||||||
}
|
}
|
||||||
.filter-group {
|
.filter-group {
|
||||||
@@ -162,18 +264,26 @@ export default {
|
|||||||
.filter-price-range {
|
.filter-price-range {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20rpx;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.price-input {
|
.price-input {
|
||||||
flex: 1;
|
/* 固定宽度,不依赖 flex 收缩(uni-input 自定义元素收缩行为不可靠) */
|
||||||
|
width: 230rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
height: 72rpx;
|
height: 72rpx;
|
||||||
padding: 0 20rpx;
|
padding: 0 20rpx;
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
|
border: 2rpx solid transparent;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
/* 校验失败冒红 */
|
||||||
|
.price-input.input-error {
|
||||||
|
border-color: #ff0000;
|
||||||
|
background: #fff0f0;
|
||||||
|
}
|
||||||
.price-divider {
|
.price-divider {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #999999;
|
color: #999999;
|
||||||
|
|||||||
@@ -42,10 +42,19 @@
|
|||||||
<!-- 商品信息 -->
|
<!-- 商品信息 -->
|
||||||
<view class="product-info">
|
<view class="product-info">
|
||||||
<text class="product-name">{{ item.name }}</text>
|
<text class="product-name">{{ item.name }}</text>
|
||||||
<!-- 价格 + "起"字 -->
|
<!-- 价格 + "起"字 + 国家标签(未选国家时显示商品所属国家) -->
|
||||||
<view class="price-row">
|
<view class="price-row">
|
||||||
<text class="product-price">¥{{ item.price }}</text>
|
<text class="product-price">¥{{ item.price }}</text>
|
||||||
<text class="price-unit">起</text>
|
<text class="price-unit">起</text>
|
||||||
|
<view class="country-tag" v-if="showCountryTag && item.countryName">
|
||||||
|
<image
|
||||||
|
class="country-tag-icon"
|
||||||
|
:src="item.countryIcon"
|
||||||
|
mode="aspectFill"
|
||||||
|
v-if="item.countryIcon"
|
||||||
|
></image>
|
||||||
|
<text class="country-tag-name">{{ item.countryName }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -75,6 +84,8 @@ export default {
|
|||||||
hasMore: { type: Boolean, default: true },
|
hasMore: { type: Boolean, default: true },
|
||||||
loading: { type: Boolean, default: false },
|
loading: { type: Boolean, default: false },
|
||||||
scrollTopVal: { type: Number, default: 0 },
|
scrollTopVal: { type: Number, default: 0 },
|
||||||
|
// 未选择国家时显示商品所属国家标签
|
||||||
|
showCountryTag: { type: Boolean, default: false },
|
||||||
// 商品滚动区固定高度(px),由父组件动态计算传入
|
// 商品滚动区固定高度(px),由父组件动态计算传入
|
||||||
scrollHeight: { type: Number, default: 400 }
|
scrollHeight: { type: Number, default: 400 }
|
||||||
},
|
},
|
||||||
@@ -216,6 +227,34 @@ export default {
|
|||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
margin-left: 4rpx;
|
margin-left: 4rpx;
|
||||||
}
|
}
|
||||||
|
/* 国家标签 - 价格右侧,国旗图标 + 国家名 */
|
||||||
|
.country-tag {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
margin-left: 12rpx;
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
background: #f7f7f7;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
max-width: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.country-tag-icon {
|
||||||
|
width: 28rpx;
|
||||||
|
height: 20rpx;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.country-tag-name {
|
||||||
|
font-size: 20rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.2;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
/* 底部加载提示 */
|
/* 底部加载提示 */
|
||||||
.load-tip {
|
.load-tip {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
:loading="loadingMore"
|
:loading="loadingMore"
|
||||||
:scroll-top-val="scrollTopVal"
|
:scroll-top-val="scrollTopVal"
|
||||||
:scroll-height="productScrollHeight"
|
:scroll-height="productScrollHeight"
|
||||||
|
:show-country-tag="!selectedCountry"
|
||||||
@sub-select="selectSubCategory"
|
@sub-select="selectSubCategory"
|
||||||
@product-click="goDetail"
|
@product-click="goDetail"
|
||||||
@scroll-to-lower="onScrollToLower"
|
@scroll-to-lower="onScrollToLower"
|
||||||
@@ -114,6 +115,8 @@ export default {
|
|||||||
activeFilters: [],
|
activeFilters: [],
|
||||||
showFilter: false,
|
showFilter: false,
|
||||||
filterGroups: [],
|
filterGroups: [],
|
||||||
|
minPrice: '',
|
||||||
|
maxPrice: '',
|
||||||
pagination: {
|
pagination: {
|
||||||
total: 0,
|
total: 0,
|
||||||
current: 1,
|
current: 1,
|
||||||
@@ -255,7 +258,9 @@ export default {
|
|||||||
id: item.goodId,
|
id: item.goodId,
|
||||||
name: truncateProductTitle(item.goodName),
|
name: truncateProductTitle(item.goodName),
|
||||||
price: item.price,
|
price: item.price,
|
||||||
image: item.image
|
image: item.image,
|
||||||
|
countryName: (item.country && item.country.countryName) || '',
|
||||||
|
countryIcon: (item.country && item.country.countryIcon) ? buildUrl(item.country.countryIcon) : ''
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const total = data.total || 0
|
const total = data.total || 0
|
||||||
@@ -316,6 +321,13 @@ export default {
|
|||||||
if (kw) {
|
if (kw) {
|
||||||
params.keyword = kw
|
params.keyword = kw
|
||||||
}
|
}
|
||||||
|
// 价格区间:两个可单独传,也可同时传
|
||||||
|
if (this.minPrice !== '' && this.minPrice !== null && this.minPrice !== undefined) {
|
||||||
|
params.minPrice = this.minPrice
|
||||||
|
}
|
||||||
|
if (this.maxPrice !== '' && this.maxPrice !== null && this.maxPrice !== undefined) {
|
||||||
|
params.maxPrice = this.maxPrice
|
||||||
|
}
|
||||||
const tagsList = (this.filterGroups || [])
|
const tagsList = (this.filterGroups || [])
|
||||||
.filter((g) => g.selected && g.selected.length > 0)
|
.filter((g) => g.selected && g.selected.length > 0)
|
||||||
.map((g) => ({
|
.map((g) => ({
|
||||||
@@ -514,6 +526,8 @@ export default {
|
|||||||
async clearAllFilters() {
|
async clearAllFilters() {
|
||||||
const hasCountry = this.activeFilters.some((f) => f.key === 'country')
|
const hasCountry = this.activeFilters.some((f) => f.key === 'country')
|
||||||
this.activeFilters = []
|
this.activeFilters = []
|
||||||
|
this.minPrice = ''
|
||||||
|
this.maxPrice = ''
|
||||||
if (hasCountry || this.selectedCountry) {
|
if (hasCountry || this.selectedCountry) {
|
||||||
this.selectedCountry = ''
|
this.selectedCountry = ''
|
||||||
await this.loadCategoryData('')
|
await this.loadCategoryData('')
|
||||||
@@ -532,7 +546,10 @@ export default {
|
|||||||
this.filterGroups = groups
|
this.filterGroups = groups
|
||||||
},
|
},
|
||||||
|
|
||||||
async confirmFilters() {
|
async confirmFilters(payload) {
|
||||||
|
payload = payload || {}
|
||||||
|
this.minPrice = payload.minPrice || ''
|
||||||
|
this.maxPrice = payload.maxPrice || ''
|
||||||
var filters = []
|
var filters = []
|
||||||
var countryFilter = this.activeFilters.find(function (f) { return f.key === 'country' })
|
var countryFilter = this.activeFilters.find(function (f) { return f.key === 'country' })
|
||||||
if (countryFilter) filters.push(countryFilter)
|
if (countryFilter) filters.push(countryFilter)
|
||||||
|
|||||||
Reference in New Issue
Block a user