Browse Source

style

dev_des
1054425342@qq.com 2 months ago
parent
commit
2767b35236
  1. 253
      README_AreaPicker.md
  2. 10
      common/index.js
  3. 429
      components/AreaPicker.vue
  4. 31
      components/ProductSection.vue
  5. 175
      components/header.vue
  6. 14
      manifest.json
  7. 12
      pages.json
  8. 2155
      pages/index/iSoul.vue
  9. 436
      pages/index/index.vue
  10. 6
      pages/index/timeShopBank.vue
  11. 35
      static/js/request.js
  12. 336
      subPackages/equityGoods/detail.vue
  13. 25
      subPackages/equityGoods/index.vue
  14. 1275
      subPackages/equityGoods/list.vue
  15. 793
      subPackages/orderQy/confrim.vue
  16. 123
      subPackages/orderQy/detail.vue
  17. 135
      subPackages/orderQy/list.vue
  18. 408
      subPackages/user/collection.vue
  19. 4
      utils/request.js

253
README_AreaPicker.md

@ -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` 文件获取完整的使用示例,包含多种自定义样式的演示。

10
common/index.js

@ -25,9 +25,13 @@ async function getLocationInfo() {
location, location,
success(response) { success(response) {
console.log(response) console.log(response)
let info = response.result; let info = response.result;
location.province = info.address_component.province; let _c = info.ad_info.adcode.slice(0,3)
location.cityCode = info.ad_info.city_code.slice(3); location.cityId = info.ad_info.city_code.slice(3);
location.provinceId = _c.padEnd(6,'0');
location.areaId = info.ad_info.adcode;
location.province = info.address_component.province;
location.city = info.address_component.city; location.city = info.address_component.city;
location.area = info.address_component.district; location.area = info.address_component.district;
location.street = info.address_component.street; location.street = info.address_component.street;

429
components/AreaPicker.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 = []
// ObjectArray
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>

31
components/ProductSection.vue

@ -50,7 +50,7 @@
<view class="card-title">{{ item.title }}</view> <view class="card-title">{{ item.title }}</view>
<view class="card-price">{{ item.price }}</view> <view class="card-price">{{ item.price }}</view>
<image <image
v-if="!item.isLiked" v-if="!item.type"
class="heart-icon" class="heart-icon"
src="https://epic.js-dyyj.com/uploads/20250728/2f3ae212c01fa3b67be81abc5723cf5c.png" src="https://epic.js-dyyj.com/uploads/20250728/2f3ae212c01fa3b67be81abc5723cf5c.png"
@click.stop="handleLikeClick(item, index)" @click.stop="handleLikeClick(item, index)"
@ -151,17 +151,26 @@ export default {
// //
handleLikeClick(item, index) { handleLikeClick(item, index) {
// //
const updatedItem = { ...item, isLiked: !item.isLiked }; this.Post({
packageId: item.id,
type:!item.type
},
"/framework/benefitPackage/collect",'DES'
).then((res) => {
//
const updatedItem = { ...item, type: !item.type };
this.productList[index].type = !item.type
uni.showToast({
title: updatedItem.type ? "已收藏" : "取消收藏",
icon: "none",
duration: 1500,
});
this.$forceUpdate()
//
this.$emit("like-toggle", { item: updatedItem, index });
});
//
uni.showToast({
title: updatedItem.isLiked ? "已收藏" : "取消收藏",
icon: "none",
duration: 1500,
});
//
this.$emit("like-toggle", { item: updatedItem, index });
}, },
// //

175
components/header.vue

@ -8,15 +8,36 @@
:class="{ 'header-fixed': fixed }" :class="{ 'header-fixed': fixed }"
:style="{ height: height + 'px', 'padding-top': statusBarHeight + 'px' }" :style="{ height: height + 'px', 'padding-top': statusBarHeight + 'px' }"
> >
<!-- 左侧地区筛选和搜索 --> <!-- 左侧地区筛选和搜索 -->
<view class="left-section" v-if="isSearch"> <view class="left-section" v-if="isSearch">
<view class="location-selector" @click="showLocationPicker" v-if="isLocation&&selectedLocation"> <!-- 使用省市区选择组件 -->
<text class="location-text">{{ selectedLocation }}</text> <!-- 原有的简单地区选择 -->
<image <view v-if="isLocation&&address.cityId ">
class="dropdown-icon" <AreaPicker
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iOCIgdmlld0JveD0iMCAwIDEyIDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0xIDFMNiA2TDExIDEiIHN0cm9rZT0iIzk5OTk5OSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+" :defaultValue="{
mode="heightFix" provinceId: address.provinceId,
></image> cityId: address.cityId,
areaId:address.areaId
}"
ref="areaPicker3"
placeholder="请选择省市区"
:selectedText="selectedText"
@change="changeAddress"
>
<template v-slot="{ selectedText, placeholder, currentSelection }">
<view class="location-selector">
<text class="location-text">{{ selectedText }}</text>
<image
class="dropdown-icon"
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iOCIgdmlld0JveD0iMCAwIDEyIDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0xIDFMNiA2TDExIDEiIHN0cm9rZT0iIzk5OTk5OSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+"
mode="heightFix"
></image>
</view>
</template>
</AreaPicker>
</view> </view>
<image <image
class="search-icon" class="search-icon"
@ -41,11 +62,20 @@
</template> </template>
<script> <script>
import AreaPicker from './AreaPicker.vue'
export default { export default {
components: {
AreaPicker
},
props: { props: {
isSearch: { isSearch: {
type: Boolean, type: Boolean,
default: true, default: true,
},
address: {
type: Object,
default: () =>{},
}, },
type: { type: {
type: String, type: String,
@ -55,11 +85,33 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
isLocation: { isLocation: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
selectedLocation:'' selectedText: {
type: String,
default: ''
},
// 使
isAreaPicker: {
type: Boolean,
default: true,
},
//
areaPlaceholder: {
type: String,
default: '请选择地区'
},
//
defaultAreaValue: {
type: Object,
default: () => ({
provinceId: null,
cityId: null,
areaId: null
})
}
}, },
name: "header", name: "header",
data() { data() {
@ -68,6 +120,8 @@ export default {
height: 0, height: 0,
statusBarHeight: 0, statusBarHeight: 0,
// //
areaPickerVisible: false,
selectedAreaText: ''
}; };
}, },
mounted() { mounted() {
@ -87,12 +141,45 @@ export default {
itemList: ["苏州", "上海", "杭州", "南京", "无锡"], itemList: ["苏州", "上海", "杭州", "南京", "无锡"],
success: (res) => { success: (res) => {
const locations = ["苏州", "上海", "杭州", "南京", "无锡"]; const locations = ["苏州", "上海", "杭州", "南京", "无锡"];
this.selectedLocation = locations[res.tapIndex]; this.selectedText = locations[res.tapIndex];
// //
this.$emit("locationChange", this.selectedLocation); this.$emit("locationChange", this.selectedText);
}, },
}); });
}, },
changeAddress(e){
this.$emit('change',e)
},
//
showAreaPicker() {
this.areaPickerVisible = true;
},
//
onAreaChange(areaData) {
this.selectedAreaText = areaData.fullText;
//
this.$emit('areaChange', areaData);
},
//
getAreaValue() {
if (this.$refs.areaPicker) {
return this.$refs.areaPicker.getValue();
}
return null;
},
//
resetArea() {
this.selectedAreaText = '';
if (this.$refs.areaPicker) {
this.$refs.areaPicker.reset();
}
},
// AreaPicker Post
Post(data, url) {
//
// mixin
return this.$parent.Post ? this.$parent.Post(data, url) : Promise.reject('Post method not found');
}
}, },
}; };
</script> </script>
@ -124,6 +211,56 @@ export default {
align-items: center; align-items: center;
flex: 1; flex: 1;
.area-picker-wrapper {
margin-right: 20rpx;
min-width: 200rpx;
max-width: 300rpx;
}
.area-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;
}
.area-text {
font-size: 30rpx;
color: #333;
flex: 1;
&.placeholder {
color: #999;
}
}
.dropdown-icon {
width: 24rpx;
height: 16rpx;
margin-left: 8rpx;
}
.search-icon {
height: 36.16rpx;
}
}
.logo {
height: 36.16rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.right-section {
flex: 1;
}
.location-selector { .location-selector {
display: flex; display: flex;
align-items: center; align-items: center;
@ -145,20 +282,4 @@ export default {
height: 16rpx; height: 16rpx;
} }
} }
.search-icon {
height: 36.16rpx;
}
}
.logo {
height: 36.16rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.right-section {
flex: 1;
}
</style> </style>

14
manifest.json

@ -58,13 +58,13 @@
"minified" : true "minified" : true
}, },
"usingComponents" : true, "usingComponents" : true,
"permission" : { "permission" : {
"scope.userLocation" : { "scope.userLocation" : {
"desc" : "为了提供更好更快速的体验,需要获取您的当前位置" "desc" : "为了提供更好更快速的体验,需要获取您的当前位置"
} }
}, },
"lazyCodeLoading" : "requiredComponents", "lazyCodeLoading" : "requiredComponents",
"requiredPrivateInfos" : [ "getLocation", "chooseLocation" ] "requiredPrivateInfos" : [ "getLocation", "chooseLocation" ]
}, },
"mp-alipay" : { "mp-alipay" : {
"usingComponents" : true "usingComponents" : true

12
pages.json

@ -54,8 +54,8 @@
"path": "pages/notes/detail", "path": "pages/notes/detail",
"style": { "style": {
"navigationBarTitleText": "笔记详情", "navigationBarTitleText": "笔记详情",
"navigationBarBackgroundColor": "#ffffff", "navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black" "navigationBarTextStyle": "black"
} }
}, },
{ {
@ -189,6 +189,14 @@
"navigationBarTitleText": "修改昵称" "navigationBarTitleText": "修改昵称"
} }
}, },
{
"path": "user/collection",
"style": {
"navigationBarTitleText": "我的收藏",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{ {
"path": "search/search", "path": "search/search",
"style": { "style": {

2155
pages/index/iSoul.vue

File diff suppressed because it is too large

436
pages/index/index.vue

@ -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"
title="有感商品"
:productList="productListFeeling"
moreUrl="/pages/index/sensoryStore"
detailUrlPrefix="/subPackages/techan/detail"
@like-toggle="handleLikeToggle"
isFeel
/>
<view class="tab-bar-placeholder"></view>
</view>
<CustomTabBar :currentTab="0" /> <CustomTabBar :currentTab="0" />
<MusicControl /> <MusicControl />
</view> </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
handleLikeToggle({ item, index }) { },
this.productList[index] = item; "/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)),
});
},
// //
handleIslandToggle(isExpanded) { handleLikeToggle({
console.log("灵动岛状态切换:", isExpanded ? "展开模式" : "紧凑模式"); item,
}, index
}) {
this.productList[index] = item;
},
// //
handleIslandAction() { handleIslandToggle(isExpanded) {
uni.showToast({ console.log("灵动岛状态切换:", isExpanded ? "展开模式" : "紧凑模式");
title: "执行操作", },
icon: "none",
});
console.log("执行操作");
},
// //
handleContentClick() { handleIslandAction() {
// uni.showToast({
if (this.$refs.dynamicIsland) { title: "执行操作",
this.$refs.dynamicIsland.collapseIsland(); icon: "none",
} });
}, console.log("执行操作");
}, },
};
//
handleContentClick() {
//
if (this.$refs.dynamicIsland) {
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;
</style> box-sizing: border-box;
margin-top: 20rpx;
}
.tab-bar-placeholder {
height: 143rpx;
width: 100%;
}
</style>

6
pages/index/timeShopBank.vue

@ -378,9 +378,9 @@ page {
} }
.fab-btn { .fab-btn {
width: 120rpx; width: 80rpx;
height: 120rpx; height: 80rpx;
border-radius: 60rpx; border-radius: 40rpx;
background: linear-gradient(135deg, #ff4757, #ff6b7a); background: linear-gradient(135deg, #ff4757, #ff6b7a);
color: #fff; color: #fff;
border: none; border: none;

35
static/js/request.js

@ -2,10 +2,13 @@ import Vue from 'vue';
import store from '@/store'; import store from '@/store';
// 定义 API URL // 定义 API URL
// const DEV_API_URL = 'https://epic.new.js-dyyj.com';
const DEV_API_URL = 'https://epic.js-dyyj.com'; const DEV_API_URL = 'https://epic.js-dyyj.com';
const PROD_API_URL = 'https://epic.js-dyyj.com'; const PROD_API_URL = 'https://epic.js-dyyj.com';
const NEWAPIURL = process.env.NODE_ENV === 'development' ? DEV_API_URL : PROD_API_URL; const NEWAPIURL = process.env.NODE_ENV === 'development' ? DEV_API_URL : PROD_API_URL;
const DEV_API_URL_DES = 'http://192.168.124.118:8083/xcx';
const PROD_API_URL_DES = 'http://192.168.124.118:8083/xcx';
const NEWAPIURL_DES = process.env.NODE_ENV === 'development' ? DEV_API_URL_DES : PROD_API_URL_DES;
// 获取token // 获取token
const getToken = () => { const getToken = () => {
const userInfoFromStorage = uni.getStorageSync('userInfo'); const userInfoFromStorage = uni.getStorageSync('userInfo');
@ -17,7 +20,16 @@ const getToken = () => {
} }
return store.state.user.userInfo.token; return store.state.user.userInfo.token;
}; };
const getUserId = () => {
const userInfoFromStorage = uni.getStorageSync('userInfo');
if (userInfoFromStorage) {
const userInfo = JSON.parse(userInfoFromStorage);
if (userInfo.id) {
return userInfo.id;
}
}
return store.state.user.userInfo.id;
};
// 定义错误处理函数 noForceLogin 不强制登录 // 定义错误处理函数 noForceLogin 不强制登录
const handleError = (res, reject, noForceLogin) => { const handleError = (res, reject, noForceLogin) => {
if (res.data?.code === 401) { if (res.data?.code === 401) {
@ -38,26 +50,39 @@ const handleError = (res, reject, noForceLogin) => {
// 挂载到 Vue 原型上 // 挂载到 Vue 原型上
Vue.prototype.NEWAPIURL = NEWAPIURL; Vue.prototype.NEWAPIURL = NEWAPIURL;
Vue.prototype.NEWAPIURL_DES = NEWAPIURL_DES;
// #ifdef H5 // #ifdef H5
Vue.prototype.NEWAPIURL = '/api'; Vue.prototype.NEWAPIURL = '/api';
Vue.prototype.NEWAPIURL_DES = '/api_des';
// #endif // #endif
Vue.prototype.Post = (params = {}, apiurl) => { Vue.prototype.Post = (params = {}, apiurl,customUrl) => {
const token = getToken(); const token = getToken();
if (token) { if (token) {
params.token = token; params.token = token;
} }
let baseUrl = Vue.prototype.NEWAPIURL
if (customUrl) {
const urls = {
DES: Vue.prototype.NEWAPIURL_DES,
}
baseUrl = urls[customUrl]
} else {
baseUrl = Vue.prototype.NEWAPIURL
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
uni.showLoading({ uni.showLoading({
title: '加载中' title: '加载中'
}); });
uni.request({ uni.request({
method: params.method || 'GET', method: params.method || 'GET',
url: Vue.prototype.NEWAPIURL + apiurl, url:baseUrl + apiurl,
data: params, data: params,
header: { header: {
'content-type': 'application/json', 'content-type': 'application/json',
'token': token || '' 'token': token || '',
'userId': getUserId() || '',
}, },
success: (res) => { success: (res) => {
console.log('success', res.data); console.log('success', res.data);

336
subPackages/equityGoods/detail.vue

@ -40,11 +40,11 @@
<view class="product-info"> <view class="product-info">
<!-- 标题和标签 --> <!-- 标题和标签 -->
<view class="title-section"> <view class="title-section">
<view class="product-title">食在苏州世界美食之都巡礼</view> <view class="product-title">{{ goodsInfo.title||'-' }}</view>
<view class="tags-container"> <view class="tags-container">
<view class="limit-tag">限量</view> <view class="limit-tag">限量</view>
<view class="limit-count">1000</view> <view class="limit-count">{{ goodsInfo.publishQuantity||0 }}</view>
<view class="remaining">剩余 900</view> <view class="remaining">剩余 {{ goodsInfo.remainQuantity||0 }}</view>
</view> </view>
</view> </view>
@ -52,15 +52,24 @@
<view class="price-section"> <view class="price-section">
<view class="price-container"> <view class="price-container">
<text class="currency">¥</text> <text class="currency">¥</text>
<text class="price">699.00</text> <text class="price">{{ goodsInfo.price||0 }}</text>
</view> </view>
<view class="collect-container"> <view class="collect-container">
<image <image
v-if="!goodsInfo.type"
class="heart-icon" class="heart-icon"
src="https://epic.js-dyyj.com/uploads/20250728/2f3ae212c01fa3b67be81abc5723cf5c.png" src="https://epic.js-dyyj.com/uploads/20250728/2f3ae212c01fa3b67be81abc5723cf5c.png"
@click.stop="handleLikeClick(item, index)" @click.stop="handleLikeClick()"
></image> ></image>
<text class="collect-count">1700收藏</text> <image
v-if="goodsInfo.type"
class="heart-icon"
src="https://epic.js-dyyj.com/uploads/20250728/dd7ed269b24e84a2dd141da6ab980fd6.png"
@click.stop="handleLikeClick()"
></image>
<text class="collect-count"
>{{ goodsInfo.collectQuantity || 0 }}人收藏</text
>
</view> </view>
</view> </view>
@ -98,8 +107,26 @@
:key="index" :key="index"
> >
<view class="detail-content-wrapper"> <view class="detail-content-wrapper">
<text class="detail-label">{{ item.label }}</text> <!-- <text v-if="item.goodsType == 1" class="detail-label">
<text class="detail-content">{{ item.content }}</text> {{index+1}} 数字资产
</text>
<text class="detail-label" v-else-if="item.goodsType == 2">
{{index+1}} 资源产品
</text>
<text class="detail-label" v-else-if="item.goodsType == 3">
{{index+1}} 景点门票
</text> -->
<text class="detail-label">
产品{{
["一", "二", "三", "四", "五", "六", "七", "八", "九", "十"][
index
]
}}
</text>
<text class="detail-content"
>{{ item.goodsName }}{{ item.skuName || "" }}</text
>
<text class="detail-content">X{{ item.bindQuantity }}</text>
</view> </view>
<!-- 在最后一个显示项的右侧添加展开收起图标 --> <!-- 在最后一个显示项的右侧添加展开收起图标 -->
<!-- <view v-if="index === displayEquityList.length - 1" class="toggle-icon" @click="toggleEquityExpand"> <!-- <view v-if="index === displayEquityList.length - 1" class="toggle-icon" @click="toggleEquityExpand">
@ -113,16 +140,18 @@
</view> </view>
<!-- Tab切换区域 --> <!-- Tab切换区域 -->
<view class="tab-nav"> <scroll-view class="tab-nav" scroll-x="true" :show-scrollbar="false">
<view <view class="tab-container">
v-for="(tab, index) in tabList" <view
:key="index" v-for="(tab, index) in tabList"
:class="['tab-item', { active: currentTab === index }]" :key="index"
@click="switchTab(index)" :class="['tab-item', { active: currentTab === index }]"
> @click="switchTab(index)"
<text class="tab-text">{{ tab.name }}</text> >
<text class="tab-text">{{ tab.name }}</text>
</view>
</view> </view>
</view> </scroll-view>
<view class="tab-section"> <view class="tab-section">
<!-- Tab导航 --> <!-- Tab导航 -->
@ -133,43 +162,33 @@
<view class="certificate-container"> <view class="certificate-container">
<image <image
class="certificate-image" class="certificate-image"
src="https://epic.js-dyyj.com/uploads/20250731/d7d5be03617e3cce94dc34e8de426395.png" :src="goodsInfo.ipDigitalAsset.copyrightUrl"
mode="widthFix" mode="widthFix"
></image> >
</image>
</view> </view>
</view> </view>
<!-- IP资产详情 --> <!-- IP资产详情 -->
<view v-if="currentTab === 1" class="tab-panel"> <view v-if="currentTab === 1" class="tab-panel">
<image <view class="" v-html="goodsInfo.ipDigitalAsset.detailUrl"> </view>
:src="
showImg('/uploads/20250731/0d8cb190c358c0cf52fab89bb98a0373.jpg')
"
style="width: 100%"
mode="widthFix"
></image>
</view> </view>
<!-- 数据商品详情 --> <!-- 门票详情 -->
<view v-if="currentTab === 2" class="tab-panel"> <view
<image v-if="currentTab === 2 && goodsInfo.sku && goodsInfo.sku.product"
:src=" class="tab-panel"
showImg('/uploads/20250731/ecf91ddfb9f89658a7d82e3ff846f71c.png') >
" <view class="" v-html="goodsInfo.sku.product.expenseInfo"> </view>
style="width: 100%" <view class="" v-html="goodsInfo.sku.product.bookInfo"> </view>
mode="widthFix" <view class="" v-html="goodsInfo.sku.product.cancelInfo"> </view>
></image>
</view>
<!-- 权益信息 -->
<view v-if="currentTab === 3" class="tab-panel">
<image
:src="
showImg('/uploads/20250731/83af15ea93f0cd91eb780a7bb240257a.jpg')
"
style="width: 100%"
mode="widthFix"
></image>
</view> </view>
<!-- 商品详情 -->
<template v-for="(item, index) in goodsInfo.goodsVos">
<view v-if="currentTab == index + 3" class="tab-panel">
<view class="" v-html="item.detailUrl"> </view>
</view>
</template>
</view> </view>
</view> </view>
@ -186,43 +205,14 @@
export default { export default {
data() { data() {
return { return {
topBanner: [ topBanner: [],
"/uploads/20250731/5f6f00e6f037c5ca03423b3ed190c20d.png",
"/uploads/20250731/5f6f00e6f037c5ca03423b3ed190c20d.png",
],
list: [], list: [],
swiperIndex: 0, swiperIndex: 0,
isEquityExpanded: false, // isEquityExpanded: false, //
currentTab: 0, // tab currentTab: 0, // tab
tabList: [ tabList: [],
{ equityList: [],
name: "登记证书", goodsInfo: null,
},
{
name: "IP资产详情",
},
{
name: "资源商品详情",
},
{
name: "资源商品详情",
},
],
equityList: [
//
{
label: "权益1:",
content: "数字资产*1、数字资产*1",
},
{
label: "权益2:",
content: "IP文创公仔*1、IP文创公仔*1",
},
{
label: "权益3:",
content: "XX园林门票*1、XX园林门票*1",
},
],
}; };
}, },
computed: { computed: {
@ -234,7 +224,40 @@ export default {
// this.equityList.slice(0, 2); // this.equityList.slice(0, 2);
}, },
}, },
onLoad(e) {
this.getDetail(e.id);
},
methods: { methods: {
getDetail(benefitPackageId) {
this.Post(
{},
`/framework/benefitPackage/detail/${benefitPackageId}`,
"DES"
).then((res) => {
res.data.goodsVos.forEach((element) => {
element.detailUrl = this.addImgStyleToHtml(element.detailUrl);
});
this.goodsInfo = res.data;
this.goodsInfo.ipDigitalAsset.detailUrl = this.addImgStyleToHtml(
this.goodsInfo.ipDigitalAsset.detailUrl
);
this.topBanner = res.data.coverUrl.split(",");
this.equityList = res.data.benefitGoods;
this.generateTabList();
});
},
handleLikeClick() {
this.Post(
{
packageId: this.goodsInfo.benefitPackageId,
type: !this.goodsInfo.type,
},
"/framework/benefitPackage/collect",
"DES"
).then((res) => {
this.goodsInfo.type = !this.goodsInfo.type;
});
},
swiperChange(e) { swiperChange(e) {
this.swiperIndex = e.detail.current; this.swiperIndex = e.detail.current;
}, },
@ -252,15 +275,58 @@ export default {
}); });
// //
}, },
//
handleLikeClick(item, index) {
console.log("收藏点击", item, index);
//
},
// tab // tab
switchTab(index) { switchTab(index) {
this.currentTab = index; this.currentTab = index;
}, },
addImgStyleToHtml(htmlStr) {
return htmlStr.replace(/<img\b([^>]*)>/gi, (match, attrs) => {
// style
console.log("====开始计算");
if (/style\s*=/.test(attrs)) {
// style width:100%
return `<img${attrs.replace(
/style\s*=\s*(['"])(.*?)\1/,
(m, quote, styleVal) => {
// width:100% style
let newStyle = styleVal;
if (!/width\s*:\s*100%/.test(styleVal)) {
newStyle = `width:100%;${styleVal}`;
}
return `style=${quote}${newStyle}${quote}`;
}
)}>`;
} else {
// style
return `<img${attrs} style="width:100%">`;
}
});
},
// Tab
generateTabList() {
this.tabList = [];
// Tab -
this.tabList.push({ name: "登记证书" });
this.tabList.push({ name: "IP资产详情" });
// Tab
if (this.goodsInfo) {
// Tab
if (this.goodsInfo.sku && this.goodsInfo.sku.product) {
this.tabList.push({ name: "门票详情" });
}
// goodsVosTab
if (this.goodsInfo.goodsVos) {
this.goodsInfo.goodsVos.forEach((element) => {
this.tabList.push({ name: "权益商品" });
});
}
}
},
}, },
}; };
</script> </script>
@ -345,7 +411,7 @@ export default {
background: #94fafa; background: #94fafa;
color: #333333; color: #333333;
padding: 8rpx 16rpx; padding: 8rpx 16rpx;
font-size: 20rpx; font-size: 26rpx;
border-radius: 6rpx 0 0 6rpx; border-radius: 6rpx 0 0 6rpx;
} }
@ -353,13 +419,13 @@ export default {
background: #f0f0f0; background: #f0f0f0;
color: #000000; color: #000000;
padding: 8rpx 16rpx; padding: 8rpx 16rpx;
font-size: 20rpx; font-size: 26rpx;
border-radius: 0rpx 6rpx 6rpx 0; border-radius: 0rpx 6rpx 6rpx 0;
} }
.remaining { .remaining {
color: #808080; color: #808080;
font-size: 22rpx; font-size: 26rpx;
margin-left: auto; margin-left: auto;
} }
} }
@ -474,28 +540,24 @@ export default {
border-top: 1rpx solid #e5e5e5; border-top: 1rpx solid #e5e5e5;
padding-top: 60rpx; padding-top: 60rpx;
padding-bottom: 20rpx; padding-bottom: 20rpx;
.equity-detail-item { .equity-detail-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10rpx; margin-bottom: 10rpx;
line-height: 1.5; line-height: 1.5;
.detail-content-wrapper { .detail-content-wrapper {
display: flex;
flex: 1;
.detail-label { .detail-label {
font-size: 26rpx; font-size: 28rpx;
color: #808080; color: #808080;
font-weight: 500; font-weight: 500;
margin-right: 10rpx;
} }
.detail-content { .detail-content {
font-size: 26rpx; font-size: 28rpx;
color: #808080; color: #808080;
flex: 1;
font-weight: 500; font-weight: 500;
margin-right: 10rpx;
} }
} }
@ -535,28 +597,40 @@ export default {
.tab-nav { .tab-nav {
margin: 0 40rpx; margin: 0 40rpx;
margin-top: 40rpx; margin-top: 40rpx;
display: flex; white-space: nowrap;
justify-content: space-between;
.tab-item { .tab-container {
padding: 10rpx 20rpx;
text-align: center;
color: #3e3a39;
cursor: pointer;
transition: all 0.3s ease;
background-color: #94fafa66;
display: flex; display: flex;
align-items: center; min-width: 100%;
&.active {
background-color: #94fafa; .tab-item {
color: #525454; padding: 10rpx 20rpx;
font-size: 24rpx; text-align: center;
border-radius: 10rpx 10rpx 0 0; color: #3e3a39;
font-weight: bold; cursor: pointer;
} transition: all 0.3s ease;
background-color: #94fafa66;
display: flex;
align-items: center;
flex-shrink: 0;
margin-right: 16rpx;
.tab-text { &:last-child {
font-size: 24rpx; margin-right: 0;
}
&.active {
background-color: #94fafa;
color: #525454;
font-size: 30rpx;
border-radius: 10rpx 10rpx 0 0;
font-weight: bold;
}
.tab-text {
font-size: 30rpx;
white-space: nowrap;
}
} }
} }
} }
@ -576,6 +650,9 @@ export default {
.tab-panel { .tab-panel {
min-height: 400rpx; min-height: 400rpx;
img {
width: 100%;
}
// //
.certificate-container { .certificate-container {
@ -653,6 +730,29 @@ export default {
} }
} }
} }
//
.equity-detail-list {
.equity-detail-item {
margin-bottom: 30rpx;
padding: 20rpx;
background: #f8f9fa;
border-radius: 12rpx;
.equity-name {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 12rpx;
}
.equity-desc {
font-size: 26rpx;
color: #666;
line-height: 1.5;
}
}
}
} }
} }
} }
@ -664,15 +764,15 @@ export default {
left: 0; left: 0;
right: 0; right: 0;
background: white; background: white;
padding: 20rpx 30rpx 40rpx; padding: 20rpx 30rpx;
border-top: 1rpx solid #e5e5e5; border-top: 1rpx solid #e5e5e5;
z-index: 100; z-index: 100;
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
padding-bottom: calc(constant(safe-area-inset-bottom) + 20rpx);
.purchase-button { .purchase-button {
width: 100%; width: 690rpx;
height: 88rpx; height: 88rpx;
margin: 0 auto;
background: #94fafa; background: #94fafa;
border-radius: 44rpx; border-radius: 44rpx;
display: flex; display: flex;

25
subPackages/equityGoods/index.vue

@ -1,13 +1,36 @@
<template> <template>
<view class="" style="font-size: 0;" @click="toList"> <view class="" style="font-size: 0;" @click="toList">
<image class="bannerImg" mode="aspectFill" :src="showImg('/uploads/20250728/5625c6a60945a256da15ef983d6db87c.png')"> </image> <image class="bannerImg" mode="aspectFill" :src="img"> </image>
</view> </view>
</template> </template>
<script> <script>
export default{ export default{
data(){
return{
img:''
}
},
onLoad() {
this.getList()
},
methods:{ methods:{
getList() {
let address = uni.getStorageSync('SYS_ADDRESS_INFO')
let code = ''
if(address){
code = JSON.parse(address).cityId
}
this.Post({
cityId:code
},
"/framework/index/getUrl",
'DES'
).then((res) => {
this.img = res.msg
});
},
toList(){ toList(){
uni.navigateTo({ uni.navigateTo({
url:'/subPackages/equityGoods/list' url:'/subPackages/equityGoods/list'

1275
subPackages/equityGoods/list.vue

File diff suppressed because it is too large

793
subPackages/orderQy/confrim.vue

@ -60,20 +60,52 @@
<view class="section-title"> <view class="section-title">
<text>IP资产供应商{{ supplier || "苏州XXX博物馆" }}</text> <text>IP资产供应商{{ supplier || "苏州XXX博物馆" }}</text>
</view> </view>
<view class="goods-card" v-for="item in 4"> <view
class="goods-card"
v-for="(item, index) in goodsList"
:key="index"
>
<image <image
class="goods-image" class="goods-image"
:src="goodsInfo.image || '/static/image/goods-default.jpg'" :src="item.image || '/static/image/goods-default.jpg'"
mode="aspectFill" mode="aspectFill"
/> />
<view class="goods-info"> <view class="goods-info">
<text class="goods-name">{{ <text class="goods-name">{{
goodsInfo.name || "食在苏州 | 世界美食之都巡礼+实物探真" item.name || "食在苏州 | 世界美食之都巡礼+实物探真"
}}</text> }}</text>
<text class="goods-desc">{{
goodsInfo.desc || "商品规格信息描述" <!-- 规格列表 -->
}}</text> <view class="specifications-list">
<text class="goods-price">¥{{ goodsInfo.price || "699.00" }}</text> <view
class="spec-item"
v-for="(spec, specIndex) in item.specifications"
:key="specIndex"
@click="selectSpecification(index, specIndex)"
>
<view class="spec-content">
<text class="spec-label">规格{{ specIndex + 1 }}</text>
<text
class="spec-text"
:class="{ placeholder: !spec.selectedSpec }"
>
{{ spec.selectedSpec || "请选择规格" }}
</text>
</view>
<uni-icons type="right" size="14" color="#c0c4cc" />
</view>
<!-- 添加规格按钮 -->
<view
class="add-spec-btn"
v-if="item.specifications.length < item.quantity"
@click="addSpecification(index)"
>
<text class="add-spec-text">+ 添加规格</text>
</view>
</view>
<text class="goods-price">¥{{ item.price || "699.00" }}</text>
</view> </view>
</view> </view>
</view> </view>
@ -123,6 +155,94 @@
<view class="submit-section-content" style="height: 230rpx"> </view> <view class="submit-section-content" style="height: 230rpx"> </view>
</scroll-view> </scroll-view>
<!-- 规格选择弹窗 -->
<uni-popup ref="specificationPopup" type="bottom">
<view class="specification-popup">
<view class="popup-header">
<text class="popup-title">选择规格</text>
<text class="popup-close" @click="closeSpecificationPopup">×</text>
</view>
<view class="popup-content">
<!-- 商品信息 -->
<view class="goods-preview">
<image
class="preview-image"
:src="currentGoods.image || '/static/image/goods-default.jpg'"
mode="aspectFill"
/>
<view class="preview-info">
<text class="preview-name">{{ currentGoods.name }}</text>
<!-- <text class="preview-price">¥{{ currentGoods.price }}</text> -->
</view>
</view>
<!-- 一级规格选择 -->
<view class="spec-section">
<text class="spec-title">
{{ specificationData.level1Title || "规格" }}
<text class="required">*</text>
</text>
<view class="spec-options">
<view
v-for="(option, index) in specificationData.level1Options"
:key="index"
:class="['spec-option', { active: selectedLevel1 === index }]"
@click="selectLevel1(index)"
>
<text class="option-text">{{ option.name }}</text>
</view>
</view>
</view>
<!-- 二级规格选择 -->
<view
class="spec-section"
v-if="
specificationData.level2Options &&
specificationData.level2Options.length > 0
"
>
<text class="spec-title">
{{ specificationData.level2Title || "子规格" }}
<text class="required">*</text>
</text>
<view class="spec-options">
<view
v-for="(option, index) in specificationData.level2Options"
:key="index"
:class="['spec-option', { active: selectedLevel2 === index }]"
@click="selectLevel2(index)"
>
<text class="option-text">{{ option.name }}</text>
</view>
</view>
</view>
<!-- 数量选择 -->
<!-- <view class="quantity-section">
<text class="quantity-title">数量</text>
<view class="quantity-control">
<text class="quantity-btn" @click="decreaseQuantity">-</text>
<text class="quantity-value">{{ quantity }}</text>
<text class="quantity-btn" @click="increaseQuantity">+</text>
</view>
</view> -->
</view>
<!-- 确认按钮 -->
<view class="popup-actions">
<button
class="confirm-spec-btn"
@click="confirmSpecification"
:disabled="!canConfirmSpec"
>
确认规格
</button>
</view>
</view>
</uni-popup>
<!-- 底部提交区域 --> <!-- 底部提交区域 -->
<view <view
class=" " class=" "
@ -159,6 +279,8 @@ export default {
price: "", price: "",
image: "", image: "",
}, },
//
goodsList: [],
// //
supplier: "", supplier: "",
// //
@ -171,6 +293,20 @@ export default {
coupon: "", coupon: "",
// //
totalAmount: "", totalAmount: "",
//
currentGoodsIndex: -1,
currentSpecIndex: -1,
currentGoods: {},
specificationData: {
level1Title: "",
level1Options: [],
level2Title: "",
level2Options: [],
},
selectedLevel1: -1,
selectedLevel2: -1,
quantity: 1,
}; };
}, },
@ -202,6 +338,27 @@ export default {
uni.$off("addressSelected", this.handleAddressSelected); uni.$off("addressSelected", this.handleAddressSelected);
}, },
computed: {
//
canConfirmSpec() {
//
if (this.selectedLevel1 < 0) {
return false;
}
//
if (
this.specificationData.level2Options &&
this.specificationData.level2Options.length > 0
) {
return this.selectedLevel2 >= 0;
}
//
return true;
},
},
methods: { methods: {
// //
goBack() { goBack() {
@ -295,26 +452,310 @@ export default {
const goodsData = await this.getGoodsDetail(goodsId); const goodsData = await this.getGoodsDetail(goodsId);
this.goodsInfo = goodsData; this.goodsInfo = goodsData;
this.supplier = goodsData.supplier; this.supplier = goodsData.supplier;
this.totalAmount = goodsData.price;
//
this.goodsList = [
{
id: goodsData.id,
name: goodsData.name,
price: goodsData.price,
image: goodsData.image,
quantity: 3, // 3
specifications: [
{
selectedSpec: "",
selectedLevel1: -1,
selectedLevel2: -1,
},
],
},
];
this.calculateTotalAmount();
} catch (error) { } catch (error) {
console.error("加载商品信息失败:", error); console.error("加载商品信息失败:", error);
// 使
this.loadMockGoodsData();
} }
}, },
//
loadMockGoodsData() {
this.goodsList = [
{
id: 1,
name: "食在苏州 | 世界美食之都巡礼+实物探真",
price: "699.00",
image: "/static/image/goods-sample.jpg",
quantity: 3,
specifications: [
{
selectedSpec: "",
selectedLevel1: -1,
selectedLevel2: -1,
},
],
},
{
id: 2,
name: "苏州园林文化体验套餐",
price: "899.00",
image: "/static/image/goods-sample.jpg",
quantity: 2,
specifications: [
{
selectedSpec: "",
selectedLevel1: -1,
selectedLevel2: -1,
},
],
},
];
this.calculateTotalAmount();
},
// //
async loadDefaultAddress() { async loadDefaultAddress() {
try { try {
this.Post({
isDefault:1
},
"/framework/concat/selectList",
'DES'
).then((res) => {
if(res.data){
this.updateAddressInfo(res.data[0]);
}
});
// API // API
const addressData = await this.getDefaultAddress();
if (addressData && !this.address.id) {
//
this.updateAddressInfo(addressData);
}
} catch (error) { } catch (error) {
console.error("加载默认地址失败:", error); console.error("加载默认地址失败:", error);
} }
}, },
//
selectSpecification(goodsIndex, specIndex) {
this.currentGoodsIndex = goodsIndex;
this.currentSpecIndex = specIndex;
this.currentGoods = this.goodsList[goodsIndex];
this.loadSpecificationData(this.currentGoods.id);
//
const currentSpec = this.currentGoods.specifications[specIndex];
this.selectedLevel1 = currentSpec.selectedLevel1 || -1;
this.selectedLevel2 = currentSpec.selectedLevel2 || -1;
this.quantity = this.currentGoods.quantity || 1;
this.$refs.specificationPopup.open();
},
//
addSpecification(goodsIndex) {
const goods = this.goodsList[goodsIndex];
if (goods.specifications.length < goods.quantity) {
goods.specifications.push({
selectedSpec: "",
selectedLevel1: -1,
selectedLevel2: -1,
});
}
},
//
closeSpecificationPopup() {
this.$refs.specificationPopup.close();
this.resetSpecificationData();
},
//
resetSpecificationData() {
this.selectedLevel1 = -1;
this.selectedLevel2 = -1;
this.quantity = 1; // 1
},
//
async loadSpecificationData(goodsId) {
try {
const specData = await this.getSpecificationData(goodsId);
this.specificationData = specData;
//
if (
this.selectedLevel1 >= 0 &&
this.specificationData.level1Options[this.selectedLevel1]
) {
const level1Option =
this.specificationData.level1Options[this.selectedLevel1];
if (level1Option.children) {
this.specificationData.level2Options = level1Option.children;
} else {
this.specificationData.level2Options = [];
}
}
} catch (error) {
console.error("加载规格数据失败:", error);
// 使
this.loadMockSpecificationData();
}
},
//
selectLevel1(index) {
this.selectedLevel1 = index;
this.selectedLevel2 = -1; //
//
if (
this.specificationData.level1Options[index] &&
this.specificationData.level1Options[index].children
) {
this.specificationData.level2Options =
this.specificationData.level1Options[index].children;
} else {
this.specificationData.level2Options = [];
}
},
//
selectLevel2(index) {
this.selectedLevel2 = index;
},
//
decreaseQuantity() {
if (this.quantity > 1) {
this.quantity--;
}
},
//
increaseQuantity() {
this.quantity++;
},
//
confirmSpecification() {
//
if (this.selectedLevel1 < 0) {
uni.showToast({
title: "请选择一级规格",
icon: "none",
});
return;
}
//
if (
this.specificationData.level2Options &&
this.specificationData.level2Options.length > 0
) {
if (this.selectedLevel2 < 0) {
uni.showToast({
title: "请选择二级规格",
icon: "none",
});
return;
}
}
const level1Option =
this.specificationData.level1Options[this.selectedLevel1];
const level2Option =
this.selectedLevel2 >= 0
? this.specificationData.level2Options[this.selectedLevel2]
: null;
//
let specText = level1Option.name;
if (level2Option) {
specText += ` / ${level2Option.name}`;
}
// if (this.quantity > 1) {
// specText += ` × ${this.quantity}`;
// }
//
this.goodsList[this.currentGoodsIndex].specifications[
this.currentSpecIndex
].selectedSpec = specText;
this.goodsList[this.currentGoodsIndex].specifications[
this.currentSpecIndex
].selectedLevel1 = this.selectedLevel1;
this.goodsList[this.currentGoodsIndex].specifications[
this.currentSpecIndex
].selectedLevel2 = this.selectedLevel2;
//
let price = level1Option.price || this.currentGoods.price;
if (level2Option && level2Option.price) {
price = level2Option.price;
}
this.goodsList[this.currentGoodsIndex].price = price;
//
this.calculateTotalAmount();
this.closeSpecificationPopup();
uni.showToast({
title: "规格选择成功",
icon: "success",
});
},
//
calculateTotalAmount() {
let total = 0;
this.goodsList.forEach((item) => {
if (item.price) {
//
const selectedSpecsCount = item.specifications.filter(
(spec) => spec.selectedSpec
).length;
total += parseFloat(item.price) * selectedSpecsCount;
}
});
this.totalAmount = total.toFixed(2);
},
//
loadMockSpecificationData() {
this.specificationData = {
level1Title: "套餐类型",
level1Options: [
{
name: "标准套餐",
price: "699.00",
children: [
{ name: "单人套餐", price: "699.00" },
{ name: "双人套餐", price: "1299.00" },
{ name: "家庭套餐", price: "1899.00" },
],
},
{
name: "豪华套餐",
price: "999.00",
children: [
{ name: "VIP单人", price: "999.00" },
{ name: "VIP双人", price: "1799.00" },
{ name: "VIP家庭", price: "2599.00" },
],
},
{
name: "定制套餐",
price: "1299.00",
children: [
{ name: "定制单人", price: "1299.00" },
{ name: "定制双人", price: "2299.00" },
],
},
],
level2Title: "具体套餐",
level2Options: [],
};
},
// //
async submitOrder() { async submitOrder() {
// //
@ -326,9 +767,14 @@ export default {
return; return;
} }
if (!this.deliveryMethod) { //
const unselectedGoods = this.goodsList.filter((item) => {
//
return item.specifications.some((spec) => !spec.selectedSpec);
});
if (unselectedGoods.length > 0) {
uni.showToast({ uni.showToast({
title: "请选择履约方式", title: "请选择所有商品的规格",
icon: "none", icon: "none",
}); });
return; return;
@ -400,26 +846,50 @@ export default {
}); });
}, },
// API - // API -
async getDefaultAddress() { async getSpecificationData(goodsId) {
// API // API
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
resolve({ resolve({
id: 1, level1Title: "套餐类型",
name: "XX先生", level1Options: [
tel: "18312341234", {
address: "江苏省苏州市姑苏区XXXXXX", name: "标准套餐",
province_text: "江苏省", price: "699.00",
city_text: "苏州市", children: [
district_text: "姑苏区", { name: "单人套餐", price: "699.00" },
detail_addr: "XXXXXX", { name: "双人套餐", price: "1299.00" },
is_default: 1, { name: "家庭套餐", price: "1899.00" },
],
},
{
name: "豪华套餐",
price: "999.00",
children: [
{ name: "VIP单人", price: "999.00" },
{ name: "VIP双人", price: "1799.00" },
{ name: "VIP家庭", price: "2599.00" },
],
},
{
name: "定制套餐",
price: "1299.00",
children: [
{ name: "定制单人", price: "1299.00" },
{ name: "定制双人", price: "2299.00" },
],
},
],
level2Title: "具体套餐",
level2Options: [],
}); });
}, 500); }, 300);
}); });
}, },
// API - // API -
async createOrder(orderData) { async createOrder(orderData) {
// API // API
@ -769,4 +1239,273 @@ $bg-light: #f7fafc;
background-color: rgba(45, 55, 72, 0.8); background-color: rgba(45, 55, 72, 0.8);
} }
} }
//
.specification-popup {
background: white;
border-radius: 24rpx 24rpx 0 0;
max-height: 80vh;
overflow: hidden;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 30rpx 24rpx;
border-bottom: 1rpx solid $border-color;
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: $text-primary;
}
.popup-close {
font-size: 40rpx;
color: $text-muted;
line-height: 1;
padding: 8rpx;
cursor: pointer;
transition: color 0.3s;
&:active {
color: $text-secondary;
}
}
}
.popup-content {
padding: 30rpx;
max-height: 60vh;
overflow-y: auto;
}
//
.goods-preview {
display: flex;
align-items: center;
gap: 20rpx;
padding: 24rpx;
background: $bg-light;
border-radius: 16rpx;
margin-bottom: 30rpx;
.preview-image {
width: 80rpx;
height: 80rpx;
border-radius: 12rpx;
flex-shrink: 0;
}
.preview-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 8rpx;
}
.preview-name {
font-size: 26rpx;
color: $text-primary;
font-weight: 500;
line-height: 1.3;
}
.preview-price {
font-size: 28rpx;
color: $danger-color;
font-weight: 600;
}
}
//
.spec-section {
margin-bottom: 30rpx;
.spec-title {
font-size: 28rpx;
color: $text-primary;
font-weight: 600;
margin-bottom: 20rpx;
display: block;
.required {
color: $danger-color;
margin-left: 4rpx;
}
}
.spec-options {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.spec-option {
padding: 16rpx 24rpx;
border: 2rpx solid $border-color;
border-radius: 12rpx;
background: white;
transition: all 0.3s ease;
cursor: pointer;
&:active {
transform: scale(0.95);
}
&.active {
border-color: $primary-color;
background: rgba(102, 126, 234, 0.1);
}
.option-text {
font-size: 26rpx;
color: $text-primary;
font-weight: 500;
}
}
}
//
.quantity-section {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 0;
border-top: 1rpx solid $border-color;
.quantity-title {
font-size: 28rpx;
color: $text-primary;
font-weight: 600;
}
.quantity-control {
display: flex;
align-items: center;
gap: 20rpx;
}
.quantity-btn {
width: 60rpx;
height: 60rpx;
border: 2rpx solid $border-color;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: $text-primary;
font-weight: 600;
background: white;
transition: all 0.3s ease;
cursor: pointer;
&:active {
background: $bg-light;
border-color: $primary-color;
}
}
.quantity-value {
font-size: 28rpx;
color: $text-primary;
font-weight: 600;
min-width: 60rpx;
text-align: center;
}
}
//
.popup-actions {
padding: 24rpx 30rpx 40rpx;
border-top: 1rpx solid $border-color;
background: white;
.confirm-spec-btn {
width: 100%;
height: 88rpx;
background: $primary-color;
color: white;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
background: rgba(102, 126, 234, 0.8);
}
&:disabled {
background: $text-muted;
cursor: not-allowed;
}
}
}
//
.specifications-list {
margin: 12rpx 0;
}
.spec-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.spec-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 8rpx;
}
.spec-label {
font-size: 22rpx;
color: $text-secondary;
font-weight: 500;
}
.spec-text {
font-size: 24rpx;
color: $text-primary;
font-weight: 500;
&.placeholder {
color: $text-muted;
}
}
.add-spec-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 0;
margin-top: 8rpx;
border: 2rpx dashed $border-color;
border-radius: 12rpx;
background: rgba(102, 126, 234, 0.02);
transition: all 0.3s ease;
&:active {
background: rgba(102, 126, 234, 0.1);
border-color: $primary-color;
}
}
.add-spec-text {
font-size: 24rpx;
color: $primary-color;
font-weight: 500;
}
</style> </style>

123
subPackages/orderQy/detail.vue

@ -53,8 +53,11 @@
> >
<view class="goods-checkbox"> <view class="goods-checkbox">
<view class="checkbox-icon"> <view class="checkbox-icon">
<image mode="aspectFill" src="https://picsum.photos/200/300"></image> <image
</view> mode="aspectFill"
src="https://picsum.photos/200/300"
></image>
</view>
</view> </view>
<view class="goods-content"> <view class="goods-content">
<view class="goods-info"> <view class="goods-info">
@ -62,14 +65,32 @@
<view class="goods-desc">{{ <view class="goods-desc">{{
goods.description || goods.spec goods.description || goods.spec
}}</view> }}</view>
<view class="goods-quantity">数量{{ goods.quantity || 1 }}</view>
</view> </view>
<view class="goods-actions"> <view class="goods-actions">
<button class="action-btn" :class="[getGoodsActionClass(goods.type)]"> <button
class="action-btn"
:class="[getGoodsActionClass(goods.type)]"
>
{{ getGoodsActionText(goods.type) }} {{ getGoodsActionText(goods.type) }}
</button> </button>
</view> </view>
</view> </view>
</view> </view>
<!-- 金额汇总 -->
<view class="amount-summary">
<view class="summary-row">
<text class="summary-label">订单金额</text>
<text class="summary-value">¥{{ orderDetail.goodsAmount }}</text>
</view>
<view class="summary-row">
<text class="summary-label">运费</text>
<text class="summary-value"
>¥{{ orderDetail.shippingFee || "5.00" }}</text
>
</view>
</view>
</view> </view>
<!-- 订单信息 --> <!-- 订单信息 -->
@ -690,7 +711,7 @@ export default {
font-size: 28rpx; font-size: 28rpx;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
margin-bottom: 20rpx; margin-bottom: 0;
} }
// //
@ -717,12 +738,12 @@ export default {
} }
.checkbox-icon { .checkbox-icon {
width: 150rpx; width: 100rpx;
height:150rpx; height: 100rpx;
image{ image {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 10rpx; border-radius: 10rpx;
} }
} }
@ -749,6 +770,13 @@ export default {
font-size: 24rpx; font-size: 24rpx;
color: #666; color: #666;
line-height: 1.4; line-height: 1.4;
margin-bottom: 8rpx;
}
.goods-quantity {
font-size: 22rpx;
color: #999;
font-weight: 500;
} }
.goods-actions { .goods-actions {
@ -761,22 +789,50 @@ export default {
font-size: 24rpx; font-size: 24rpx;
border: none; border: none;
&.btn-view { padding: 0rpx 20rpx;
background-color: #f0f0f0; border-radius: 10rpx;
color: #666; font-size: 24rpx;
} font-weight: 600;
border: none;
color: #333333;
transition: all 0.3s ease;
min-width: 100rpx;
text-align: center;
background-color: #77f3f9;
height: 55rpx;
line-height: 55rpx;
}
&.btn-logistics { //
background-color: #007aff; .amount-summary {
color: #fff; padding: 20rpx 30rpx;
} border-top: 1rpx solid #f0f0f0;
background-color: white;
}
&.btn-used { .summary-row {
background-color: #34c759; display: flex;
color: #fff; justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
} }
} }
.summary-label {
font-size: 26rpx;
color: #666;
font-weight: 500;
}
.summary-value {
font-size: 26rpx;
color: #333;
font-weight: 600;
}
// //
.order-info-section { .order-info-section {
background-color: #fff; background-color: #fff;
@ -844,21 +900,18 @@ export default {
.action-btn { .action-btn {
flex: 1; flex: 1;
height: 80rpx; padding: 0rpx 20rpx;
border-radius: 40rpx; border-radius: 10rpx;
font-size: 28rpx; font-size: 24rpx;
border: none;
font-weight: 600; font-weight: 600;
border: none;
&.secondary { color: #333333;
background-color: #f0f0f0; transition: all 0.3s ease;
color: #666; min-width: 100rpx;
} text-align: center;
background-color: #77f3f9;
&.primary { height: 55rpx;
background-color: #333; line-height: 55rpx;
color: #fff;
}
} }
} }

135
subPackages/orderQy/list.vue

@ -4,7 +4,7 @@
<view class="tab-container"> <view class="tab-container">
<view <view
class="tab-item" class="tab-item"
:class="{ active: currentTab === index }" :class="{ active: currentTab == index }"
v-for="(tab, index) in tabs" v-for="(tab, index) in tabs"
:key="index" :key="index"
@click="switchTab(index)" @click="switchTab(index)"
@ -19,10 +19,10 @@
scroll-y scroll-y
@scrolltolower="loadMore" @scrolltolower="loadMore"
refresher-enabled refresher-enabled
:show-scrollbar="false" :show-scrollbar="false"
:refresher-triggered="refresherTriggered" :refresher-triggered="refresherTriggered"
@refresherrefresh="onRefresh" @refresherrefresh="onRefresh"
enhanced enhanced
> >
<!-- 订单项 --> <!-- 订单项 -->
<view <view
@ -32,42 +32,35 @@
@click="goToOrderDetail(order)" @click="goToOrderDetail(order)"
> >
<!-- 订单头部 --> <!-- 订单头部 -->
<view class="order-header">
<view class="order-left">
<view class="order-number-section">
<text class="order-label">订单号</text>
<text class="order-number">{{ order.orderNo }}</text>
</view>
</view>
<view class="order-status-wrapper">
<text class="order-status" :class="[getStatusClass(order.status)]">
{{ getStatusText(order.status) }}
</text>
</view>
</view>
<!-- 权益商品包名 --> <!-- 权益商品包名 -->
<view class="package-section"> <view class="package-section">
<view class="package-icon">📦</view>
<text class="package-name">{{ order.packageName }}</text> <text class="package-name">{{ order.packageName }}</text>
<text class="status-name">{{ getStatusText(order.status) }}</text>
</view> </view>
<!-- 商品列表 --> <!-- 商品列表 -->
<view class="goods-section"> <view class="goods-section">
<view class="goods-title">商品清单</view>
<view class="goods-list"> <view class="goods-list">
<view <view
class="goods-item" class="goods-item"
v-for="goods in order.goodsList" v-for="goods in order.goodsList"
:key="goods.id" :key="goods.id"
> >
<image class="goods-image" :src="goods.image" mode="aspectFill" /> <image
class="goods-image"
src="https://picsum.photos/200/200"
mode="aspectFill"
/>
<view class="goods-info"> <view class="goods-info">
<text class="goods-name">{{ goods.name }}</text> <text class="goods-name">{{ goods.name }}</text>
<view class="goods-meta"> <view class="goods-meta">
<text class="goods-type">{{ <text class="goods-type">{{
getGoodsTypeName(goods.type) getGoodsTypeName(goods.type)
}}</text> }}</text>
<text class="goods-quantity"
>数量{{ goods.quantity || 1 }}</text
>
</view> </view>
</view> </view>
<view class="goods-action"> <view class="goods-action">
@ -86,10 +79,6 @@
<!-- 订单底部 --> <!-- 订单底部 -->
<view class="order-footer"> <view class="order-footer">
<view class="order-info"> <view class="order-info">
<view class="order-time-section">
<text class="time-label">下单时间</text>
<text class="order-time">{{ formatTime(order.createTime) }}</text>
</view>
<view class="order-total-section"> <view class="order-total-section">
<text class="total-label">实付金额</text> <text class="total-label">实付金额</text>
<text class="order-total">¥{{ order.totalAmount }}</text> <text class="order-total">¥{{ order.totalAmount }}</text>
@ -101,7 +90,6 @@
@click.stop="showEquityCode(order)" @click.stop="showEquityCode(order)"
v-if="order.status !== 0" v-if="order.status !== 0"
> >
<text class="btn-icon">🎫</text>
<text>权益码</text> <text>权益码</text>
</button> </button>
</view> </view>
@ -189,7 +177,10 @@ export default {
}, },
}; };
}, },
onLoad() { onLoad(e) {
if(e.status){
this.currentTab = e.status
}
this.loadOrderList(); this.loadOrderList();
}, },
methods: { methods: {
@ -330,6 +321,9 @@ export default {
title: "查看数字资产", title: "查看数字资产",
icon: "none", icon: "none",
}); });
uni.navigateTo({
url:'/subPackages/memorialAlbum/detail'
})
}, },
// //
@ -505,8 +499,9 @@ $bg-light: #f7fafc;
// Tab // Tab
.tab-container { .tab-container {
display: flex; display: flex;
background-color: $primary-color; background-color: #ffffff;
border-bottom: 1px solid rgba(255, 255, 255, 0.1); border-bottom: 1px solid rgba(255, 255, 255, 0.1);
color: black;
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 10; z-index: 10;
@ -518,10 +513,11 @@ $bg-light: #f7fafc;
text-align: center; text-align: center;
position: relative; position: relative;
transition: all 0.3s ease; transition: all 0.3s ease;
color: #333333;
&.active { &.active {
.tab-text { .tab-text {
color: #ffffff; color: #77f3f9;
font-weight: 600; font-weight: 600;
} }
@ -533,21 +529,21 @@ $bg-light: #f7fafc;
transform: translateX(-50%); transform: translateX(-50%);
width: 60rpx; width: 60rpx;
height: 4rpx; height: 4rpx;
background-color: #ffffff; background-color: #77f3f9;
border-radius: 2rpx; border-radius: 2rpx;
} }
} }
&:not(.active) { &:not(.active) {
.tab-text { .tab-text {
opacity: 0.7; opacity: 1;
} }
} }
} }
.tab-text { .tab-text {
font-size: 26rpx; font-size: 26rpx;
color: #ffffff; color: #333333;
font-weight: 600; font-weight: 600;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
@ -647,22 +643,28 @@ $bg-light: #f7fafc;
// //
.package-section { .package-section {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
padding: 20rpx 28rpx; padding: 20rpx 20rpx;
margin: 0 20rpx;
margin-top: -6rpx;
}
.package-icon {
font-size: 36rpx;
margin-right: 20rpx;
} }
.package-name { .package-name {
flex: 1;
font-size: 30rpx; font-size: 30rpx;
color: $text-primary; color: $text-primary;
font-weight: 600; font-weight: 600;
letter-spacing: 0.5rpx; letter-spacing: 0.5rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 20rpx;
}
.status-name {
font-size: 26rpx;
color: $text-secondary;
font-weight: 500;
flex-shrink: 0;
} }
// //
@ -718,7 +720,7 @@ $bg-light: #f7fafc;
.goods-name { .goods-name {
display: block; display: block;
font-size: 30rpx; font-size: 26rpx;
color: $text-primary; color: $text-primary;
font-weight: 600; font-weight: 600;
margin-bottom: 12rpx; margin-bottom: 12rpx;
@ -737,10 +739,17 @@ $bg-light: #f7fafc;
.goods-type { .goods-type {
font-size: 24rpx; font-size: 24rpx;
color: $text-secondary; color: $text-secondary;
background: $secondary-color; // background: $secondary-color;
padding: 8rpx 16rpx; // padding: 8rpx 16rpx;
border-radius: 20rpx; border-radius: 20rpx;
font-weight: 500; font-weight: 500;
margin-right: 16rpx;
}
.goods-quantity {
font-size: 22rpx;
color: $text-muted;
font-weight: 500;
} }
.goods-price { .goods-price {
@ -753,30 +762,32 @@ $bg-light: #f7fafc;
.goods-action { .goods-action {
.action-btn { .action-btn {
padding: 0rpx 20rpx; padding: 0rpx 20rpx;
border-radius: 20rpx; border-radius: 10rpx;
font-size: 24rpx; font-size: 24rpx;
font-weight: 600; font-weight: 600;
border: none; border: none;
color: #ffffff; color: #333333;
transition: all 0.3s ease; transition: all 0.3s ease;
min-width: 100rpx; min-width: 100rpx;
text-align: center; text-align: center;
background-color: #77f3f9;
height: 55rpx;
line-height: 55rpx;
&:active { &:active {
transform: scale(0.95); transform: scale(0.95);
} }
&.btn-view { // &.btn-view {
background: $primary-color; // background: $primary-color;
} // }
&.btn-reserve { // &.btn-reserve {
background: $warning-color; // background: $warning-color;
} // }
&.btn-use { // &.btn-use {
background: $success-color; // background: $success-color;
} // }
} }
} }
@ -786,7 +797,7 @@ $bg-light: #f7fafc;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 24rpx 28rpx; padding: 24rpx 28rpx;
background: $secondary-color; background: white;
margin-top: 20rpx; margin-top: 20rpx;
} }
@ -798,13 +809,11 @@ $bg-light: #f7fafc;
.order-time-section, .order-time-section,
.order-total-section { .order-total-section {
display: flex;
flex-direction: column;
gap: 6rpx;
} }
.time-label, .time-label,
.total-label { .total-label {
margin-right: 10rpx;
font-size: 22rpx; font-size: 22rpx;
color: $text-muted; color: $text-muted;
font-weight: 500; font-weight: 500;
@ -832,15 +841,15 @@ $bg-light: #f7fafc;
align-items: center; align-items: center;
gap: 10rpx; gap: 10rpx;
padding: 2rpx 24rpx; padding: 2rpx 24rpx;
background: $primary-color; background: white;
color: #ffffff; color: #333333;
border-radius: 24rpx; border-radius: 10rpx;
font-size: 24rpx; font-size: 24rpx;
font-weight: 600; font-weight: 600;
border: none; border: 1rpx solid #77f3f9;
transition: all 0.3s ease; transition: all 0.3s ease;
box-shadow: 0 2rpx 8rpx rgba(102, 126, 234, 0.2); height: 55rpx;
line-height: 55rpx;
&:active { &:active {
transform: scale(0.95); transform: scale(0.95);
} }

408
subPackages/user/collection.vue

@ -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>

4
utils/request.js

@ -10,9 +10,9 @@
* @returns {Promise} - 返回Promise对象 * @returns {Promise} - 返回Promise对象
*/ */
const DOMAIN = 'https://m.dayunyuanjian.com';
export const request = (options = {}) => { export const request = (options = {}) => {
const DOMAIN = 'https://m.dayunyuanjian.com';
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
options.url = options.url.startsWith('/') ? `${DOMAIN}${options.url}` : options.url; options.url = options.url.startsWith('/') ? `${DOMAIN}${options.url}` : options.url;
// 默认配置 // 默认配置

Loading…
Cancel
Save