19 changed files with 4387 additions and 2268 deletions
@ -0,0 +1,253 @@ |
|||||
|
# 省市区选择组件使用说明 |
||||
|
|
||||
|
## 概述 |
||||
|
|
||||
|
`AreaPicker.vue` 是一个功能完整的省市区三级联动选择组件,支持自定义样式插槽,可以灵活地集成到各种页面中。 |
||||
|
|
||||
|
## 功能特性 |
||||
|
|
||||
|
- ✅ 省市区三级联动选择 |
||||
|
- ✅ 支持默认值设置 |
||||
|
- ✅ 支持插槽自定义显示样式 |
||||
|
- ✅ 完整的事件回调 |
||||
|
- ✅ 灵活的数据请求方式 |
||||
|
- ✅ 错误处理和容错机制 |
||||
|
|
||||
|
## Props |
||||
|
|
||||
|
| 参数 | 类型 | 默认值 | 说明 | |
||||
|
|------|------|--------|------| |
||||
|
| placeholder | String | '请选择' | 占位符文本 | |
||||
|
| defaultValue | Object | null | 默认选中值,格式:`{ provinceId, cityId, areaId }` | |
||||
|
| disabled | Boolean | false | 是否禁用 | |
||||
|
|
||||
|
## Events |
||||
|
|
||||
|
| 事件名 | 参数 | 说明 | |
||||
|
|--------|------|------| |
||||
|
| change | data | 选择改变时触发,返回选中的省市区信息 | |
||||
|
|
||||
|
### change 事件返回数据格式 |
||||
|
|
||||
|
```javascript |
||||
|
{ |
||||
|
provinceId: '110000', // 省份ID |
||||
|
cityId: '110100', // 城市ID |
||||
|
areaId: '110101', // 区域ID |
||||
|
fullText: '北京市北京市东城区' // 完整地址文本 |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Methods |
||||
|
|
||||
|
| 方法名 | 参数 | 返回值 | 说明 | |
||||
|
|--------|------|--------|------| |
||||
|
| getValue | - | Object | 获取当前选中的值 | |
||||
|
| reset | - | - | 重置选择 | |
||||
|
|
||||
|
## 插槽使用 |
||||
|
|
||||
|
组件提供了默认插槽,允许完全自定义显示样式。插槽提供以下数据: |
||||
|
|
||||
|
| 插槽参数 | 类型 | 说明 | |
||||
|
|----------|------|------| |
||||
|
| selectedText | String | 当前选中的完整文本 | |
||||
|
| placeholder | String | 占位符文本 | |
||||
|
| provinceData | Array | 省份数据列表 | |
||||
|
| cityData | Array | 城市数据列表 | |
||||
|
| areaData | Array | 区域数据列表 | |
||||
|
| multiIndex | Array | 当前选中的索引数组 | |
||||
|
| currentSelection | Object | 当前选中的详细信息 | |
||||
|
|
||||
|
### currentSelection 对象结构 |
||||
|
|
||||
|
```javascript |
||||
|
{ |
||||
|
province: { id: '110000', name: '北京市' }, |
||||
|
city: { id: '110100', name: '北京市' }, |
||||
|
area: { id: '110101', name: '东城区' }, |
||||
|
fullText: '北京市北京市东城区' |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 使用示例 |
||||
|
|
||||
|
### 基本使用 |
||||
|
|
||||
|
```vue |
||||
|
<template> |
||||
|
<view> |
||||
|
<!-- 使用默认样式 --> |
||||
|
<AreaPicker |
||||
|
ref="areaPicker" |
||||
|
placeholder="请选择省市区" |
||||
|
:defaultValue="{ provinceId: '110000', cityId: '110100', areaId: '110101' }" |
||||
|
@change="onAreaChange" |
||||
|
/> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
methods: { |
||||
|
onAreaChange(data) { |
||||
|
console.log('选中的地区:', data) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
``` |
||||
|
|
||||
|
### 自定义样式(插槽) |
||||
|
|
||||
|
```vue |
||||
|
<template> |
||||
|
<view> |
||||
|
<!-- 自定义样式1:简单自定义 --> |
||||
|
<AreaPicker |
||||
|
placeholder="请选择省市区" |
||||
|
@change="onAreaChange" |
||||
|
> |
||||
|
<template v-slot="{ selectedText, placeholder }"> |
||||
|
<view class="custom-display"> |
||||
|
<text class="icon">📍</text> |
||||
|
<text class="text">{{ selectedText || placeholder }}</text> |
||||
|
<text class="arrow">▼</text> |
||||
|
</view> |
||||
|
</template> |
||||
|
</AreaPicker> |
||||
|
|
||||
|
<!-- 自定义样式2:显示详细信息 --> |
||||
|
<AreaPicker |
||||
|
placeholder="请选择省市区" |
||||
|
@change="onAreaChange" |
||||
|
> |
||||
|
<template v-slot="{ selectedText, placeholder, currentSelection }"> |
||||
|
<view class="detail-display"> |
||||
|
<view v-if="currentSelection.province" class="detail-info"> |
||||
|
<text>省:{{ currentSelection.province.name }}</text> |
||||
|
<text>市:{{ currentSelection.city.name }}</text> |
||||
|
<text>区:{{ currentSelection.area.name }}</text> |
||||
|
</view> |
||||
|
<text v-else class="placeholder">{{ placeholder }}</text> |
||||
|
</view> |
||||
|
</template> |
||||
|
</AreaPicker> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
methods: { |
||||
|
onAreaChange(data) { |
||||
|
console.log('选中的地区:', data) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.custom-display { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 12px 16px; |
||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
border-radius: 25px; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.icon { |
||||
|
margin-right: 10px; |
||||
|
} |
||||
|
|
||||
|
.text { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.arrow { |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
|
||||
|
.detail-display { |
||||
|
padding: 15px; |
||||
|
background-color: #f8f9fa; |
||||
|
border: 2px dashed #dee2e6; |
||||
|
border-radius: 8px; |
||||
|
} |
||||
|
|
||||
|
.detail-info { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 5px; |
||||
|
} |
||||
|
|
||||
|
.placeholder { |
||||
|
color: #adb5bd; |
||||
|
font-style: italic; |
||||
|
} |
||||
|
</style> |
||||
|
``` |
||||
|
|
||||
|
### 在 header 组件中使用 |
||||
|
|
||||
|
```vue |
||||
|
<template> |
||||
|
<view> |
||||
|
<header |
||||
|
:isSearch="true" |
||||
|
:isAreaPicker="true" |
||||
|
areaPlaceholder="请选择省市区" |
||||
|
:defaultAreaValue="{ provinceId: '110000', cityId: '110100', areaId: '110101' }" |
||||
|
@areaChange="onAreaChange" |
||||
|
/> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
methods: { |
||||
|
onAreaChange(data) { |
||||
|
console.log('选中的地区:', data) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
``` |
||||
|
|
||||
|
## 数据接口要求 |
||||
|
|
||||
|
组件需要后端提供省市区数据接口,接口返回格式如下: |
||||
|
|
||||
|
```javascript |
||||
|
// 省份数据 |
||||
|
[ |
||||
|
{ id: '110000', name: '北京市' }, |
||||
|
{ id: '120000', name: '天津市' }, |
||||
|
// ... |
||||
|
] |
||||
|
|
||||
|
// 城市数据(根据省份ID获取) |
||||
|
[ |
||||
|
{ id: '110100', name: '北京市', pid: '110000' }, |
||||
|
// ... |
||||
|
] |
||||
|
|
||||
|
// 区域数据(根据城市ID获取) |
||||
|
[ |
||||
|
{ id: '110101', name: '东城区', pid: '110100' }, |
||||
|
{ id: '110102', name: '西城区', pid: '110100' }, |
||||
|
// ... |
||||
|
] |
||||
|
``` |
||||
|
|
||||
|
## 注意事项 |
||||
|
|
||||
|
1. 组件会自动处理数据请求的多种方式(父组件 Post 方法、全局 Post 方法、uni.request) |
||||
|
2. 建议在使用前确保数据接口正常可用 |
||||
|
3. 插槽内容会完全替换默认的显示样式 |
||||
|
4. 可以通过 ref 调用组件的 getValue() 和 reset() 方法 |
||||
|
5. 组件支持设置默认值,会自动回显对应的省市区信息 |
||||
|
|
||||
|
## 完整示例 |
||||
|
|
||||
|
查看 `AreaPickerSlotExample.vue` 文件获取完整的使用示例,包含多种自定义样式的演示。 |
@ -0,0 +1,429 @@ |
|||||
|
<template> |
||||
|
<view class="area-picker"> |
||||
|
<picker |
||||
|
mode="multiSelector" |
||||
|
:range="newProvinceDataList" |
||||
|
range-key="name" |
||||
|
@change="changeArea" |
||||
|
@columnchange="pickerColumnchange" |
||||
|
:value="multiIndex" |
||||
|
> |
||||
|
<!-- 使用插槽,允许父组件自定义显示样式 --> |
||||
|
<slot |
||||
|
:selectedText="selectedText" |
||||
|
:placeholder="placeholder" |
||||
|
:provinceData="newProvinceDataList[0]" |
||||
|
:cityData="newProvinceDataList[1]" |
||||
|
:areaData="newProvinceDataList[2]" |
||||
|
:multiIndex="multiIndex" |
||||
|
:currentSelection="getCurrentSelection()" |
||||
|
> |
||||
|
<!-- 默认显示样式 --> |
||||
|
<view class="picker-display"> |
||||
|
<text class="picker-text" :class="{ 'placeholder': !selectedText }">{{ selectedText || placeholder }}</text> |
||||
|
<image |
||||
|
class="dropdown-icon" |
||||
|
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iOCIgdmlld0JveD0iMCAwIDEyIDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0xIDFMNiA2TDExIDEiIHN0cm9rZT0iIzk5OTk5OSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+" |
||||
|
mode="heightFix" |
||||
|
></image> |
||||
|
</view> |
||||
|
</slot> |
||||
|
</picker> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'AreaPicker', |
||||
|
props: { |
||||
|
// 占位符文本 |
||||
|
placeholder: { |
||||
|
type: String, |
||||
|
default: '请选择地区' |
||||
|
}, |
||||
|
// 默认选中的省市区ID |
||||
|
defaultValue: { |
||||
|
type: Object, |
||||
|
default: () => ({ |
||||
|
provinceId: null, |
||||
|
cityId: null, |
||||
|
areaId: null |
||||
|
}) |
||||
|
}, |
||||
|
// 是否禁用 |
||||
|
disabled: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
// 省市区数据 |
||||
|
columns: [], |
||||
|
newProvinceDataList: [[], [], []], |
||||
|
multiIndex: [0, 0, 0], |
||||
|
provinceId: null, |
||||
|
cityId: null, |
||||
|
areaId: null, |
||||
|
ready: false, |
||||
|
selectedText: '', |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.getSeldCityList() |
||||
|
}, |
||||
|
watch: { |
||||
|
defaultValue: { |
||||
|
handler(newVal) { |
||||
|
console.log('----有值回显') |
||||
|
if (newVal && newVal.provinceId && this.ready) { |
||||
|
this.setDefaultValue(newVal) |
||||
|
} |
||||
|
}, |
||||
|
deep: true, |
||||
|
immediate: true |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 获取省市区数据 |
||||
|
getSeldCityList() { |
||||
|
// 尝试多种方式获取数据 |
||||
|
let requestMethod = null; |
||||
|
|
||||
|
// 1. 尝试使用父组件的 Post 方法 |
||||
|
if (this.$parent && this.$parent.Post) { |
||||
|
requestMethod = this.$parent.Post; |
||||
|
} |
||||
|
// 2. 尝试使用全局的 Post 方法(如果通过 mixin 引入) |
||||
|
else if (this.Post) { |
||||
|
requestMethod = this.Post; |
||||
|
} |
||||
|
// 3. 尝试使用 uni.request |
||||
|
else { |
||||
|
this.requestWithUni(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
requestMethod({}, '/api/areas/getAll').then(res => { |
||||
|
if (res) { |
||||
|
this.processAreaData(res.data) |
||||
|
} |
||||
|
}).catch(err => { |
||||
|
console.warn('省市区数据获取失败:', err) |
||||
|
// 可以在这里设置一些默认数据或提示用户 |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 使用 uni.request 获取数据的备用方法 |
||||
|
requestWithUni() { |
||||
|
uni.request({ |
||||
|
url: '/api/areas/getAll', // 这里需要根据实际的完整URL调整 |
||||
|
method: 'POST', |
||||
|
data: {}, |
||||
|
success: (res) => { |
||||
|
if (res.data) { |
||||
|
this.processAreaData(res.data.data || res.data) |
||||
|
} |
||||
|
}, |
||||
|
fail: (err) => { |
||||
|
console.warn('省市区数据获取失败:', err) |
||||
|
// 可以提供一些默认的省市区数据或提示用户 |
||||
|
uni.showToast({ |
||||
|
title: '地区数据加载失败', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 处理省市区数据 |
||||
|
processAreaData(data) { |
||||
|
var result = {} |
||||
|
for (var i = 0; i < data.length; i++) { |
||||
|
var item = data[i] |
||||
|
if (item.parent_id == 0) { |
||||
|
continue |
||||
|
} |
||||
|
// 获取省 |
||||
|
if (item.parent_id == "1") { |
||||
|
result[item.id.toString()] = {} |
||||
|
result[item.id.toString()].children = [] |
||||
|
result[item.id.toString()].name = item.name |
||||
|
result[item.id.toString()].id = item.id |
||||
|
} else if (result[item.parent_id.toString()]) { |
||||
|
// 填充市 |
||||
|
var t = { |
||||
|
id: item.id, |
||||
|
name: item.name, |
||||
|
children: [] |
||||
|
} |
||||
|
result[item.parent_id.toString()].children.push(t) |
||||
|
} else { |
||||
|
// 填充区 |
||||
|
var k = { |
||||
|
id: item.id, |
||||
|
name: item.name |
||||
|
} |
||||
|
for (var j = 0; j < result[item.parent_id.toString().substr(0, 2) + "0000"].children.length; j++) { |
||||
|
if (result[item.parent_id.toString().substr(0, 2) + "0000"].children[j].id == item.parent_id) { |
||||
|
result[item.parent_id.toString().substr(0, 2) + "0000"].children[j].children.push(k) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var r = [] |
||||
|
// 将Object转为Array |
||||
|
for (var i in result) { |
||||
|
r.push(result[i]) |
||||
|
} |
||||
|
|
||||
|
this.columns = r |
||||
|
this.initPickerData() |
||||
|
this.ready = true |
||||
|
|
||||
|
// 如果有默认值,设置默认选中 |
||||
|
if (this.defaultValue && this.defaultValue.provinceId) { |
||||
|
this.setDefaultValue(this.defaultValue) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 初始化picker数据 |
||||
|
initPickerData() { |
||||
|
if (this.columns.length === 0) return |
||||
|
|
||||
|
// 初始化省份数据 |
||||
|
this.newProvinceDataList[0] = this.columns.map(item => ({ |
||||
|
name: item.name, |
||||
|
id: item.id |
||||
|
})) |
||||
|
|
||||
|
// 初始化城市数据(默认第一个省份的城市) |
||||
|
if (this.columns[0] && this.columns[0].children) { |
||||
|
this.newProvinceDataList[1] = this.columns[0].children.map(item => ({ |
||||
|
name: item.name, |
||||
|
id: item.id |
||||
|
})) |
||||
|
} |
||||
|
|
||||
|
// 初始化区县数据(默认第一个城市的区县) |
||||
|
if (this.columns[0] && this.columns[0].children[0] && this.columns[0].children[0].children) { |
||||
|
this.newProvinceDataList[2] = this.columns[0].children[0].children.map(item => ({ |
||||
|
name: item.name, |
||||
|
id: item.id |
||||
|
})) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 设置默认值 |
||||
|
setDefaultValue(defaultValue) { |
||||
|
if (!this.ready || !defaultValue) return |
||||
|
|
||||
|
// 查找省份索引 |
||||
|
const provinceIndex = this.newProvinceDataList[0].findIndex(item => item.id == defaultValue.provinceId) |
||||
|
if (provinceIndex === -1) return |
||||
|
|
||||
|
this.multiIndex[0] = provinceIndex |
||||
|
|
||||
|
// 更新城市数据 |
||||
|
this.newProvinceDataList[1] = this.columns[provinceIndex].children.map(item => ({ |
||||
|
name: item.name, |
||||
|
id: item.id |
||||
|
})) |
||||
|
|
||||
|
// 查找城市索引 |
||||
|
const cityIndex = this.newProvinceDataList[1].findIndex(item => item.id == defaultValue.cityId) |
||||
|
if (cityIndex !== -1) { |
||||
|
this.multiIndex[1] = cityIndex |
||||
|
|
||||
|
// 更新区县数据 |
||||
|
this.newProvinceDataList[2] = this.columns[provinceIndex].children[cityIndex].children.map(item => ({ |
||||
|
name: item.name, |
||||
|
id: item.id |
||||
|
})) |
||||
|
|
||||
|
// 查找区县索引 |
||||
|
const areaIndex = this.newProvinceDataList[2].findIndex(item => item.id == defaultValue.areaId) |
||||
|
if (areaIndex !== -1) { |
||||
|
this.multiIndex[2] = areaIndex |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.updateSelectedText() |
||||
|
}, |
||||
|
|
||||
|
// 选择完成事件 |
||||
|
changeArea(e) { |
||||
|
this.multiIndex = e.detail.value |
||||
|
this.updateSelectedText() |
||||
|
this.emitChange() |
||||
|
}, |
||||
|
|
||||
|
// 列滑动事件 |
||||
|
pickerColumnchange(e) { |
||||
|
const column = e.detail.column |
||||
|
const value = e.detail.value |
||||
|
|
||||
|
if (column === 0) { |
||||
|
// 第一列滑动(省份) |
||||
|
this.multiIndex[0] = value |
||||
|
|
||||
|
// 更新城市数据 |
||||
|
this.newProvinceDataList[1] = this.columns[this.multiIndex[0]].children.map(item => ({ |
||||
|
name: item.name, |
||||
|
id: item.id |
||||
|
})) |
||||
|
|
||||
|
// 更新区县数据 |
||||
|
if (this.columns[this.multiIndex[0]].children.length === 1) { |
||||
|
this.newProvinceDataList[2] = this.columns[this.multiIndex[0]].children[0].children.map(item => ({ |
||||
|
name: item.name, |
||||
|
id: item.id |
||||
|
})) |
||||
|
} else { |
||||
|
this.newProvinceDataList[2] = this.columns[this.multiIndex[0]].children[this.multiIndex[1]].children.map(item => ({ |
||||
|
name: item.name, |
||||
|
id: item.id |
||||
|
})) |
||||
|
} |
||||
|
|
||||
|
// 重置城市和区县索引 |
||||
|
this.multiIndex.splice(1, 1, 0) |
||||
|
this.multiIndex.splice(2, 1, 0) |
||||
|
} else if (column === 1) { |
||||
|
// 第二列滑动(城市) |
||||
|
this.multiIndex[1] = value |
||||
|
|
||||
|
// 更新区县数据 |
||||
|
this.newProvinceDataList[2] = this.columns[this.multiIndex[0]].children[this.multiIndex[1]].children.map(item => ({ |
||||
|
name: item.name, |
||||
|
id: item.id |
||||
|
})) |
||||
|
|
||||
|
// 重置区县索引 |
||||
|
this.multiIndex.splice(2, 1, 0) |
||||
|
} else if (column === 2) { |
||||
|
// 第三列滑动(区县) |
||||
|
this.multiIndex[2] = value |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 更新选中的省市区ID和显示文本 |
||||
|
updateSelectedText() { |
||||
|
if (this.newProvinceDataList[0][this.multiIndex[0]] && |
||||
|
this.newProvinceDataList[1][this.multiIndex[1]] && |
||||
|
this.newProvinceDataList[2][this.multiIndex[2]]) { |
||||
|
|
||||
|
this.selectedText = |
||||
|
this.newProvinceDataList[1][this.multiIndex[1]].name |
||||
|
|
||||
|
this.provinceId = this.newProvinceDataList[0][this.multiIndex[0]].id |
||||
|
this.cityId = this.newProvinceDataList[1][this.multiIndex[1]].id |
||||
|
this.areaId = this.newProvinceDataList[2][this.multiIndex[2]].id |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 触发change事件 |
||||
|
emitChange() { |
||||
|
const selectedText = this.newProvinceDataList[0][this.multiIndex[0]].name + |
||||
|
this.newProvinceDataList[1][this.multiIndex[1]].name + |
||||
|
this.newProvinceDataList[2][this.multiIndex[2]].name |
||||
|
|
||||
|
const result = { |
||||
|
provinceId: this.provinceId, |
||||
|
cityId: this.cityId, |
||||
|
areaId: this.areaId, |
||||
|
province: this.newProvinceDataList[0][this.multiIndex[0]]?.name || '', |
||||
|
city: this.newProvinceDataList[1][this.multiIndex[1]]?.name || '', |
||||
|
area: this.newProvinceDataList[2][this.multiIndex[2]]?.name || '', |
||||
|
fullText: selectedText |
||||
|
} |
||||
|
|
||||
|
this.$emit('change', result) |
||||
|
}, |
||||
|
|
||||
|
// 获取当前选中值 |
||||
|
getValue() { |
||||
|
const selectedText = this.newProvinceDataList[0][this.multiIndex[0]]?.name + |
||||
|
this.newProvinceDataList[1][this.multiIndex[1]]?.name + |
||||
|
this.newProvinceDataList[2][this.multiIndex[2]]?.name |
||||
|
|
||||
|
return { |
||||
|
provinceId: this.provinceId, |
||||
|
cityId: this.cityId, |
||||
|
areaId: this.areaId, |
||||
|
provinceName: this.newProvinceDataList[0][this.multiIndex[0]]?.name || '', |
||||
|
cityName: this.newProvinceDataList[1][this.multiIndex[1]]?.name || '', |
||||
|
areaName: this.newProvinceDataList[2][this.multiIndex[2]]?.name || '', |
||||
|
fullText: selectedText |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 重置选择 |
||||
|
reset() { |
||||
|
this.multiIndex = [0, 0, 0] |
||||
|
this.selectedText = '' |
||||
|
this.provinceId = null |
||||
|
this.cityId = null |
||||
|
this.areaId = null |
||||
|
this.initPickerData() |
||||
|
}, |
||||
|
|
||||
|
// 获取当前选中的详细信息(供插槽使用) |
||||
|
getCurrentSelection() { |
||||
|
if (!this.newProvinceDataList[0][this.multiIndex[0]] || |
||||
|
!this.newProvinceDataList[1][this.multiIndex[1]] || |
||||
|
!this.newProvinceDataList[2][this.multiIndex[2]]) { |
||||
|
return { |
||||
|
province: null, |
||||
|
city: null, |
||||
|
area: null, |
||||
|
fullText: '' |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
province: this.newProvinceDataList[0][this.multiIndex[0]], |
||||
|
city: this.newProvinceDataList[1][this.multiIndex[1]], |
||||
|
area: this.newProvinceDataList[2][this.multiIndex[2]], |
||||
|
fullText: this.newProvinceDataList[0][this.multiIndex[0]].name + |
||||
|
this.newProvinceDataList[1][this.multiIndex[1]].name + |
||||
|
this.newProvinceDataList[2][this.multiIndex[2]].name |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.area-picker { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.picker-display { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
padding: 8rpx 16rpx; |
||||
|
border: 2rpx solid #e0e0e0; |
||||
|
border-radius: 8rpx; |
||||
|
background-color: #fff; |
||||
|
min-height: 60rpx; |
||||
|
} |
||||
|
|
||||
|
.picker-text { |
||||
|
font-size: 30rpx; |
||||
|
color: #333; |
||||
|
flex: 1; |
||||
|
|
||||
|
&.placeholder { |
||||
|
color: #999; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.dropdown-icon { |
||||
|
width: 24rpx; |
||||
|
height: 16rpx; |
||||
|
margin-left: 8rpx; |
||||
|
} |
||||
|
</style> |
File diff suppressed because it is too large
@ -1,230 +1,240 @@ |
|||||
<template> |
<template> |
||||
<view class="bg"> |
<view class="bg"> |
||||
<headerVue :selectedLocation="selectedLocation" fixed isLocation></headerVue> |
<headerVue @change="changeAddress" :address="addressInfo" fixed isLocation></headerVue> |
||||
|
|
||||
<!-- 灵动岛组件 --> |
<!-- 灵动岛组件 --> |
||||
<!-- 灵动岛组件 - 自包含,无需传递参数 --> |
<!-- 灵动岛组件 - 自包含,无需传递参数 --> |
||||
<DynamicIsland |
<DynamicIsland ref="dynamicIsland" :page-id="'index_page'" @toggle="handleIslandToggle" |
||||
ref="dynamicIsland" |
@action="handleIslandAction" /> |
||||
:page-id="'index_page'" |
|
||||
@toggle="handleIslandToggle" |
|
||||
@action="handleIslandAction" |
|
||||
/> |
|
||||
|
|
||||
<view class="content" @click="handleContentClick"> |
<view class="content" @click="handleContentClick"> |
||||
<!-- 权益商品区域 --> |
<!-- 权益商品区域 --> |
||||
<ProductSection |
<ProductSection v-if="productList.length" title="权益商品" :productList="productList" |
||||
title="权益商品" |
moreUrl="/subPackages/equityGoods/index" detailUrlPrefix="/subPackages/equityGoods/detail" |
||||
:productList="productList" |
@like-toggle="handleLikeToggle" /> |
||||
moreUrl="/subPackages/equityGoods/index" |
<ProductSection titleBgColor="#92FF8F" aiTagTextColor="#08FB05" aiTagBorderColor="#6EAA3D" title="有感商品" |
||||
detailUrlPrefix="/subPackages/equityGoods/detail" |
:productList="productListFeeling" moreUrl="/pages/index/sensoryStore" |
||||
@like-toggle="handleLikeToggle" |
detailUrlPrefix="/subPackages/techan/detail" @like-toggle="handleLikeToggle" isFeel /> |
||||
/> |
<view class="tab-bar-placeholder"></view> |
||||
<ProductSection |
</view> |
||||
titleBgColor="#92FF8F" |
|
||||
aiTagTextColor="#08FB05" |
|
||||
|
|
||||
aiTagBorderColor="#6EAA3D" |
<CustomTabBar :currentTab="0" /> |
||||
title="有感商品" |
<MusicControl /> |
||||
:productList="productListFeeling" |
</view> |
||||
moreUrl="/pages/index/sensoryStore" |
|
||||
detailUrlPrefix="/subPackages/techan/detail" |
|
||||
@like-toggle="handleLikeToggle" |
|
||||
isFeel |
|
||||
/> |
|
||||
<view class="tab-bar-placeholder"></view> |
|
||||
</view> |
|
||||
|
|
||||
<CustomTabBar :currentTab="0" /> |
|
||||
<MusicControl /> |
|
||||
</view> |
|
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import MusicControl from "@/components/MusicControl.vue"; |
import MusicControl from "@/components/MusicControl.vue"; |
||||
import headerVue from "@/components/header.vue"; |
import headerVue from "@/components/header.vue"; |
||||
import CustomTabBar from "@/components/CustomTabBar.vue"; |
import CustomTabBar from "@/components/CustomTabBar.vue"; |
||||
import DynamicIsland from "@/components/DynamicIsland.vue"; |
import DynamicIsland from "@/components/DynamicIsland.vue"; |
||||
import ProductSection from "@/components/ProductSection.vue"; |
import ProductSection from "@/components/ProductSection.vue"; |
||||
export default { |
export default { |
||||
components: { |
components: { |
||||
CustomTabBar, |
CustomTabBar, |
||||
headerVue, |
headerVue, |
||||
MusicControl, |
MusicControl, |
||||
DynamicIsland, |
DynamicIsland, |
||||
ProductSection, |
ProductSection, |
||||
}, |
}, |
||||
computed: { |
computed: { |
||||
// 其他计算属性可以在这里添加 |
// 其他计算属性可以在这里添加 |
||||
}, |
}, |
||||
data() { |
data() { |
||||
return { |
return { |
||||
topBanner: [], |
topBanner: [], |
||||
productList: [ |
productList: [ |
||||
{ |
// { |
||||
id: 1, |
// id: 1, |
||||
image: |
// image: |
||||
"https://epic.js-dyyj.com/uploads/20250728/58aed304917c9d60761f833c4f8dceb8.png", |
// "https://epic.js-dyyj.com/uploads/20250728/58aed304917c9d60761f833c4f8dceb8.png", |
||||
avatar: |
// avatar: |
||||
"https://epic.js-dyyj.com/uploads/20250728/d27ef6e6c26877da7775664fed376c6f.png", |
// "https://epic.js-dyyj.com/uploads/20250728/d27ef6e6c26877da7775664fed376c6f.png", |
||||
aiName: "文徵明", |
// aiName: "文徵明", |
||||
title: "世界花园 | 研学之旅", |
// title: "世界花园 | 研学之旅", |
||||
price: "588.00", |
// price: "588.00", |
||||
isLiked: true, |
// isLiked: true, |
||||
}, |
// }, |
||||
{ |
// { |
||||
id: 2, |
// id: 2, |
||||
image: |
// image: |
||||
"https://epic.js-dyyj.com/uploads/20250728/00e8704b23a0c9fd57023527146211b9.png", |
// "https://epic.js-dyyj.com/uploads/20250728/00e8704b23a0c9fd57023527146211b9.png", |
||||
avatar: |
// avatar: |
||||
"https://epic.js-dyyj.com/uploads/20250728/d7bf0dd2f3f272afba687b525a7c575c.png", |
// "https://epic.js-dyyj.com/uploads/20250728/d7bf0dd2f3f272afba687b525a7c575c.png", |
||||
aiName: "苏青壳", |
// aiName: "苏青壳", |
||||
title: "生命的扶持 | 风景之旅", |
// title: "生命的扶持 | 风景之旅", |
||||
price: "398.00", |
// price: "398.00", |
||||
isLiked: false, |
// isLiked: false, |
||||
}, |
// }, |
||||
], |
], |
||||
productListFeeling: [ |
productListFeeling: [{ |
||||
{ |
id: 34, |
||||
id: 34, |
image: "https://epic.js-dyyj.com/uploads/20250728/22e319f3feb1b63fbb539d425c51fe70.png", |
||||
image: |
title: "OUT OF SPACE 东方线香", |
||||
"https://epic.js-dyyj.com/uploads/20250728/22e319f3feb1b63fbb539d425c51fe70.png", |
price: "138.00", |
||||
title: "OUT OF SPACE 东方线香", |
isLiked: true, |
||||
price: "138.00", |
isShop: true |
||||
isLiked: true, |
}, |
||||
isShop:true |
{ |
||||
}, |
id: 32, |
||||
{ |
image: "https://epic.js-dyyj.com/uploads/20250728/cc9907153c887a6428283a407928db9a.png", |
||||
id: 32, |
title: "AI-Agent智能玩具", |
||||
image: |
price: "398.00", |
||||
"https://epic.js-dyyj.com/uploads/20250728/cc9907153c887a6428283a407928db9a.png", |
isLiked: false, |
||||
title: "AI-Agent智能玩具", |
}, |
||||
price: "398.00", |
], |
||||
isLiked: false, |
selectedText: '', |
||||
}, |
addressInfo: null, |
||||
], |
}; |
||||
selectedLocation:'' |
}, |
||||
}; |
onLoad() {}, |
||||
}, |
async onReady() { |
||||
onLoad() {}, |
let res = await this.$main.getLocationInfo() |
||||
async onReady() { |
console.log(res) |
||||
let res =await this.$main.getLocationInfo() |
this.addressInfo = res |
||||
console.log(res) |
this.selectedText = res && res.city |
||||
this.selectedLocation = res&&res.city |
uni.setStorageSync('SYS_ADDRESS_INFO', JSON.stringify(res)) |
||||
uni.setStorageSync('SYS_ADDRESS_INFO',JSON.stringify(res)) |
this.getList(); |
||||
this.getList(); |
this.geBenefitPackaget() |
||||
|
|
||||
}, |
}, |
||||
onShow() { |
onShow() { |
||||
this.browse_record({ type: "page", title: "首页" }); |
this.browse_record({ |
||||
this.$nextTick(() =>{ |
type: "page", |
||||
this.$refs.dynamicIsland.getUserInfo(); |
title: "首页" |
||||
}) |
}); |
||||
}, |
this.$nextTick(() => { |
||||
onPageScroll(e) { |
this.$refs.dynamicIsland.getUserInfo(); |
||||
// 只触发带页面ID的事件,避免不同页面间的状态冲突 |
}) |
||||
uni.$emit("pageScroll_index_page", e.scrollTop); |
}, |
||||
}, |
onPageScroll(e) { |
||||
onReachBottom() {}, |
// 只触发带页面ID的事件,避免不同页面间的状态冲突 |
||||
methods: { |
uni.$emit("pageScroll_index_page", e.scrollTop); |
||||
gotoUrlNew(item, index) { |
}, |
||||
if (index == 0) { |
onReachBottom() {}, |
||||
uni.switchTab({ |
methods: { |
||||
url: "/pages/index/readingBody", |
changeAddress(res){ |
||||
}); |
this.addressInfo = res |
||||
} else if (index == 1) { |
uni.setStorageSync('SYS_ADDRESS_INFO', JSON.stringify(res)) |
||||
uni.switchTab({ |
this.geBenefitPackaget() |
||||
url: "/pages/index/intelligentAgent", |
}, |
||||
}); |
gotoUrlNew(item, index) { |
||||
} |
if (index == 0) { |
||||
}, |
uni.switchTab({ |
||||
viewDetail(item) { |
url: "/pages/index/readingBody", |
||||
if (item.url) { |
}); |
||||
uni.navigateTo({ |
} else if (index == 1) { |
||||
url: |
uni.switchTab({ |
||||
"/subPackages/webPage/webPage?url=" + encodeURIComponent(item.url), |
url: "/pages/index/intelligentAgent", |
||||
}); |
}); |
||||
return; |
} |
||||
} |
}, |
||||
uni.navigateTo({ |
viewDetail(item) { |
||||
url: "/subPackages/letter/detail?id=" + item.id, |
if (item.url) { |
||||
}); |
uni.navigateTo({ |
||||
}, |
url: "/subPackages/webPage/webPage?url=" + encodeURIComponent(item.url), |
||||
getList() { |
}); |
||||
// 大轮播 |
return; |
||||
this.Post( |
} |
||||
{ |
uni.navigateTo({ |
||||
type_id: 3, |
url: "/subPackages/letter/detail?id=" + item.id, |
||||
position: 17, |
}); |
||||
}, |
}, |
||||
"/api/adv/getAdv" |
getList() { |
||||
).then((res) => { |
// 大轮播 |
||||
if (res.data) { |
this.Post({ |
||||
this.topBanner = res.data; |
type_id: 3, |
||||
} |
position: 17, |
||||
}); |
}, |
||||
}, |
"/api/adv/getAdv" |
||||
gotoVideo(item) { |
).then((res) => { |
||||
uni.navigateTo({ |
if (res.data) { |
||||
url: |
this.topBanner = res.data; |
||||
"/subPackages/video/video?item=" + |
} |
||||
encodeURIComponent(JSON.stringify(item)), |
}); |
||||
}); |
}, |
||||
}, |
geBenefitPackaget() { |
||||
|
this.Post({ |
||||
|
cityId: this.addressInfo.cityId |
||||
|
}, |
||||
|
"/framework/index/benefitPackage/list", |
||||
|
'DES' |
||||
|
).then((res) => { |
||||
|
if (res.data) { |
||||
|
this.productList = res.data.map(item => { |
||||
|
return { |
||||
|
...item, |
||||
|
image: item.mainUrl, |
||||
|
id: item.benefitPackageId, |
||||
|
avatar: "https://epic.js-dyyj.com/uploads/20250728/d27ef6e6c26877da7775664fed376c6f.png", |
||||
|
aiName: "文徵明", |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
gotoVideo(item) { |
||||
|
uni.navigateTo({ |
||||
|
url: "/subPackages/video/video?item=" + |
||||
|
encodeURIComponent(JSON.stringify(item)), |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
// 处理收藏状态切换 |
// 处理收藏状态切换 |
||||
handleLikeToggle({ item, index }) { |
handleLikeToggle({ |
||||
this.productList[index] = item; |
item, |
||||
}, |
index |
||||
|
}) { |
||||
|
this.productList[index] = item; |
||||
|
}, |
||||
|
|
||||
// 处理灵动岛点击切换 |
// 处理灵动岛点击切换 |
||||
handleIslandToggle(isExpanded) { |
handleIslandToggle(isExpanded) { |
||||
console.log("灵动岛状态切换:", isExpanded ? "展开模式" : "紧凑模式"); |
console.log("灵动岛状态切换:", isExpanded ? "展开模式" : "紧凑模式"); |
||||
}, |
}, |
||||
|
|
||||
// 处理灵动岛操作按钮 |
// 处理灵动岛操作按钮 |
||||
handleIslandAction() { |
handleIslandAction() { |
||||
uni.showToast({ |
uni.showToast({ |
||||
title: "执行操作", |
title: "执行操作", |
||||
icon: "none", |
icon: "none", |
||||
}); |
}); |
||||
console.log("执行操作"); |
console.log("执行操作"); |
||||
}, |
}, |
||||
|
|
||||
// 处理内容区域点击 |
// 处理内容区域点击 |
||||
handleContentClick() { |
handleContentClick() { |
||||
// 点击内容区域时收缩灵动岛 |
// 点击内容区域时收缩灵动岛 |
||||
if (this.$refs.dynamicIsland) { |
if (this.$refs.dynamicIsland) { |
||||
this.$refs.dynamicIsland.collapseIsland(); |
this.$refs.dynamicIsland.collapseIsland(); |
||||
} |
} |
||||
}, |
}, |
||||
}, |
}, |
||||
}; |
}; |
||||
</script> |
</script> |
||||
<style> |
<style> |
||||
page{ |
page {} |
||||
|
|
||||
} |
|
||||
</style> |
</style> |
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.bg { |
.bg { |
||||
min-height: 100vh; |
min-height: 100vh; |
||||
background: #f5f5f5; |
background: #f5f5f5; |
||||
display: flex; |
display: flex; |
||||
flex-direction: column; |
flex-direction: column; |
||||
} |
} |
||||
/* 页面样式 */ |
|
||||
|
|
||||
.content { |
/* 页面样式 */ |
||||
height: calc(100vh - 123rpx); |
|
||||
width: 100%; |
|
||||
padding: 0 20rpx; |
|
||||
box-sizing: border-box; |
|
||||
margin-top: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.tab-bar-placeholder { |
.content { |
||||
height: 143rpx; |
height: calc(100vh - 123rpx); |
||||
width: 100%; |
width: 100%; |
||||
} |
padding: 0 20rpx; |
||||
|
box-sizing: border-box; |
||||
|
margin-top: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.tab-bar-placeholder { |
||||
|
height: 143rpx; |
||||
|
width: 100%; |
||||
|
} |
||||
</style> |
</style> |
File diff suppressed because it is too large
@ -0,0 +1,408 @@ |
|||||
|
<template> |
||||
|
<view class="collection-page"> |
||||
|
<!-- 顶部标题区域 --> |
||||
|
<!-- 商品网格 --> |
||||
|
<view class="products-grid" v-if="collectionList.length > 0"> |
||||
|
<view |
||||
|
class="product-item" |
||||
|
:style="{ |
||||
|
'border-width': |
||||
|
index == collectionList.length - 1 || |
||||
|
index == collectionList.length - 2 |
||||
|
? 0 |
||||
|
: '0.5rpx', |
||||
|
}" |
||||
|
v-for="(item, index) in collectionList" |
||||
|
:key="index" |
||||
|
@click="goToDetail(item)" |
||||
|
> |
||||
|
<image |
||||
|
class="product-image" |
||||
|
src="https://images.unsplash.com/photo-1578662996442-48f60103fc96?auto=format&fit=crop&w=400" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<view class="product-info"> |
||||
|
<view class="product-title">{{ item.title }}</view> |
||||
|
<view class="product-price-box"> |
||||
|
<view class="product-price">{{ item.price }}</view> |
||||
|
<view class=""> |
||||
|
<image |
||||
|
@click.stop="handleUnlikeClick(item, index)" |
||||
|
class="heart-icon" |
||||
|
src="https://epic.js-dyyj.com/uploads/20250728/dd7ed269b24e84a2dd141da6ab980fd6.png" |
||||
|
> |
||||
|
</image> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="product-details"> |
||||
|
<text class="detail-item" |
||||
|
>限量发售{{ item.publishQuantity }}份</text |
||||
|
> |
||||
|
<text class="detail-item" |
||||
|
>{{ item.purchaseQuantity || 0 }}人付款</text |
||||
|
> |
||||
|
<text class="detail-item">浏览{{ item.viewQuantity || 0 }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 空状态展示 --> |
||||
|
<view class="empty-state" v-else> |
||||
|
<view class="empty-content"> |
||||
|
<image |
||||
|
class="empty-icon" |
||||
|
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIHI9IjQ1IiBzdHJva2U9IiNEREREREQiIHN0cm9rZS13aWR0aD0iMiIgZmlsbD0ibm9uZSIvPgo8cGF0aCBkPSJNMzUgNDBINjVNMzUgNTBINjVNMzUgNjBINjUiIHN0cm9rZT0iI0RERERERCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPC9zdmc+" |
||||
|
mode="widthFix" |
||||
|
></image> |
||||
|
<view class="empty-title">暂无收藏</view> |
||||
|
<view class="empty-desc">您还没有收藏任何商品</view> |
||||
|
<view class="empty-tip">去发现更多精彩商品吧</view> |
||||
|
<view class="empty-action" @click="goToDiscover"> |
||||
|
<text class="action-text">去发现</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "UserCollection", |
||||
|
mixins: [require("@/mixins/myMixins.js")], |
||||
|
data() { |
||||
|
return { |
||||
|
collectionList: [], |
||||
|
sortValue: 0, // 0: 最新收藏, 1: 价格排序 |
||||
|
}; |
||||
|
}, |
||||
|
onLoad() { |
||||
|
this.loadCollectionList(); |
||||
|
}, |
||||
|
onShow() { |
||||
|
// 每次显示页面时刷新收藏列表 |
||||
|
this.loadCollectionList(); |
||||
|
}, |
||||
|
methods: { |
||||
|
// 加载收藏列表 |
||||
|
loadCollectionList() { |
||||
|
this.Post( |
||||
|
{ |
||||
|
sortValue: this.sortValue, |
||||
|
}, |
||||
|
"/framework/benefitPackage/collectList", |
||||
|
"DES" |
||||
|
) |
||||
|
.then((res) => { |
||||
|
if (res.data) { |
||||
|
this.collectionList = res.data; |
||||
|
} |
||||
|
}) |
||||
|
.catch((error) => { |
||||
|
console.error("加载收藏列表失败:", error); |
||||
|
// 使用模拟数据 |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 模拟数据(当API不可用时使用) |
||||
|
loadMockData() { |
||||
|
this.collectionList = [ |
||||
|
{ |
||||
|
benefitPackageId: 1, |
||||
|
title: "苏州园林数字藏品", |
||||
|
price: "299", |
||||
|
coverUrl: |
||||
|
"https://images.unsplash.com/photo-1578662996442-48f60103fc96?auto=format&fit=crop&w=400", |
||||
|
publishQuantity: 1000, |
||||
|
purchaseQuantity: 156, |
||||
|
viewQuantity: 2340, |
||||
|
type: true, |
||||
|
}, |
||||
|
{ |
||||
|
benefitPackageId: 2, |
||||
|
title: "江南水乡文化资产", |
||||
|
price: "199", |
||||
|
coverUrl: |
||||
|
"https://images.unsplash.com/photo-1578662996442-48f60103fc96?auto=format&fit=crop&w=400", |
||||
|
publishQuantity: 500, |
||||
|
purchaseQuantity: 89, |
||||
|
viewQuantity: 1200, |
||||
|
type: true, |
||||
|
}, |
||||
|
{ |
||||
|
benefitPackageId: 3, |
||||
|
title: "吴文化数字纪念册", |
||||
|
price: "399", |
||||
|
coverUrl: |
||||
|
"https://images.unsplash.com/photo-1578662996442-48f60103fc96?auto=format&fit=crop&w=400", |
||||
|
publishQuantity: 200, |
||||
|
purchaseQuantity: 45, |
||||
|
viewQuantity: 890, |
||||
|
type: true, |
||||
|
}, |
||||
|
{ |
||||
|
benefitPackageId: 4, |
||||
|
title: "苏州丝绸数字资产", |
||||
|
price: "159", |
||||
|
coverUrl: |
||||
|
"https://images.unsplash.com/photo-1578662996442-48f60103fc96?auto=format&fit=crop&w=400", |
||||
|
publishQuantity: 800, |
||||
|
purchaseQuantity: 234, |
||||
|
viewQuantity: 1560, |
||||
|
type: true, |
||||
|
}, |
||||
|
]; |
||||
|
}, |
||||
|
|
||||
|
// 排序处理 |
||||
|
handleSort(sortType) { |
||||
|
this.sortValue = sortType; |
||||
|
this.loadCollectionList(); |
||||
|
}, |
||||
|
|
||||
|
// 取消收藏 |
||||
|
handleUnlikeClick(item, index) { |
||||
|
uni.showModal({ |
||||
|
title: "取消收藏", |
||||
|
content: "确定要取消收藏这个商品吗?", |
||||
|
success: (res) => { |
||||
|
if (res.confirm) { |
||||
|
this.Post( |
||||
|
{ |
||||
|
packageId: item.benefitPackageId, |
||||
|
type: false, |
||||
|
}, |
||||
|
"/framework/benefitPackage/collect", |
||||
|
"DES" |
||||
|
) |
||||
|
.then((res) => { |
||||
|
// 从列表中移除 |
||||
|
this.collectionList.splice(index, 1); |
||||
|
uni.showToast({ |
||||
|
title: "已取消收藏", |
||||
|
icon: "success", |
||||
|
}); |
||||
|
}) |
||||
|
.catch((error) => { |
||||
|
console.error("取消收藏失败:", error); |
||||
|
// 模拟成功 |
||||
|
this.collectionList.splice(index, 1); |
||||
|
uni.showToast({ |
||||
|
title: "已取消收藏", |
||||
|
icon: "success", |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 跳转到商品详情 |
||||
|
goToDetail(item) { |
||||
|
uni.navigateTo({ |
||||
|
url: `/subPackages/equityGoods/detail?id=${item.benefitPackageId}`, |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 跳转到发现页面 |
||||
|
goToDiscover() { |
||||
|
uni.navigateTo({ |
||||
|
url: "/subPackages/equityGoods/list", |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
page { |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.collection-page { |
||||
|
min-height: 100vh; |
||||
|
background: #f5f5f5; |
||||
|
padding-bottom: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.header-section { |
||||
|
padding: 60rpx 30rpx 30rpx; |
||||
|
|
||||
|
.title { |
||||
|
font-size: 36rpx; |
||||
|
color: #000000; |
||||
|
text-align: center; |
||||
|
margin-bottom: 30rpx; |
||||
|
padding-bottom: 30rpx; |
||||
|
font-weight: 500; |
||||
|
border-bottom: 0.5rpx solid #999999; |
||||
|
} |
||||
|
|
||||
|
.filter-buttons { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
gap: 40rpx; |
||||
|
|
||||
|
.filter-btn { |
||||
|
font-size: 28rpx; |
||||
|
color: #000000; |
||||
|
border-radius: 20rpx; |
||||
|
transition: all 0.3s; |
||||
|
padding: 12rpx 24rpx; |
||||
|
|
||||
|
&.active { |
||||
|
background: #007aff; |
||||
|
color: white; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.products-grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
gap: 20rpx; |
||||
|
padding: 30rpx; |
||||
|
|
||||
|
.product-item { |
||||
|
overflow: hidden; |
||||
|
background: white; |
||||
|
border-radius: 16rpx; |
||||
|
margin-bottom: 20rpx; |
||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08); |
||||
|
transition: all 0.3s ease; |
||||
|
|
||||
|
&:active { |
||||
|
transform: scale(0.98); |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12); |
||||
|
} |
||||
|
|
||||
|
.product-image { |
||||
|
width: 100%; |
||||
|
height: 429rpx; |
||||
|
object-fit: cover; |
||||
|
border-radius: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.product-info { |
||||
|
padding: 20rpx 24rpx 24rpx; |
||||
|
|
||||
|
.product-title { |
||||
|
font-size: 22rpx; |
||||
|
color: #000000; |
||||
|
margin-bottom: 16rpx; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.product-price { |
||||
|
font-size: 36rpx; |
||||
|
font-weight: 500; |
||||
|
color: #000000; |
||||
|
} |
||||
|
|
||||
|
.product-details { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
|
||||
|
.detail-item { |
||||
|
font-size: 18rpx; |
||||
|
color: #808080; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 空状态样式 |
||||
|
.empty-state { |
||||
|
padding: 80rpx 30rpx; |
||||
|
min-height: 80vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
|
||||
|
.empty-content { |
||||
|
text-align: center; |
||||
|
max-width: 400rpx; |
||||
|
|
||||
|
.empty-icon { |
||||
|
width: 120rpx; |
||||
|
height: 120rpx; |
||||
|
margin: 0 auto 40rpx; |
||||
|
opacity: 0.6; |
||||
|
} |
||||
|
|
||||
|
.empty-title { |
||||
|
font-size: 32rpx; |
||||
|
color: #666666; |
||||
|
font-weight: 500; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.empty-desc { |
||||
|
font-size: 26rpx; |
||||
|
color: #999999; |
||||
|
line-height: 1.5; |
||||
|
margin-bottom: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.empty-tip { |
||||
|
font-size: 24rpx; |
||||
|
color: #bbbbbb; |
||||
|
line-height: 1.4; |
||||
|
margin-bottom: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.empty-action { |
||||
|
display: inline-block; |
||||
|
padding: 20rpx 40rpx; |
||||
|
background: #007aff; |
||||
|
border-radius: 40rpx; |
||||
|
transition: all 0.3s; |
||||
|
|
||||
|
&:active { |
||||
|
transform: scale(0.95); |
||||
|
background: #0056cc; |
||||
|
} |
||||
|
|
||||
|
.action-text { |
||||
|
font-size: 28rpx; |
||||
|
color: white; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.heart-icon { |
||||
|
width: 35rpx; |
||||
|
height: 30rpx; |
||||
|
transition: all 0.3s ease; |
||||
|
flex-shrink: 0; |
||||
|
top: -2rpx; |
||||
|
position: relative; |
||||
|
margin-right: 6rpx; |
||||
|
|
||||
|
&.liked { |
||||
|
opacity: 1; |
||||
|
filter: hue-rotate(320deg) saturate(2); |
||||
|
} |
||||
|
|
||||
|
&:active { |
||||
|
transform: scale(1.2); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.product-price-box { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
</style> |
Loading…
Reference in new issue