Merge branch 'main' of https://gitlab.guxuan.icu/Leo_Ding/JinShan_uniapp
This commit is contained in:
commit
967b9289d0
@ -1,20 +1,134 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="kfc-container">
|
<view class="container">
|
||||||
<!-- 顶部商品图片 -->
|
<!-- 商品图片(带轻微圆角和阴影) -->
|
||||||
<view class="container-img">
|
<view class="cover-container">
|
||||||
<image
|
<image
|
||||||
class="kfc-bucket-img"
|
class="store-cover"
|
||||||
:src="detailInfo.storeCover"
|
:src="detailInfo.storeCover"
|
||||||
></image>
|
mode="aspectFill"
|
||||||
</view>
|
></image>
|
||||||
|
<view class="cover-overlay"></view>
|
||||||
<!-- 店铺信息模块 -->
|
|
||||||
<view class="store-info">
|
|
||||||
<text class="store-name">{{ detailInfo.storeName }}</text>
|
|
||||||
<view class="business-hours">营业时间:<text>{{ detailInfo.openAt}}</text></view>
|
|
||||||
<view class="address">地址:<text>{{ detailInfo.storeAddress }}</text></view>
|
|
||||||
<view class="distance">距您{{ local }}米</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 店铺信息卡片 -->
|
||||||
|
<view class="store-card">
|
||||||
|
<view class="store-header">
|
||||||
|
<text class="store-name">{{ detailInfo.storeName }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 价格和距离合并一行 -->
|
||||||
|
<view class="price-distance-row">
|
||||||
|
<view class="price-tag" v-if="detailInfo.price !== undefined">
|
||||||
|
{{ detailInfo.price > 0 ? '¥' + detailInfo.price : '免费' }}
|
||||||
|
</view>
|
||||||
|
<view class="distance-badge">
|
||||||
|
<u-icon name="map" color="#fff" size="24"></u-icon>
|
||||||
|
<text>{{ formatDistance(local) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 标签展示 -->
|
||||||
|
<view class="tags-container" v-if="detailInfo.labels && detailInfo.labels.length">
|
||||||
|
<view class="tag" v-for="(tag, index) in detailInfo.labels" :key="index">
|
||||||
|
<u-icon name="tags" size="20" color="#8dbafc" style="margin-right: 6rpx;"></u-icon>
|
||||||
|
{{ tag }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="divider dotted"></view>
|
||||||
|
|
||||||
|
<view class="info-grid">
|
||||||
|
<view class="info-item">
|
||||||
|
<u-icon name="map" color="#8dbafc" size="36"></u-icon>
|
||||||
|
<view class="info-content">
|
||||||
|
<text class="info-label">详细地址</text>
|
||||||
|
<text class="info-value">{{ detailInfo.storeAddress }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="info-item" v-if="detailInfo.openAt">
|
||||||
|
<u-icon name="clock" color="#8dbafc" size="32"></u-icon>
|
||||||
|
<view class="info-content">
|
||||||
|
<text class="info-label">开放时间</text>
|
||||||
|
<text class="info-value">{{ detailInfo.openAt }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="info-item" v-if="detailInfo.concatPhone">
|
||||||
|
<u-icon name="phone" color="#8dbafc" size="32"></u-icon>
|
||||||
|
<view class="info-content">
|
||||||
|
<text class="info-label">联系电话</text>
|
||||||
|
<text class="info-value">{{ detailInfo.concatPhone }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="info-item" v-if="detailInfo.content">
|
||||||
|
<u-icon name="info-circle" color="#8dbafc" size="36"></u-icon>
|
||||||
|
<view class="info-content">
|
||||||
|
<text class="info-label">详情介绍</text>
|
||||||
|
<text class="info-value">{{ detailInfo.content }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="info-item" v-if="detailInfo.remark">
|
||||||
|
<u-icon name="chat-fill" color="#8dbafc" size="36"></u-icon>
|
||||||
|
<view class="info-content">
|
||||||
|
<text class="info-label">备注信息</text>
|
||||||
|
<text class="info-value">{{ detailInfo.remark }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="info-item" v-if="detailInfo.link">
|
||||||
|
<u-icon name="link" color="#8dbafc" size="36"></u-icon>
|
||||||
|
<view class="info-content">
|
||||||
|
<text class="info-label">相关链接</text>
|
||||||
|
<text class="info-value">{{ detailInfo.link }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="divider dotted"></view>
|
||||||
|
|
||||||
|
<!-- 详情图片部分 -->
|
||||||
|
<view class="detail-section" v-if="detailInfo.imgs && detailInfo.imgs.length">
|
||||||
|
<text class="section-title">
|
||||||
|
<u-icon name="photo-fill" color="#8dbafc" size="32" style="margin-right: 12rpx;"></u-icon>
|
||||||
|
环境展示
|
||||||
|
</text>
|
||||||
|
<view class="image-grid">
|
||||||
|
<image
|
||||||
|
class="detail-image"
|
||||||
|
v-for="(img, index) in detailInfo.imgs"
|
||||||
|
:key="index"
|
||||||
|
:src="IMAGE_BASE_URL + img"
|
||||||
|
mode="aspectFill"
|
||||||
|
@click="previewImage(index)"
|
||||||
|
lazy-load
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 地图导航按钮 -->
|
||||||
|
<view class="action-buttons">
|
||||||
|
<button class="nav-button" @click="openMap">
|
||||||
|
<u-icon name="map-fill" color="#fff" size="28"></u-icon>
|
||||||
|
<text>导航前往</text>
|
||||||
|
</button>
|
||||||
|
<button class="share-button" open-type="share" @click="handleShareClick">
|
||||||
|
<u-icon name="share-square" color="#8dbafc" size="28"></u-icon>
|
||||||
|
<text>分享</text>
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 授权弹窗 -->
|
||||||
|
<u-modal
|
||||||
|
v-model="showLocationModal"
|
||||||
|
title="位置授权提示"
|
||||||
|
:content="locationModalContent"
|
||||||
|
show-cancel-button
|
||||||
|
@confirm="handleAuthConfirm"
|
||||||
|
@cancel="handleAuthCancel"
|
||||||
|
></u-modal>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -22,90 +136,548 @@
|
|||||||
import { get, post } from '@/utils/request';
|
import { get, post } from '@/utils/request';
|
||||||
import { IMAGE_BASE_URL, BASE_URL } from '@/utils/config';
|
import { IMAGE_BASE_URL, BASE_URL } from '@/utils/config';
|
||||||
import { formatTime, formatRelativeTime } from '@/utils/timeFormat';
|
import { formatTime, formatRelativeTime } from '@/utils/timeFormat';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
formatTime,
|
formatTime,
|
||||||
IMAGE_BASE_URL,
|
IMAGE_BASE_URL,
|
||||||
Id:null,
|
Id: null,
|
||||||
detailInfo: {},
|
defaultCover: '/static/images/default-store-cover.png',
|
||||||
local:null,
|
detailInfo: {
|
||||||
|
storeName: '',
|
||||||
|
storeCover: '',
|
||||||
|
storeAddress: '',
|
||||||
|
openAt: '',
|
||||||
|
content: '',
|
||||||
|
labels: [],
|
||||||
|
remark: '',
|
||||||
|
detailImages: [],
|
||||||
|
id: '',
|
||||||
|
createdAt: '',
|
||||||
|
updatedAt: '',
|
||||||
|
deletedAt: 0,
|
||||||
|
createdId: '',
|
||||||
|
deletedId: '',
|
||||||
|
typeId: '',
|
||||||
|
longitude: '',
|
||||||
|
latitude: '',
|
||||||
|
price: 0,
|
||||||
|
sequence: 0,
|
||||||
|
link: '',
|
||||||
|
status: 1
|
||||||
|
},
|
||||||
|
local: null,
|
||||||
|
showLocationModal: false,
|
||||||
|
locationModalContent: '我们需要获取您的位置信息,以便提供更精准的服务',
|
||||||
|
userLocation: null,
|
||||||
|
isRequestingLocation: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
statusText() {
|
||||||
|
const statusMap = {
|
||||||
|
1: '营业中',
|
||||||
|
2: '已关闭',
|
||||||
|
3: '即将开业',
|
||||||
|
0: '未知状态'
|
||||||
|
};
|
||||||
|
return statusMap[this.detailInfo.status] || `状态: ${this.detailInfo.status}`;
|
||||||
|
},
|
||||||
|
statusClass() {
|
||||||
|
const statusMap = {
|
||||||
|
1: 'status-open',
|
||||||
|
2: 'status-closed',
|
||||||
|
3: 'status-soon'
|
||||||
|
};
|
||||||
|
return statusMap[this.detailInfo.status] || 'status-default';
|
||||||
|
},
|
||||||
|
statusIcon() {
|
||||||
|
const iconMap = {
|
||||||
|
1: 'home-fill',
|
||||||
|
2: 'close-circle-fill',
|
||||||
|
3: 'clock-fill'
|
||||||
|
};
|
||||||
|
return iconMap[this.detailInfo.status] || 'question-circle-fill';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
if (options && options.Id && options.local) {
|
if (options?.Id) {
|
||||||
this.Id = options.Id;
|
this.Id = options.Id;
|
||||||
this.local = options.local;
|
this.local = options.local || null;
|
||||||
}
|
this.getAroundDetail();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启用分享功能
|
||||||
|
wx.showShareMenu({
|
||||||
|
withShareTicket: true,
|
||||||
|
menus: ['shareAppMessage', 'shareTimeline']
|
||||||
|
});
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted() {
|
||||||
this.getAroundDetail();
|
this.getAroundDetail();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async getAroundDetail(){
|
formatDistance(distance) {
|
||||||
try {
|
if (!distance) return '未知距离';
|
||||||
const res = await get(`/api/v1/apps/surrounding/${this.Id}`);
|
if (distance < 1000) {
|
||||||
if (!res || !res.success) {
|
return `${distance}米`;
|
||||||
throw new Error('获取详情失败');
|
} else {
|
||||||
}
|
return `${(distance / 1000).toFixed(1)}公里`;
|
||||||
res.data.storeCover = IMAGE_BASE_URL + res.data.storeCover;
|
}
|
||||||
this.detailInfo = {...res.data};
|
},
|
||||||
} catch (err) {
|
|
||||||
console.error('获取详情失败:', err);
|
async getAroundDetail() {
|
||||||
}
|
try {
|
||||||
|
const res = await get(`/api/v1/apps/surrounding/${this.Id}`);
|
||||||
}
|
if (res?.success) {
|
||||||
|
this.detailInfo = {
|
||||||
|
...this.detailInfo,
|
||||||
|
...res.data,
|
||||||
|
storeCover: res.data.storeCover?.startsWith('http')
|
||||||
|
? res.data.storeCover
|
||||||
|
: IMAGE_BASE_URL + res.data.storeCover
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取详情失败:', error);
|
||||||
|
uni.showToast({ title: '获取详情失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理分享按钮点击
|
||||||
|
async handleShareClick() {
|
||||||
|
this.isRequestingLocation = true;
|
||||||
|
try {
|
||||||
|
await this.checkLocationPermission();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('位置授权异常:', error);
|
||||||
|
} finally {
|
||||||
|
this.isRequestingLocation = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查位置权限
|
||||||
|
async checkLocationPermission() {
|
||||||
|
const status = await this.getPermissionStatus();
|
||||||
|
if (status === 'granted') {
|
||||||
|
await this.getUserLocation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'none') {
|
||||||
|
// 首次请求授权
|
||||||
|
this.requestLocationPermission();
|
||||||
|
} else {
|
||||||
|
// 已拒绝过,显示提示
|
||||||
|
this.showLocationModal = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取权限状态
|
||||||
|
getPermissionStatus() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
uni.getSetting({
|
||||||
|
success: (res) => {
|
||||||
|
if (res.authSetting['scope.userLocation'] === undefined) {
|
||||||
|
resolve('none');
|
||||||
|
} else if (res.authSetting['scope.userLocation']) {
|
||||||
|
resolve('granted');
|
||||||
|
} else {
|
||||||
|
resolve('denied');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: () => resolve('none')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 请求位置授权
|
||||||
|
requestLocationPermission() {
|
||||||
|
uni.authorize({
|
||||||
|
scope: 'scope.userLocation',
|
||||||
|
success: () => this.getUserLocation(),
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('授权失败:', err);
|
||||||
|
this.showLocationModal = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取用户位置
|
||||||
|
getUserLocation() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.getLocation({
|
||||||
|
type: 'wgs84',
|
||||||
|
success: (res) => {
|
||||||
|
this.userLocation = {
|
||||||
|
latitude: res.latitude,
|
||||||
|
longitude: res.longitude
|
||||||
|
};
|
||||||
|
resolve(res);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('获取位置失败:', err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理授权确认
|
||||||
|
handleAuthConfirm() {
|
||||||
|
uni.openSetting({
|
||||||
|
success: (res) => {
|
||||||
|
if (res.authSetting['scope.userLocation']) {
|
||||||
|
this.getUserLocation().catch(() => {
|
||||||
|
uni.showToast({ title: '获取位置失败', icon: 'none' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.showLocationModal = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理授权取消
|
||||||
|
handleAuthCancel() {
|
||||||
|
this.showLocationModal = false;
|
||||||
|
uni.showToast({ title: '已取消位置授权', icon: 'none' });
|
||||||
|
},
|
||||||
|
|
||||||
|
// 微信分享好友
|
||||||
|
onShareAppMessage() {
|
||||||
|
return {
|
||||||
|
title: this.detailInfo.storeName || '发现一个好地方',
|
||||||
|
path: `/pages/surrounding/detail?Id=${this.Id}`,
|
||||||
|
imageUrl: this.detailInfo.storeCover,
|
||||||
|
success: () => {
|
||||||
|
this.logShareEvent();
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('分享失败:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// 朋友圈分享
|
||||||
|
onShareTimeline() {
|
||||||
|
return {
|
||||||
|
title: this.detailInfo.storeName || '发现一个好地方',
|
||||||
|
query: `Id=${this.Id}`,
|
||||||
|
imageUrl: this.detailInfo.storeCover
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
openMap() {
|
||||||
|
const { latitude, longitude, storeName, storeAddress } = this.detailInfo;
|
||||||
|
if (latitude && longitude) {
|
||||||
|
uni.openLocation({
|
||||||
|
latitude: Number(latitude),
|
||||||
|
longitude: Number(longitude),
|
||||||
|
name: storeName,
|
||||||
|
address: storeAddress,
|
||||||
|
success: () => {
|
||||||
|
console.log('打开地图成功');
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('打开地图失败:', err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '打开地图失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '暂无位置信息',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
previewImage(index) {
|
||||||
|
if (!this.detailInfo.detailImages || !this.detailInfo.detailImages.length) return;
|
||||||
|
|
||||||
|
uni.previewImage({
|
||||||
|
current: index,
|
||||||
|
urls: this.detailInfo.detailImages.map(img => img.startsWith('http') ? img : IMAGE_BASE_URL + img)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleShare() {
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: ['分享到微信', '分享到朋友圈', '复制链接'],
|
||||||
|
success: (res) => {
|
||||||
|
uni.showToast({
|
||||||
|
title: `已选择: ${res.tapIndex === 0 ? '微信' : res.tapIndex === 1 ? '朋友圈' : '复制链接'}`,
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
// 定义变量,便于统一修改和维护
|
$primary-color: #8dbafc;
|
||||||
$primary-color: #999;
|
$secondary-color: #00A699;
|
||||||
$title-size: 36rpx;
|
$dark-color: #2D2D2D;
|
||||||
$text-size: 26rpx;
|
$gray-color: #767676;
|
||||||
$spacing: 30rpx;
|
$light-gray: #F7F7F7;
|
||||||
$border-radius: 10rpx;
|
$border-color: #EBEBEB;
|
||||||
|
$success-color: #52c41a;
|
||||||
|
$warning-color: #faad14;
|
||||||
|
$danger-color: #ff4d4f;
|
||||||
|
|
||||||
.kfc-container {
|
$border-radius: 24rpx;
|
||||||
padding: 10px;
|
$spacing: 32rpx;
|
||||||
|
$card-padding: 40rpx;
|
||||||
|
$shadow-light: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
|
||||||
|
$shadow-medium: 0 10rpx 30rpx rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background-color: $light-gray;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding-bottom: 80rpx;
|
||||||
|
|
||||||
.container-img{
|
.cover-container {
|
||||||
max-width: 750rpx;
|
width: 100%;
|
||||||
height: 500rpx;
|
height: 500rpx;
|
||||||
// 图片样式
|
position: relative;
|
||||||
.kfc-bucket-img {
|
overflow: hidden;
|
||||||
width: 100%;
|
border-bottom-left-radius: $border-radius;
|
||||||
height: 100%;
|
border-bottom-right-radius: $border-radius;
|
||||||
border-radius: $border-radius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 店铺信息样式
|
|
||||||
.store-info {
|
|
||||||
margin-top: $spacing;
|
|
||||||
|
|
||||||
.store-name {
|
.store-cover {
|
||||||
font-size: $title-size;
|
width: 100%;
|
||||||
font-weight: bold;
|
height: 100%;
|
||||||
//margin-bottom: $spacing / 2;
|
transition: transform 0.5s ease;
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.business-hours,
|
.store-cover:active {
|
||||||
.address,
|
transform: scale(1.02);
|
||||||
.distance {
|
}
|
||||||
font-size: $text-size;
|
|
||||||
color: $primary-color;
|
.cover-overlay {
|
||||||
margin-bottom: 5px;
|
position: absolute;
|
||||||
display: block;
|
bottom: 0;
|
||||||
text{
|
left: 0;
|
||||||
color: #333;
|
right: 0;
|
||||||
// font-weight: 900;
|
height: 160rpx;
|
||||||
|
background: linear-gradient(transparent, rgba(0,0,0,0.6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-card {
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
margin: -60rpx $spacing 0;
|
||||||
|
padding: $card-padding;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
box-shadow: $shadow-medium;
|
||||||
|
transition: transform 0.3s, box-shadow 0.3s;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(2rpx);
|
||||||
|
box-shadow: $shadow-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
.store-name {
|
||||||
|
font-size: 44rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $dark-color;
|
||||||
|
line-height: 1.3;
|
||||||
|
word-break: break-word;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.price-distance-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
|
||||||
|
.price-tag {
|
||||||
|
background: #f5f8ff;
|
||||||
|
color: $primary-color;
|
||||||
|
padding: 4rpx 16rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.distance-badge {
|
||||||
|
background: $primary-color;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
padding: 8rpx 20rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 26rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
.u-icon {
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
gap: 12rpx;
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
background-color: #f0f7ff;
|
||||||
|
color: $primary-color;
|
||||||
|
padding: 8rpx 20rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
border: 1rpx solid rgba($primary-color, 0.3);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
height: 2rpx;
|
||||||
|
margin: $spacing 0;
|
||||||
|
|
||||||
|
&.dotted {
|
||||||
|
background: repeating-linear-gradient(to right, $border-color, $border-color 4rpx, transparent 4rpx, transparent 8rpx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
align-items: flex-start;
|
||||||
|
border-bottom: 2rpx dashed $border-color;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-icon {
|
||||||
|
margin-right: 20rpx;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $gray-color;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
padding-left: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: $dark-color;
|
||||||
|
line-height: 1.6;
|
||||||
|
word-break: break-all;
|
||||||
|
padding-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
margin-top: 40rpx;
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
border-radius: 50rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 30rpx;
|
||||||
|
border: none;
|
||||||
|
transition: all 0.3s;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
.u-icon {
|
||||||
|
margin-right: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
background: $primary-color;
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 6rpx 16rpx rgba(141, 186, 252, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-button {
|
||||||
|
background: #f0f7ff;
|
||||||
|
color: $primary-color;
|
||||||
|
border: 2rpx solid $primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section {
|
||||||
|
margin-top: $spacing;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $dark-color;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: $spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 280rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
|
transition: transform 0.3s, box-shadow 0.3s;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-image:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,147 +1,195 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<view v-for="(item, index) in list" :key="index" class="shop-card" @click="goPage('aroundDetail')">
|
<view v-for="(item, index) in list" :key="index" class="shop-card" @click="goDetail(item.id,item.distance)">
|
||||||
<u-cell :border="false" class="custom-cell">
|
<u-cell :border="false" class="custom-cell">
|
||||||
<image slot="icon" :src="item.image" mode="aspectFill" class="cell-image"></image>
|
<image slot="icon" :src="`${IMAGE_BASE_URL}`+item.storeCover" mode="aspectFill" class="cell-image"></image>
|
||||||
<view slot="title" class="cell-right">
|
<view slot="title" class="cell-right">
|
||||||
<view class="shop-name">{{ item.name }}</view>
|
<view class="shop-name">{{ item.storeName }}</view>
|
||||||
<view class="shop-info">
|
<view class="shop-info">
|
||||||
<!-- <text>★ {{ item.rating }} </text> -->
|
<text class="price">{{ item.price > 0 ? `人均¥${item.price}` : '免费开放' }}</text>
|
||||||
<text>人均¥{{ item.perCapita }}</text>
|
<text class="distance">{{ formatDistance(item.distance) }}</text>
|
||||||
<text class="distance">直线{{ item.distance }}m</text>
|
</view>
|
||||||
</view>
|
<view class="shop-desc" v-if="item.content">
|
||||||
<view class="desc-container">
|
<text>{{ truncateContent(item.content) }}</text>
|
||||||
<text class="desc">{{ "“" + item.desc + "”" }}</text>
|
</view>
|
||||||
</view>
|
<view class="open-time" v-if="item.openAt">
|
||||||
</view>
|
<u-icon name="clock" size="24" color="#8a8a8a"></u-icon>
|
||||||
</u-cell>
|
<text>开放时间:{{ item.openAt }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</u-cell>
|
||||||
|
</view>
|
||||||
<script>
|
</view>
|
||||||
export default {
|
</template>
|
||||||
data() {
|
|
||||||
return {
|
<script>
|
||||||
list: [{
|
import { get, post } from '@/utils/request';
|
||||||
image: "/static/imgs/service/hema.png", // 假设图片放在 static 目录,根据实际路径调整
|
import { IMAGE_BASE_URL, BASE_URL } from '@/utils/config';
|
||||||
name: '肯德基(南通紫琅店)',
|
|
||||||
rating: 4.8,
|
export default {
|
||||||
monthSales: 4683,
|
data() {
|
||||||
perCapita: 30,
|
return {
|
||||||
distance: 221,
|
IMAGE_BASE_URL,
|
||||||
desc: '超棒,同学们都说好吃'
|
Id: null,
|
||||||
},
|
latitude: null,
|
||||||
{
|
longitude: null,
|
||||||
image: "/static/imgs/service/hema.png",
|
list: []
|
||||||
name: '汉堡王(南通紫琅店)',
|
};
|
||||||
rating: 4.8,
|
|
||||||
monthSales: 4683,
|
|
||||||
perCapita: 30,
|
|
||||||
distance: 221,
|
|
||||||
desc: '超棒,同学们都说好吃'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
image: "/static/imgs/service/hema.png",
|
|
||||||
name: '瑞幸咖啡(南通紫琅店)',
|
|
||||||
rating: 4.8,
|
|
||||||
monthSales: 4683,
|
|
||||||
perCapita: 30,
|
|
||||||
distance: 221,
|
|
||||||
desc: '超棒,同学们都说不错'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
image: "/static/imgs/service/hema.png",
|
|
||||||
name: '绿茶餐厅',
|
|
||||||
rating: 4.8,
|
|
||||||
monthSales: 4683,
|
|
||||||
perCapita: 30,
|
|
||||||
distance: 221,
|
|
||||||
desc: '超棒,同学们都说好吃'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad(options) {
|
||||||
|
if (options && options.Id && options.latitude && options.longitude) {
|
||||||
|
this.Id = options.Id;
|
||||||
|
this.longitude = options.longitude;
|
||||||
|
this.latitude = options.latitude;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getAroundList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 跳转不同页面
|
async getAroundList() {
|
||||||
goPage(page) {
|
let params = {
|
||||||
console.log("==进来", page)
|
current: 1,
|
||||||
uni.navigateTo({
|
pageSize: 100,
|
||||||
url: `/pages/${page}/index`,
|
longitude: this.longitude,
|
||||||
success: () => {
|
latitude: this.latitude,
|
||||||
console.log('切换到tabBar页面成功');
|
typeId: this.Id,
|
||||||
},
|
}
|
||||||
fail: (err) => {
|
try {
|
||||||
console.error('切换到tabBar页面失败:', err);
|
const res = await get('/api/v1/apps/surrounding', params);
|
||||||
|
if (!res || !res.success) {
|
||||||
|
throw new Error('获取列表失败');
|
||||||
}
|
}
|
||||||
|
this.list = res.data || [];
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取列表失败:', err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goDetail(id, local) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/aroundDetail/index?Id=${id}&local=${local}`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
formatDistance(distance) {
|
||||||
};
|
if (distance >= 1000) {
|
||||||
</script>
|
return `距离${(distance / 1000).toFixed(1)}km`;
|
||||||
|
}
|
||||||
<style lang="scss" scoped>
|
return `距离${Math.round(distance)}m`;
|
||||||
.container {
|
},
|
||||||
padding: 10px;
|
truncateContent(content) {
|
||||||
|
if (content.length > 60) {
|
||||||
.shop-card {
|
return content.substring(0, 60) + '...';
|
||||||
margin-bottom: 15px;
|
}
|
||||||
border-radius: 10px;
|
return content;
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
}
|
||||||
background-color: #fff;
|
}
|
||||||
}
|
};
|
||||||
|
</script>
|
||||||
.custom-cell {
|
|
||||||
display: flex;
|
<style lang="scss" scoped>
|
||||||
align-items: center;
|
.container {
|
||||||
padding: 12px 15px;
|
padding: 20rpx;
|
||||||
}
|
background-color: #f8f9fa;
|
||||||
|
min-height: 100vh;
|
||||||
.cell-image {
|
}
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
.shop-card {
|
||||||
border-radius: 8px;
|
margin-bottom: 24rpx;
|
||||||
}
|
border-radius: 16rpx;
|
||||||
|
background-color: #fff;
|
||||||
.cell-right {
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
|
||||||
display: flex;
|
overflow: hidden;
|
||||||
flex-direction: column;
|
transition: all 0.2s ease;
|
||||||
margin-left: 10px;
|
|
||||||
|
&:active {
|
||||||
.shop-name {
|
transform: scale(0.98);
|
||||||
font-size: 16px;
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
font-weight: bold;
|
}
|
||||||
margin-bottom: 4px;
|
}
|
||||||
}
|
|
||||||
|
.custom-cell {
|
||||||
.shop-info {
|
padding: 0;
|
||||||
font-size: 12px;
|
}
|
||||||
color: #999;
|
|
||||||
margin-bottom: 4px;
|
.cell-image {
|
||||||
display: flex;
|
width: 240rpx;
|
||||||
flex-wrap: wrap;
|
height: 240rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
.distance {
|
background-color: #f5f5f5;
|
||||||
margin-left: auto;
|
}
|
||||||
}
|
|
||||||
}
|
.cell-right {
|
||||||
|
padding: 24rpx;
|
||||||
.desc-container {
|
flex: 1;
|
||||||
display: inline-block;
|
}
|
||||||
margin-top: 5px;
|
|
||||||
}
|
.shop-name {
|
||||||
|
font-size: 32rpx;
|
||||||
.desc {
|
font-weight: 600;
|
||||||
font-size: 12px;
|
color: #2c3e50;
|
||||||
padding: 5px 10px;
|
margin-bottom: 12rpx;
|
||||||
color: rgba(128, 128, 128, 1);
|
line-height: 1.4;
|
||||||
background: rgba(229, 229, 229, 0.5);
|
}
|
||||||
border-radius: 4px;
|
|
||||||
display: inline-block;
|
.shop-info {
|
||||||
}
|
display: flex;
|
||||||
}
|
justify-content: space-between;
|
||||||
}
|
align-items: center;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
|
||||||
|
.price {
|
||||||
|
color: #3498db;
|
||||||
|
font-weight: 500;
|
||||||
|
background-color: rgba(52, 152, 219, 0.1);
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distance {
|
||||||
|
color: #7f8c8d;
|
||||||
|
font-size: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%237f8c8d"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>');
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
margin-right: 6rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-desc {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #555;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 12rpx 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.open-time {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #7f8c8d;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
|
||||||
|
.u-icon {
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -87,7 +87,7 @@
|
|||||||
<view class="card-bg"></view>
|
<view class="card-bg"></view>
|
||||||
</view>
|
</view>
|
||||||
<image class="card-icon" src="/static/imgs/index/bangzhu.png"
|
<image class="card-icon" src="/static/imgs/index/bangzhu.png"
|
||||||
style="width: 76rpx;height: 76rpx;"></image>
|
style="width: 76rpx;height: 76rpx; bottom: 32rpx;" ></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="service-card card-2" @click="goDetail('serviceTickets')" hover-class="card-hover">
|
<view class="service-card card-2" @click="goDetail('serviceTickets')" hover-class="card-hover">
|
||||||
@ -503,7 +503,7 @@
|
|||||||
width: 90rpx;
|
width: 90rpx;
|
||||||
height: 90rpx;
|
height: 90rpx;
|
||||||
right: 24rpx;
|
right: 24rpx;
|
||||||
bottom: 24rpx;
|
bottom: 20rpx;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1740
pages/mine/index.vue
1740
pages/mine/index.vue
File diff suppressed because it is too large
Load Diff
@ -1,104 +1,96 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<view class="nearby-page">
|
<view class="nearby-page">
|
||||||
|
<!-- 附近美食模块 -->
|
||||||
<!-- 附近美食模块 -->
|
<view class="section shadow" v-for="(item,index) in aroundList" :key="index">
|
||||||
<view class="section shadow" v-for="(item,index) in aroundList" :key="index">
|
<view class="section-header" @click="goPage(item.id)">
|
||||||
<view class="section-header" @click="goPage('aroundList')">
|
<view class="section-title">{{item.label}}</view>
|
||||||
<view class="section-title">{{item.label}}</view>
|
<u-icon name="arrow-right" size="20" color="#999"></u-icon>
|
||||||
<u-icon name="arrow-right" size="20" color="#999"></u-icon>
|
</view>
|
||||||
</view>
|
<!-- 优化:使用网格布局实现更稳定的四列 -->
|
||||||
<view class="goods-list">
|
<view class="goods-grid">
|
||||||
<view class="goods-item" v-for="i in item.serviceList" @click="goDetail(i.id,i.distance)">
|
<view class="goods-item" v-for="i in item.serviceList" :key="i.id"
|
||||||
<image class="goods-img" :src="`${IMAGE_BASE_URL}`+i.storeCover"></image>
|
@click="goDetail(i.id,i.distance)">
|
||||||
<text class="goods-name">{{ i.storeName }}</text>
|
<image class="goods-img" :src="`${IMAGE_BASE_URL}`+i.storeCover" mode="aspectFill"></image>
|
||||||
<view class="goods-distance">
|
<view class="goods-info">
|
||||||
<u-icon name="map" size="18" color="#c6d8fa"></u-icon>
|
<text class="goods-name">{{ i.storeName }}</text>
|
||||||
{{ i.distance + '米'}}
|
<view class="goods-distance">
|
||||||
</view>
|
<u-icon name="map" size="18" color="#c6d8fa"></u-icon>
|
||||||
</view>
|
<text>{{ formatDistance(i.distance) }} </text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<Footer></Footer>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
<Footer></Footer>
|
||||||
|
</view>
|
||||||
<script>
|
</template>
|
||||||
import Footer from '@/components/footer_common.vue';
|
|
||||||
import {
|
<script>
|
||||||
get,
|
import Footer from '@/components/footer_common.vue';
|
||||||
post
|
import { get, post } from '@/utils/request';
|
||||||
} from '@/utils/request';
|
import { IMAGE_BASE_URL, BASE_URL } from '@/utils/config';
|
||||||
import {
|
import { formatTime, formatRelativeTime } from '@/utils/timeFormat';
|
||||||
IMAGE_BASE_URL,
|
|
||||||
BASE_URL
|
export default {
|
||||||
} from '@/utils/config';
|
components: { Footer },
|
||||||
import {
|
data() {
|
||||||
formatTime,
|
|
||||||
formatRelativeTime
|
|
||||||
} from '@/utils/timeFormat';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Footer
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
return {
|
||||||
IMAGE_BASE_URL,
|
IMAGE_BASE_URL,
|
||||||
aroundList:[],
|
aroundList:[],
|
||||||
}
|
longitude:null,
|
||||||
},
|
latitude:null,
|
||||||
onLoad() {
|
}
|
||||||
|
},
|
||||||
},
|
onLoad() {},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.surroundingList();
|
this.surroundingList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 跳转不同页面
|
goDetail(id, local) {
|
||||||
goDetail(id,local) {
|
uni.navigateTo({
|
||||||
uni.navigateTo({
|
url: `/pages/aroundDetail/index?Id=${id}&local=${local}`,
|
||||||
url: `/pages/aroundDetail/index?Id=${id}&local=${local}`,
|
});
|
||||||
});
|
},
|
||||||
},
|
goPage(id){
|
||||||
async surroundingList() {
|
uni.navigateTo({
|
||||||
const position = await this.getCurrentPosition();
|
url: `/pages/aroundList/index?Id=${id}&latitude=${this.latitude}&longitude=${this.longitude}`,
|
||||||
let params = {
|
});
|
||||||
current: 1,
|
},
|
||||||
pageSize: 10,
|
formatDistance(distance) {
|
||||||
longitude: position.longitude,
|
if (!distance) return '未知距离';
|
||||||
latitude: position.latitude,
|
if (distance < 1000) {
|
||||||
|
return `${distance}米`;
|
||||||
// statusArray:"2,3,4",
|
} else {
|
||||||
};
|
return `${(distance / 1000).toFixed(1)}公里`;
|
||||||
try {
|
}
|
||||||
const res = await get('/api/v1/apps/surrounding-type', params);
|
},
|
||||||
if (!res || !res.success) {
|
async surroundingList() {
|
||||||
throw new Error('获取周边服务失败');
|
const position = await this.getCurrentPosition();
|
||||||
}
|
this.latitude = position.latitude;
|
||||||
console.log("---res", res)
|
this.longitude = position.longitude;
|
||||||
this.aroundList = [...res.data];
|
let params = {
|
||||||
// res.data.forEach(item => {
|
current: 1,
|
||||||
// let imgUrl = item.cover;
|
pageSize: 10,
|
||||||
// if (!imgUrl.startsWith('http') && !imgUrl.startsWith('https')) {
|
longitude: position.longitude,
|
||||||
// item.cover = IMAGE_BASE_URL + imgUrl;
|
latitude: position.latitude
|
||||||
// }
|
};
|
||||||
// })
|
try {
|
||||||
// this.activityList = [...res.data];
|
const res = await get('/api/v1/apps/surrounding-type', params);
|
||||||
|
if (!res || !res.success) {
|
||||||
} catch (err) {
|
throw new Error('获取周边服务失败');
|
||||||
console.error('获取周边服务失败:', err);
|
}
|
||||||
}
|
this.aroundList = [...res.data];
|
||||||
|
} catch (err) {
|
||||||
},
|
console.error('获取周边服务失败:', err);
|
||||||
|
}
|
||||||
|
},
|
||||||
getCurrentPosition() {
|
getCurrentPosition() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
uni.getLocation({
|
uni.getLocation({
|
||||||
type: 'wgs84',
|
type: 'wgs84',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('当前位置:', res);
|
|
||||||
resolve({
|
resolve({
|
||||||
longitude: res.longitude,
|
longitude: res.longitude,
|
||||||
latitude: res.latitude
|
latitude: res.latitude
|
||||||
@ -114,106 +106,136 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
min-height: 100vh;
|
||||||
background: #f8faff;
|
background: #f8faff;
|
||||||
padding-bottom: 400rpx;
|
padding-bottom: 120rpx;
|
||||||
|
}
|
||||||
.nearby-page {
|
|
||||||
padding: 20rpx;
|
.nearby-page {
|
||||||
width: 100%;
|
padding: 20rpx 24rpx;
|
||||||
box-sizing: border-box;
|
width: 100%;
|
||||||
}
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
// 优化后的section样式
|
}
|
||||||
.section {
|
|
||||||
margin: 30rpx 0; // 增大上下间距
|
.section {
|
||||||
padding: 20rpx;
|
margin: 32rpx 0;
|
||||||
border-radius: 16rpx; // 圆角更圆润
|
padding: 24rpx;
|
||||||
background-color: #fff;
|
border-radius: 20rpx;
|
||||||
transition: transform 0.3s ease; // 添加过渡效果
|
background-color: #fff;
|
||||||
|
transition: all 0.3s ease;
|
||||||
// 鼠标悬停效果
|
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06);
|
||||||
&:hover {
|
|
||||||
transform: translateY(-5rpx);
|
&:active {
|
||||||
}
|
transform: scale(0.98);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 添加阴影效果
|
|
||||||
.shadow {
|
.section-header {
|
||||||
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.08); // 更细腻的阴影
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
.section-header {
|
margin-bottom: 24rpx;
|
||||||
display: flex;
|
padding: 0 10rpx;
|
||||||
align-items: center;
|
}
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 20rpx;
|
.section-title {
|
||||||
padding: 0 10rpx;
|
font-size: 36rpx;
|
||||||
}
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
.section-title {
|
border-left: 8rpx solid #8dbafc;
|
||||||
font-size: 34rpx;
|
padding-left: 20rpx;
|
||||||
font-weight: 600;
|
line-height: 1.4;
|
||||||
color: #333;
|
}
|
||||||
/* 修正后的边框样式 */
|
|
||||||
border-left: 10rpx solid rgba(141, 186, 252, 0.55);
|
/* 优化:使用grid布局实现更稳定的四列分布 */
|
||||||
padding-left: 16rpx;
|
.goods-grid {
|
||||||
}
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr); /* 强制四列布局 */
|
||||||
.goods-list {
|
gap: 10rpx; /* 统一间距 */
|
||||||
display: flex;
|
}
|
||||||
flex-wrap: wrap;
|
|
||||||
// justify-content:;
|
.goods-item {
|
||||||
}
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
.goods-item {
|
background: #fff;
|
||||||
width: 23%;
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
text-align: center;
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
margin-bottom: 20rpx;
|
|
||||||
}
|
&:active {
|
||||||
|
transform: scale(0.96);
|
||||||
.goods-img {
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
width: 150rpx;
|
}
|
||||||
height: 150rpx;
|
}
|
||||||
border-radius: 12rpx;
|
|
||||||
// margin-bottom: 10rpx;
|
.goods-img {
|
||||||
object-fit: cover; // 确保图片比例正确
|
width: 100%;
|
||||||
}
|
height: 160rpx; /* 优化图片高度比例 */
|
||||||
|
border-radius: 12rpx 12rpx 0 0;
|
||||||
.goods-name {
|
display: block;
|
||||||
font-size: 24rpx;
|
object-fit: cover; /* 确保图片不变形 */
|
||||||
display: block;
|
}
|
||||||
margin-bottom: 6rpx;
|
|
||||||
color: #333;
|
.goods-info {
|
||||||
white-space: nowrap;
|
padding: 16rpx;
|
||||||
overflow: hidden;
|
}
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: left;
|
.goods-name {
|
||||||
}
|
font-size: 24rpx;
|
||||||
|
color: #333;
|
||||||
// 新增样式:使图标和文字在同一行显示
|
font-weight: 500;
|
||||||
.goods-distance {
|
display: -webkit-box;
|
||||||
font-size: 22rpx;
|
-webkit-line-clamp: 2;
|
||||||
color: #999;
|
-webkit-box-orient: vertical;
|
||||||
display: flex;
|
overflow: hidden;
|
||||||
justify-content: flex-end;
|
text-overflow: ellipsis;
|
||||||
align-items: center;
|
line-height: 1.5;
|
||||||
text-align: right;
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
// 调整图标与文字之间的间距
|
|
||||||
.u-icon {
|
.goods-distance {
|
||||||
margin-right: 6rpx;
|
font-size: 22rpx;
|
||||||
font-size: 18rpx;
|
color: #666;
|
||||||
}
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
}
|
|
||||||
|
.u-icon {
|
||||||
|
margin-right: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式优化:小屏幕自动调整为两列 */
|
||||||
|
@media (max-width: 750rpx) { /* 适配常见手机屏幕 */
|
||||||
|
.goods-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr); /* 小屏幕两列 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-img {
|
||||||
|
height: 200rpx; /* 增加小屏幕图片高度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-name {
|
||||||
|
font-size: 26rpx; /* 增大字体 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 针对超小屏幕的额外适配 */
|
||||||
|
@media (max-width: 375rpx) {
|
||||||
|
.goods-grid {
|
||||||
|
grid-template-columns: 1fr; /* 极小屏幕单列 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-img {
|
||||||
|
height: 240rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -55,8 +55,7 @@
|
|||||||
<!-- 只在一级评论显示折叠按钮 -->
|
<!-- 只在一级评论显示折叠按钮 -->
|
||||||
<text class="fold-btn" v-if="comment.level === 0 && hasChildren(comment)"
|
<text class="fold-btn" v-if="comment.level === 0 && hasChildren(comment)"
|
||||||
@click="toggleFold(comment.id)">
|
@click="toggleFold(comment.id)">
|
||||||
{{ foldedComments.includes(comment.id) ? '展开' : '折叠' }}
|
{{ getChildCount(comment) }}条回复{{ foldedComments.includes(comment.id) ? '展开' : '折叠' }}
|
||||||
{{ getChildCount(comment) }}条回复
|
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -84,234 +83,269 @@
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {
|
import {
|
||||||
get,
|
get,
|
||||||
post
|
post
|
||||||
} from '@/utils/request';
|
} from '@/utils/request';
|
||||||
import {
|
import {
|
||||||
formatTime
|
formatTime
|
||||||
} from '@/utils/timeFormat';
|
} from '@/utils/timeFormat';
|
||||||
// import uniIcons from '@/components/uni-icons/uni-icons.vue';
|
// import uniIcons from '@/components/uni-icons/uni-icons.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
// uniIcons
|
// uniIcons
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
Id: '',
|
Id: '',
|
||||||
flatComments: [],
|
flatComments: [],
|
||||||
newComment: "",
|
newComment: "",
|
||||||
replyingTo: null,
|
replyingTo: null,
|
||||||
postData: {
|
postData: {
|
||||||
histories: []
|
histories: []
|
||||||
},
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
error: '',
|
error: '',
|
||||||
submitting: false,
|
submitting: false,
|
||||||
foldedComments: [], // 存储被折叠的评论ID
|
foldedComments: [], // 存储被折叠的评论ID
|
||||||
showLoadMore: false, // 是否显示加载更多
|
showLoadMore: false, // 是否显示加载更多
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
if (options && options.Id) {
|
if (options && options.Id) {
|
||||||
this.Id = options.Id;
|
this.Id = options.Id;
|
||||||
this.getDetail();
|
this.getDetail();
|
||||||
} else {
|
} else {
|
||||||
this.error = '缺少内容ID';
|
this.error = '缺少内容ID';
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 图片预览
|
// 检查用户是否已登录
|
||||||
previewImage(index) {
|
checkLogin() {
|
||||||
if (!this.postData.images || !this.postData.images.length) return;
|
const token = uni.getStorageSync('token'); // 假设token存储在本地
|
||||||
|
return !!token;
|
||||||
uni.previewImage({
|
},
|
||||||
current: index,
|
|
||||||
urls: this.postData.images
|
// 跳转到登录页面
|
||||||
});
|
goToLogin() {
|
||||||
},
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
// 检查评论是否有子评论
|
content: '请先登录再进行评论',
|
||||||
hasChildren(comment) {
|
confirmText: '前往登录',
|
||||||
return comment.children && comment.children.length > 0;
|
success: (res) => {
|
||||||
},
|
if (res.confirm) {
|
||||||
|
uni.navigateTo({
|
||||||
// 获取子评论数量
|
url: '/pages/mine/index' // 替换为实际的个人中心页面路径
|
||||||
getChildCount(comment) {
|
});
|
||||||
if (!this.hasChildren(comment)) return 0;
|
}
|
||||||
return comment.children.length;
|
}
|
||||||
},
|
});
|
||||||
|
},
|
||||||
// 切换折叠状态
|
|
||||||
toggleFold(commentId) {
|
// 图片预览
|
||||||
const index = this.foldedComments.indexOf(commentId);
|
previewImage(index) {
|
||||||
if (index === -1) {
|
if (!this.postData.images || !this.postData.images.length) return;
|
||||||
this.foldedComments.push(commentId);
|
|
||||||
} else {
|
uni.previewImage({
|
||||||
this.foldedComments.splice(index, 1);
|
current: index,
|
||||||
}
|
urls: this.postData.images
|
||||||
this.updateFlatComments();
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 更新平铺评论列表(处理折叠逻辑)
|
// 检查评论是否有子评论
|
||||||
updateFlatComments() {
|
hasChildren(comment) {
|
||||||
const flattenWithFold = (comments, level = 0, parentFolded = false) => {
|
return comment.children && comment.children.length > 0;
|
||||||
return comments.reduce((arr, comment) => {
|
},
|
||||||
// 只有一级评论才检查折叠状态
|
|
||||||
const isFolded = level === 0 && this.foldedComments.includes(comment.id);
|
// 获取子评论数量
|
||||||
const currentFolded = parentFolded || isFolded;
|
getChildCount(comment) {
|
||||||
|
if (!this.hasChildren(comment)) return 0;
|
||||||
if (!parentFolded) {
|
return comment.children.length;
|
||||||
arr.push({
|
},
|
||||||
...comment,
|
|
||||||
level,
|
// 切换折叠状态
|
||||||
isFolded
|
toggleFold(commentId) {
|
||||||
});
|
const index = this.foldedComments.indexOf(commentId);
|
||||||
}
|
if (index === -1) {
|
||||||
|
this.foldedComments.push(commentId);
|
||||||
// 如果是一级评论且被折叠,则不显示子评论
|
} else {
|
||||||
if (comment.children && comment.children.length && !(level === 0 && isFolded)) {
|
this.foldedComments.splice(index, 1);
|
||||||
arr.push(...flattenWithFold(comment.children, level + 1, currentFolded));
|
}
|
||||||
}
|
this.updateFlatComments();
|
||||||
|
},
|
||||||
return arr;
|
|
||||||
}, []);
|
// 更新平铺评论列表(处理折叠逻辑)
|
||||||
};
|
updateFlatComments() {
|
||||||
|
const flattenWithFold = (comments, level = 0, parentFolded = false) => {
|
||||||
this.flatComments = flattenWithFold(this.postData.histories || []);
|
return comments.reduce((arr, comment) => {
|
||||||
},
|
// 只有一级评论才检查折叠状态
|
||||||
// 加载更多评论
|
const isFolded = level === 0 && this.foldedComments.includes(comment.id);
|
||||||
loadMoreComments() {
|
const currentFolded = parentFolded || isFolded;
|
||||||
this.currentPage++;
|
|
||||||
// 这里应该调用API获取更多评论,示例中简化处理
|
if (!parentFolded) {
|
||||||
this.getDetail();
|
arr.push({
|
||||||
},
|
...comment,
|
||||||
|
level,
|
||||||
// 获取帖子详情
|
isFolded
|
||||||
async getDetail() {
|
});
|
||||||
try {
|
}
|
||||||
this.loading = true;
|
|
||||||
const res = await get(`/api/v1/apps/home/reciprocities/${this.Id}`, {
|
// 如果是一级评论且被折叠,则不显示子评论
|
||||||
page: this.currentPage,
|
if (comment.children && comment.children.length && !(level === 0 && isFolded)) {
|
||||||
pageSize: this.pageSize
|
arr.push(...flattenWithFold(comment.children, level + 1, currentFolded));
|
||||||
});
|
}
|
||||||
if (!res?.success) throw new Error(res?.message || '获取详情失败');
|
|
||||||
|
return arr;
|
||||||
if (this.currentPage === 1) {
|
}, []);
|
||||||
this.postData = res.data;
|
};
|
||||||
} else {
|
|
||||||
// 合并评论数据
|
this.flatComments = flattenWithFold(this.postData.histories || []);
|
||||||
this.postData.histories = [...this.postData.histories, ...res.data.histories];
|
},
|
||||||
}
|
// 加载更多评论
|
||||||
|
loadMoreComments() {
|
||||||
this.updateFlatComments();
|
this.currentPage++;
|
||||||
this.showLoadMore = (res.data.histories.length >= this.pageSize);
|
// 这里应该调用API获取更多评论,示例中简化处理
|
||||||
} catch (err) {
|
this.getDetail();
|
||||||
console.error('获取详情失败:', err);
|
},
|
||||||
this.error = '加载失败,请重试';
|
|
||||||
} finally {
|
// 获取帖子详情
|
||||||
this.loading = false;
|
async getDetail() {
|
||||||
}
|
try {
|
||||||
},
|
this.loading = true;
|
||||||
|
const res = await get(`/api/v1/apps/home/reciprocities/${this.Id}`, {
|
||||||
formatTime(isoTime) {
|
page: this.currentPage,
|
||||||
if (!isoTime) return '';
|
pageSize: this.pageSize
|
||||||
return formatTime(isoTime); // 使用统一的格式化方法
|
});
|
||||||
},
|
if (!res?.success) throw new Error(res?.message || '获取详情失败');
|
||||||
|
|
||||||
// 计算总评论数
|
if (this.currentPage === 1) {
|
||||||
getTotalComments() {
|
this.postData = res.data;
|
||||||
const count = (comments) => {
|
} else {
|
||||||
return comments.reduce((total, comment) => {
|
// 合并评论数据
|
||||||
return total + 1 + (comment.children ? count(comment.children) : 0);
|
this.postData.histories = [...this.postData.histories, ...res.data.histories];
|
||||||
}, 0);
|
}
|
||||||
};
|
|
||||||
return count(this.postData.histories || []);
|
this.updateFlatComments();
|
||||||
},
|
this.showLoadMore = (res.data.histories.length >= this.pageSize);
|
||||||
|
} catch (err) {
|
||||||
// 提交评论/回复
|
console.error('获取详情失败:', err);
|
||||||
async submitComment() {
|
this.error = '加载失败,请重试';
|
||||||
const content = this.newComment.trim();
|
} finally {
|
||||||
if (!content || this.submitting) return;
|
this.loading = false;
|
||||||
|
}
|
||||||
try {
|
},
|
||||||
this.submitting = true;
|
|
||||||
const requestData = {
|
formatTime(isoTime) {
|
||||||
content: content,
|
if (!isoTime) return '';
|
||||||
reciprocityId: this.postData.id,
|
return formatTime(isoTime); // 使用统一的格式化方法
|
||||||
rootHistoryId: this.replyingTo || 0
|
},
|
||||||
};
|
|
||||||
|
// 计算总评论数
|
||||||
const response = await post(
|
getTotalComments() {
|
||||||
'/api/v1/app_auth/reciprocities/send',
|
const count = (comments) => {
|
||||||
requestData
|
return comments.reduce((total, comment) => {
|
||||||
);
|
return total + 1 + (comment.children ? count(comment.children) : 0);
|
||||||
|
}, 0);
|
||||||
if (response.success) {
|
};
|
||||||
this.newComment = '';
|
return count(this.postData.histories || []);
|
||||||
this.replyingTo = null;
|
},
|
||||||
uni.showToast({
|
|
||||||
title: '评论成功',
|
// 提交评论/回复
|
||||||
icon: 'success',
|
async submitComment() {
|
||||||
duration: 1500
|
// 检查用户是否已登录
|
||||||
});
|
if (!this.checkLogin()) {
|
||||||
setTimeout(() => {
|
this.goToLogin();
|
||||||
this.currentPage = 1;
|
this.newComment = "";
|
||||||
this.getDetail();
|
return;
|
||||||
}, 500);
|
}
|
||||||
} else {
|
|
||||||
throw new Error('评论提交失败,请重试');
|
const content = this.newComment.trim();
|
||||||
}
|
if (!content || this.submitting) return;
|
||||||
} catch (err) {
|
|
||||||
console.error('评论失败:', err);
|
try {
|
||||||
uni.showToast({
|
this.submitting = true;
|
||||||
title: err.message || '评论失败,请重试',
|
const requestData = {
|
||||||
icon: 'none',
|
content: content,
|
||||||
duration: 2000
|
reciprocityId: this.postData.id,
|
||||||
});
|
rootHistoryId: this.replyingTo || ""
|
||||||
} finally {
|
};
|
||||||
this.submitting = false;
|
|
||||||
}
|
const response = await post(
|
||||||
},
|
'/api/v1/app_auth/reciprocities/send',
|
||||||
|
requestData
|
||||||
// 处理回复按钮点击
|
);
|
||||||
handleReply(commentId) {
|
|
||||||
this.replyingTo = commentId;
|
if (response.success) {
|
||||||
this.$nextTick(() => {
|
this.newComment = '';
|
||||||
uni.createSelectorQuery().select('.comment-input')
|
this.replyingTo = null;
|
||||||
.fields({
|
uni.showToast({
|
||||||
focus: true
|
title: '评论成功',
|
||||||
}, () => {}).exec();
|
icon: 'success',
|
||||||
});
|
duration: 1500
|
||||||
},
|
});
|
||||||
|
setTimeout(() => {
|
||||||
// 取消回复
|
this.currentPage = 1;
|
||||||
cancelReply() {
|
this.getDetail();
|
||||||
this.replyingTo = null;
|
}, 500);
|
||||||
this.newComment = '';
|
} else {
|
||||||
},
|
throw new Error('评论提交失败,请重试');
|
||||||
|
}
|
||||||
// 获取被回复人的用户名
|
} catch (err) {
|
||||||
getReplyUsername(commentId) {
|
console.error('评论失败:', err);
|
||||||
const findUser = (comments) => {
|
uni.showToast({
|
||||||
for (const c of comments) {
|
title: err.message || '评论失败,请重试',
|
||||||
if (c.id === commentId) return c.pusherName;
|
icon: 'none',
|
||||||
if (c.children && c.children.length) {
|
duration: 2000
|
||||||
const user = findUser(c.children);
|
});
|
||||||
if (user) return user;
|
} finally {
|
||||||
}
|
this.submitting = false;
|
||||||
}
|
}
|
||||||
return "";
|
},
|
||||||
};
|
|
||||||
return findUser(this.postData.histories);
|
// 处理回复按钮点击
|
||||||
}
|
handleReply(commentId) {
|
||||||
}
|
// 检查用户是否已登录
|
||||||
};
|
if (!this.checkLogin()) {
|
||||||
|
this.goToLogin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.replyingTo = commentId;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
uni.createSelectorQuery().select('.comment-input')
|
||||||
|
.fields({
|
||||||
|
focus: true
|
||||||
|
}, () => {}).exec();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 取消回复
|
||||||
|
cancelReply() {
|
||||||
|
this.replyingTo = null;
|
||||||
|
this.newComment = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取被回复人的用户名
|
||||||
|
getReplyUsername(commentId) {
|
||||||
|
const findUser = (comments) => {
|
||||||
|
for (const c of comments) {
|
||||||
|
if (c.id === commentId) return c.pusherName;
|
||||||
|
if (c.children && c.children.length) {
|
||||||
|
const user = findUser(c.children);
|
||||||
|
if (user) return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
return findUser(this.postData.histories);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/* 使用SCSS增强样式能力 */
|
/* 使用SCSS增强样式能力 */
|
||||||
@ -322,33 +356,35 @@
|
|||||||
$border-color: #e2e8f0; // 更柔和的边框色
|
$border-color: #e2e8f0; // 更柔和的边框色
|
||||||
$bg-color: #f7fafc; // 浅灰色背景
|
$bg-color: #f7fafc; // 浅灰色背景
|
||||||
$comment-bg: #ffffff; // 白色评论背景
|
$comment-bg: #ffffff; // 白色评论背景
|
||||||
$shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.05); // 小阴影
|
$shadow-sm: 0 1rpx 3rpx rgba(0, 0, 0, 0.05); // 小阴影
|
||||||
$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.08); // 中等阴影
|
$shadow-md: 0 4rpx 6rpx rgba(0, 0, 0, 0.08); // 中等阴影
|
||||||
$radius-lg: 12px; // 大圆角
|
$radius-lg: 24rpx; // 大圆角
|
||||||
$radius-md: 8px; // 中圆角
|
$radius-md: 16rpx; // 中圆角
|
||||||
$radius-sm: 4px; // 小圆角
|
$radius-sm: 8rpx; // 小圆角
|
||||||
$transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); // 平滑过渡
|
$transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); // 平滑过渡
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: 16px;
|
padding: 32rpx;
|
||||||
background-color: $bg-color;
|
background-color: $bg-color;
|
||||||
|
/* 增加底部内边距,确保内容不被输入框遮挡 */
|
||||||
|
padding-bottom: 240rpx;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding-bottom: 120px;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 主帖样式优化 */
|
/* 主帖样式优化 */
|
||||||
.post-container {
|
.post-container {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: $radius-lg;
|
border-radius: $radius-lg;
|
||||||
padding: 24px;
|
padding: 48rpx;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 40rpx;
|
||||||
box-shadow: $shadow-sm;
|
box-shadow: $shadow-sm;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
border: 1px solid $border-color;
|
border: 1rpx solid $border-color;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: $shadow-md;
|
box-shadow: $shadow-md;
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1rpx);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
@ -359,15 +395,15 @@
|
|||||||
.post-header {
|
.post-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 40rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
width: 48px;
|
width: 96rpx;
|
||||||
height: 48px;
|
height: 96rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 16px;
|
margin-right: 32rpx;
|
||||||
border: 2px solid white;
|
border: 2rpx solid white;
|
||||||
box-shadow: $shadow-sm;
|
box-shadow: $shadow-sm;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
@ -384,34 +420,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.username {
|
.username {
|
||||||
font-size: 16px;
|
font-size: 32rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
font-size: 13px;
|
font-size: 26rpx;
|
||||||
color: $light-text;
|
color: $light-text;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content {
|
.post-content {
|
||||||
.title {
|
.title {
|
||||||
font-size: 18px;
|
font-size: 36rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 32rpx;
|
||||||
display: block;
|
display: block;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-text {
|
.content-text {
|
||||||
font-size: 15px;
|
font-size: 30rpx;
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 40rpx;
|
||||||
display: block;
|
display: block;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
@ -420,8 +456,8 @@
|
|||||||
.images {
|
.images {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 12px;
|
gap: 24rpx;
|
||||||
margin-top: 16px;
|
margin-top: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-image {
|
.post-image {
|
||||||
@ -443,17 +479,17 @@
|
|||||||
|
|
||||||
&.single-img {
|
&.single-img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 300px;
|
height: 600rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.double-img {
|
&.double-img {
|
||||||
width: calc(50% - 6px);
|
width: calc(50% - 12rpx);
|
||||||
height: 180px;
|
height: 360rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.multi-img {
|
&.multi-img {
|
||||||
width: calc(33.33% - 8px);
|
width: calc(33.33% - 16rpx);
|
||||||
height: 120px;
|
height: 240rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,27 +497,27 @@
|
|||||||
.comments-container {
|
.comments-container {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: $radius-lg;
|
border-radius: $radius-lg;
|
||||||
padding: 0 20px;
|
padding: 0 40rpx;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 32rpx;
|
||||||
box-shadow: $shadow-sm;
|
box-shadow: $shadow-sm;
|
||||||
border: 1px solid $border-color;
|
border: 1rpx solid $border-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comments-title {
|
.comments-title {
|
||||||
font-size: 16px;
|
font-size: 32rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
padding: 20px 0;
|
padding: 40rpx 0;
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1rpx solid $border-color;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: -1px;
|
bottom: -1rpx;
|
||||||
width: 40px;
|
width: 80rpx;
|
||||||
height: 2px;
|
height: 4rpx;
|
||||||
background-color: $primary-color;
|
background-color: $primary-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -493,26 +529,27 @@
|
|||||||
|
|
||||||
.comment-item {
|
.comment-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 20px 0;
|
padding: 20rpx 0;
|
||||||
border-bottom: 1px solid $border-color;
|
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
&:hover {
|
|
||||||
background-color: rgba($primary-light, 0.3);
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1rpx solid rgba($border-color, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:hover {
|
||||||
border-bottom: none;
|
background-color: rgba($primary-light, 0.2);
|
||||||
|
border-radius: $radius-md;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-level-indicator {
|
.comment-level-indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50px;
|
top: 70rpx;
|
||||||
width: 2px;
|
width: 4rpx;
|
||||||
background-color: #e5e5e5;
|
background-color: rgba($border-color, 0.5);
|
||||||
left: calc(30px + var(--indent) - 20px);
|
left: calc(60rpx + var(--indent) - 40rpx);
|
||||||
height: calc(100% - 50px);
|
height: calc(100% - 70rpx);
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-content-wrapper {
|
.comment-content-wrapper {
|
||||||
@ -522,13 +559,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.comment-avatar {
|
.comment-avatar {
|
||||||
width: 36px;
|
width: 64rpx;
|
||||||
height: 36px;
|
height: 64rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 12px;
|
margin-right: 24rpx;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
border: 1px solid $border-color;
|
border: 1rpx solid $border-color;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -538,50 +575,52 @@
|
|||||||
|
|
||||||
.comment-main {
|
.comment-main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-meta {
|
.comment-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-username {
|
.comment-username {
|
||||||
font-size: 14px;
|
font-size: 28rpx;
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-time {
|
.comment-time {
|
||||||
font-size: 12px;
|
font-size: 24rpx;
|
||||||
color: $light-text;
|
color: $light-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-text {
|
.comment-text {
|
||||||
font-size: 14px;
|
font-size: 28rpx;
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12rpx;
|
||||||
padding: 4px 0;
|
padding: 8rpx 0;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-actions {
|
.comment-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 32rpx;
|
||||||
padding: 4px 0;
|
padding: 4rpx 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reply-btn,
|
.reply-btn,
|
||||||
.fold-btn {
|
.fold-btn {
|
||||||
font-size: 13px;
|
font-size: 24rpx;
|
||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
padding: 4px 8px;
|
padding: 6rpx 12rpx;
|
||||||
border-radius: $radius-sm;
|
border-radius: $radius-sm;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background-color: rgba($primary-color, 0.05);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba($primary-color, 0.1);
|
background-color: rgba($primary-color, 0.1);
|
||||||
@ -594,6 +633,11 @@
|
|||||||
|
|
||||||
.fold-btn {
|
.fold-btn {
|
||||||
color: $light-text;
|
color: $light-text;
|
||||||
|
background-color: rgba($light-text, 0.05);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba($light-text, 0.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 评论输入框样式优化 */
|
/* 评论输入框样式优化 */
|
||||||
@ -603,9 +647,9 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding: 16px;
|
padding: 24rpx 32rpx;
|
||||||
border-top: 1px solid $border-color;
|
border-top: 1rpx solid $border-color;
|
||||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,11 +657,11 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 12px;
|
padding: 16rpx 24rpx;
|
||||||
background-color: $primary-light;
|
background-color: $primary-light;
|
||||||
border-radius: $radius-md;
|
border-radius: $radius-md;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 16rpx;
|
||||||
font-size: 13px;
|
font-size: 26rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.replying-text {
|
.replying-text {
|
||||||
@ -627,29 +671,30 @@
|
|||||||
|
|
||||||
.cancel-reply {
|
.cancel-reply {
|
||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
padding: 4px 8px;
|
padding: 8rpx 16rpx;
|
||||||
border-radius: $radius-sm;
|
border-radius: $radius-sm;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
|
background-color: rgba($primary-color, 0.1);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba($primary-color, 0.1);
|
background-color: rgba($primary-color, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-input {
|
.comment-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 48px;
|
height: 88rpx;
|
||||||
padding: 0 16px;
|
padding: 0 32rpx;
|
||||||
font-size: 14px;
|
font-size: 28rpx;
|
||||||
border: 1px solid $border-color;
|
border: 1rpx solid $border-color;
|
||||||
border-radius: 24px;
|
border-radius: 48rpx;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
outline: none;
|
outline: none;
|
||||||
@ -657,7 +702,7 @@
|
|||||||
&:focus {
|
&:focus {
|
||||||
border-color: $primary-color;
|
border-color: $primary-color;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-shadow: 0 0 0 2px rgba($primary-color, 0.1);
|
box-shadow: 0 0 0 4rpx rgba($primary-color, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
@ -666,20 +711,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.send-btn {
|
.send-btn {
|
||||||
min-width: 80px;
|
min-width: 140rpx;
|
||||||
height: 48px;
|
height: 88rpx;
|
||||||
background-color: $primary-color;
|
background-color: $primary-color;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 24px;
|
border-radius: 48rpx;
|
||||||
font-size: 14px;
|
font-size: 28rpx;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0 16px;
|
padding: 0 32rpx;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: darken($primary-color, 5%);
|
background-color: darken($primary-color, 5%);
|
||||||
@ -704,9 +749,9 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 16px 0;
|
padding: 32rpx 0;
|
||||||
color: $light-text;
|
color: $light-text;
|
||||||
font-size: 14px;
|
font-size: 28rpx;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
|
|
||||||
@ -715,13 +760,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
text {
|
text {
|
||||||
margin-right: 8px;
|
margin-right: 16rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 添加一些动画效果 */
|
/* 添加一些动画效果 */
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from { opacity: 0; transform: translateY(10px); }
|
from { opacity: 0; transform: translateY(20rpx); }
|
||||||
to { opacity: 1; transform: translateY(0); }
|
to { opacity: 1; transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,15 +777,30 @@
|
|||||||
/* 响应式调整 */
|
/* 响应式调整 */
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.content {
|
.content {
|
||||||
padding: 12px;
|
padding: 24rpx;
|
||||||
|
padding-bottom: 220rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-container {
|
.post-container {
|
||||||
padding: 16px;
|
padding: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-input-container {
|
.comment-input-container {
|
||||||
padding: 12px;
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-item {
|
||||||
|
padding: 16rpx 0;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-avatar {
|
||||||
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-text {
|
||||||
|
font-size: 26rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 2.0 KiB |
Loading…
x
Reference in New Issue
Block a user