This commit is contained in:
浅念
2025-12-12 18:34:44 +08:00
parent 2c10e621aa
commit 5112803313
49 changed files with 3677 additions and 2571 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
// const baseUrl = "https://api.collect.hs.hnzhwlkj.club/"; //这里放置url域名32044
// const baseUrl = "https://api.collect.xyh.muzili.top/"; //这里放置url域名32044
const baseUrl = "https://bsh.info.hb.hnzhwlkj.cn/"; //这里放置url域名32044
const baseUrl = "https://cb.hnxinyouhe.com/"; //这里放置url域名32044
// const baseUrl = "https://114.215.24.240:8106/api/v1/"; //这里放置url域名32044
export function request(options){//封装的requset
let { url,data,method } = options;
+1 -1
View File
@@ -4,7 +4,7 @@ export default {
home: 'home',
square: 'square',
publish: 'publish',
message: 'message',
classify: 'classify',
mine: 'mine'
},
navbar: {
+1 -1
View File
@@ -4,7 +4,7 @@ export default {
home: '首页',
square: '广场',
publish: '发布',
message: '消息',
classify: '分类',
mine: '我的'
},
navbar: {
+111
View File
@@ -0,0 +1,111 @@
<template>
<view class="card">
<view class="card-left" @click="$emit('view', item)">
<image class="card-img" :src="thumb" mode="aspectFit"></image>
</view>
<view class="card-right">
<view class="card-title line2" @click="$emit('view', item)">{{ item.content}}</view>
<view class="sku" @click="$emit('view', item)">{{ item.sku.model }}</view>
<view class="meta" @click="$emit('view', item)">
<text class="tag">#{{ item.age}}</text>
</view>
<view class="price-row">
<view @click="$emit('view', item)">
<text class="price">¥{{ item.price}}</text>
<text class="origin">¥{{ item.sku.price }}</text>
</view>
<uni-icons @click="$emit('start', item)" :type="item.is_favorite ? 'star-filled' : 'star'" :color="item.is_favorite ? '#05b3a8' : '#333'" size="18"></uni-icons>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'ClassifyCard',
props: {
item: {
type: Object,
required: true,
default: () => ({})
}
},
computed: {
thumb() {
const img = this.item.media?.[0]?.url_pull || this.item.cover || this.item.image?.[0]?.url_pull
return img || '/static/image/cp.png'
}
}
}
</script>
<style lang="scss" scoped>
.card {
display: flex;
gap: 16rpx;
padding: 16rpx;
background: #fff;
margin-bottom: 20rpx;
border-radius: 20rpx;
}
.card-left {
width: 180rpx;
position: relative;
}
.card-img {
width: 180rpx;
height: 180rpx;
background: #f5f5f5;
border-radius: 12rpx;
}
.card-right {
flex: 1;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.card-title {
font-size: 26rpx;
color: #111;
line-height: 1.5;
}
.line2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.sku {
font-size: 22rpx;
color: #999;
}
.price-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10rpx;
}
.price {
font-size: 38rpx;
color: #00bfb2;
font-weight: 700;
margin-right: 10rpx;
}
.origin {
font-size: 22rpx;
color: #ccc;
text-decoration: line-through;
}
.meta {
display: flex;
gap: 10rpx;
}
.tag {
font-size: 22rpx;
color: #3BC6BD;
padding: 4rpx 28rpx;
background: rgb(59 198 189 / 0.1);
border-radius: 6rpx;
}
</style>
+257
View File
@@ -0,0 +1,257 @@
<template>
<view class="card-item">
<view class="user" @click="handleUserClick">
<image :src="item.user.avatar_pull || '/static/image/user.png'" class="avatar"></image>
<view class="info">
<view class="name">{{ item.user.nickname || '用户名' }}</view>
<view class="time">{{ item.created_at }}</view>
</view>
<button v-if="!ismysq" class="chat-btn" @click.stop="handleContact">联系门店</button>
<button
v-else
class="chat-btn"
:class="item.status==1 ? 'chat-btn1' : item.status==2 ? 'chat-btn2' : item.status==3 ? 'chat-btn3' : 'chat-btn4'"
@click.stop="handleContact"
>
{{ item.status==1 ? '已发布' : item.status==2 ? '审核中' : item.status==3 ? '未通过' : '已过期' }}
</button>
</view>
<view class="content" @click="handleView">
<u-read-more :showHeight="80" ref="uReadMore" color="#999999" closeText="查看更多">
<rich-text :nodes="item.content" @load="handleContentLoad"></rich-text>
</u-read-more>
</view>
<!-- <view class="tags" v-if="item.tag && item.tag.length">
<text v-for="(tag, index) in item.tag" :key="index">{{ tag }}</text>
</view> -->
<view class="tags">
<text>#{{ item.age}}</text>
</view>
<view class="imgs" v-if="item.media && item.media.length">
<image
v-for="(img, index) in item.media"
:key="index"
:src="getImgUrl(img)"
class="img"
mode="aspectFill"
@click.stop="previewImage(index)"
@longpress.stop="downloadImage(img)"
/>
</view>
<view class="location" v-if="item.location_name">
<uni-icons type="location" color="#999" size="18"></uni-icons>
<text>{{ item.location_name }}</text>
</view>
<view class="comment" @click="handleComment">
评论({{ item.comment || 0 }})
</view>
</view>
</template>
<script>
import uReadMore from '@/uni_modules/uview-ui/components/u-read-more/u-read-more.vue'
export default {
name: 'PostCard',
components: {
uReadMore
},
props: {
item: {
type: Object,
required: true,
default: () => ({})
},
ismysq:{
type: Boolean,
default: false
}
},
methods: {
getImgUrl(img) {
if (!img) return '';
if (typeof img === 'string') return img;
return img.img_src || img.url_pull || img.url || '';
},
handleContentLoad() {
this.$nextTick(() => {
if (this.$refs.uReadMore) {
this.$refs.uReadMore.init();
}
});
},
handleUserClick() {
this.$emit('userClick', this.item);
},
handleContact() {
this.$emit('contact', this.item);
},
handleView() {
this.$emit('view', this.item);
},
previewImage(index) {
if (!this.item.media || !this.item.media.length) return;
const urls = this.item.media.map(m => this.getImgUrl(m)).filter(Boolean);
uni.previewImage({
current: index,
urls
});
},
downloadImage(img) {
const url = this.getImgUrl(img);
if (!url) return;
uni.showActionSheet({
itemList: ['保存图片'],
success: (res) => {
if (res.tapIndex === 0) {
uni.showLoading({ title: '下载中...', mask: true });
uni.downloadFile({
url,
success: (downloadRes) => {
if (downloadRes.statusCode === 200) {
uni.saveImageToPhotosAlbum({
filePath: downloadRes.tempFilePath,
success: () => {
uni.hideLoading();
uni.showToast({ title: '保存成功', icon: 'success' });
},
fail: () => {
uni.hideLoading();
uni.showToast({ title: '保存失败', icon: 'none' });
}
});
} else {
uni.hideLoading();
uni.showToast({ title: '下载失败', icon: 'none' });
}
},
fail: () => {
uni.hideLoading();
uni.showToast({ title: '下载失败', icon: 'none' });
}
});
}
}
});
},
handleComment() {
this.$emit('comment', this.item);
}
}
}
</script>
<style lang="scss" scoped>
.card-item {
margin-bottom: 40rpx;
background: #fff;
padding: 20rpx;
border-radius: 10rpx;
}
.user {
flex-direction: row;
display: flex;
align-items: center;
.avatar {
width: 110rpx;
height: 110rpx;
border-radius: 50%;
}
.info {
margin-left: 20rpx;
flex: 1;
.name {
font-size: 30rpx;
font-weight: bold;
}
.time {
font-size: 24rpx;
color: #777;
margin-top: 6rpx;
}
}
.chat-btn {
padding: 0 26rpx;
height: 45rpx;
line-height: 45rpx;
border-radius: 10rpx;
background: #3BC6BD;
color: #fff;
font-size: 22rpx;
border: none;
}
.chat-btn1{
background:rgb(59 198 189 / 20%);
color: #3BC6BD;
}
.chat-btn2{
background: rgb(255 0 0 / 20%);
color: #fff;
}
.chat-btn3{
background: rgb(62 94 215 / 20%);
color: #3E5ED7;
}
.chat-btn4{
background: rgb(105 105 105 / 20%);
color: #696969;
}
}
.content {
margin-top: 20rpx;
font-size: 28rpx;
color: #444;
}
.tags {
margin-top: 20rpx;
text {
margin-right: 20rpx;
padding: 10rpx 20rpx;
border-radius: 30rpx;
font-size: 24rpx;
color: #3BC6BD;
}
}
.imgs {
display: flex;
flex-wrap: wrap;
margin-top: 20rpx;
.img {
width: 30%;
margin-right: 3%;
margin-bottom: 3%;
border-radius: 12rpx;
height: 190rpx;
}
}
.location {
display: flex;
align-items: center;
color: #666666;
font-size: 22rpx;
margin-top: 20rpx;
gap: 8rpx;
}
.comment {
border: 1px solid #ccc;
text-align: center;
line-height: 66rpx;
border-radius: 34rpx;
margin: 20rpx 0;
font-size: 26rpx;
color: #666;
}
</style>
+124
View File
@@ -0,0 +1,124 @@
<template>
<view class="danmu-container" v-if="visible">
<view
v-for="item in danmuItems"
:key="item.id"
class="danmu-item"
:style="{
top: item.top + 'rpx',
animationDuration: item.speed + 's',
color: item.color
}"
>
{{ item.text }}
</view>
</view>
</template>
<script>
export default {
props: {
list: Array,
visible: { type: Boolean, default: true },
row: { type: Number, default: 5 } // 轨道数
},
data() {
return {
danmuItems: [],
laneBusy: [] // 轨道是否被占用
}
},
mounted() {
this.laneBusy = Array(this.row).fill(false)
this.startDanmu()
},
methods: {
startDanmu() {
let index = 0
setInterval(() => {
const text = this.list[index].content
this.createDanmu(text)
index = (index + 1) % this.list.length
}, 1500)
},
createDanmu(text) {
const lane = this.getFreeLane()
const id = Date.now() + Math.random()
const item = {
id,
text,
top: lane * 100, // 每条轨道高度 60rpx
speed: 6 + Math.random()*2,
color: this.randomColor(),
lane
}
this.danmuItems.push(item)
setTimeout(() => {
this.finishDanmu(id, lane)
}, item.speed * 1000)
},
getFreeLane() {
// 找到第一个空轨道
for (let i = 0; i < this.row; i++) {
if (!this.laneBusy[i]) {
this.laneBusy[i] = true
return i
}
}
// 如果都满了,就随机一个
return Math.floor(Math.random() * this.row)
},
finishDanmu(id, lane) {
this.danmuItems = this.danmuItems.filter(i => i.id !== id)
// 整个动画跑完后释放轨道
this.laneBusy[lane] = false
},
randomColor() {
const colors = ["#fff", "#00F6FF", "#FFEA00", "#FF66C4"]
return colors[Math.floor(Math.random() * colors.length)]
}
}
}
</script>
<style>
.danmu-container {
position: absolute;
left: 0;
top: 88rpx;
width: 100%;
height: 600rpx;
overflow: hidden;
pointer-events: none;
z-index: 100;
}
.danmu-item {
position: absolute;
right: -400rpx;
font-size: 26rpx;
white-space: nowrap;
animation-name: danmu-move;
animation-timing-function: linear;
animation-fill-mode: forwards;
background: rgb(0 0 0 / 60%);
padding: 5px 14px;
border-radius: 20px;
}
@keyframes danmu-move {
from { transform: translateX(0); }
to { transform: translateX(-1200rpx); }
}
</style>
+10 -1
View File
@@ -100,8 +100,17 @@
"minified" : true,
"postcss" : true
},
"permission": {
"scope.userFuzzyLocation": {
"desc": "用于为你推荐附近内容/门店"
}
},
"requiredPrivateInfos": [
"getFuzzyLocation",
"chooseLocation",
"chooseAddress"
],
"usingComponents" : true,
"permission" : {},
"uniStatistics" : {
"enable" : true
}
+30 -5
View File
@@ -83,7 +83,17 @@
"enablePullDownRefresh": false,
"navigationStyle":"custom"// 去掉微信小程序顶部导航
}
},
{
"path" : "pages/classify/classify",
"style" :
{
"navigationBarTitleText" : "分类",
"enablePullDownRefresh": false,
"navigationStyle":"custom"// 去掉微信小程序顶部导航
}
}
],
"subPackages":[
{
@@ -124,6 +134,15 @@
"enablePullDownRefresh": false,
"navigationStyle":"custom"// 去掉微信小程序顶部导航
}
},
{
"path" : "my_reply",
"style" :
{
"navigationBarTitleText" : "回复我的",
"enablePullDownRefresh": false,
"navigationStyle":"custom"// 去掉微信小程序顶部导航
}
}
]
}
@@ -147,7 +166,7 @@
},
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#0071FC",
"selectedColor": "#3BC6BD",
"borderStyle": "white",
"backgroundColor": "#fff",
"list": [
@@ -158,10 +177,10 @@
"text": "首页"
},
{
"pagePath": "pages/square/index",
"iconPath": "/static/image/square1.png",
"selectedIconPath": "/static/image/square.png",
"text": "广场"
"pagePath": "pages/classify/classify",
"iconPath": "/static/image/classify1.png",
"selectedIconPath": "/static/image/classify.png",
"text": "分类"
},
{
"pagePath": "pages/publish/index",
@@ -169,6 +188,12 @@
"selectedIconPath": "/static/image/publish.png",
"text": "发布"
},
{
"pagePath": "pages/square/index",
"iconPath": "/static/image/square1.png",
"selectedIconPath": "/static/image/square.png",
"text": "广场"
},
{
"pagePath": "pages/index/mine",
"iconPath": "/static/image/mine1.png",
+630
View File
@@ -0,0 +1,630 @@
<template>
<view class="page">
<uni-nav-bar leftText=" " :fixed="true" :border="false" @clickLeft="leftClick" color="#000" backgroundColor="#fff" :statusBar="true" title="样机分类"></uni-nav-bar>
<!-- 顶部 tabs -->
<view class="tabs">
<view :class="['tab', currentTab==='latest'?'on':'']" @click="switchTab('latest')">最新发布</view>
<view class="divider"></view>
<view :class="['tab', currentTab==='near'?'on':'']" @click="switchTab('near')">距离最近</view>
</view>
<view class="content-con">
<!-- 左侧分类 -->
<scroll-view scroll-y class="left-aside">
<view
v-for="item in flist"
:key="item.id"
class="f-item"
:class="{active: item.id === currentCategoryId}"
@click="onCategoryChange(item.id)"
>
<view class="cat-text">{{ item.name }}</view>
</view>
</scroll-view>
<!-- 右侧列表 -->
<scroll-view
scroll-y
class="right-aside"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
:lower-threshold="100"
@scrolltolower="onLoadMore"
>
<view class="list">
<classify-card
v-for="item in slist"
:key="item.id"
:item="item"
@view="navToList"
@start="startLink"
/>
<view v-if="hasMore" class="load-more">
<text v-if="loadingMore">加载中...</text>
<text v-else>上拉加载更多</text>
</view>
<view v-else-if="slist.length > 0" class="load-more">
<text>没有更多了</text>
</view>
<view class="empty" v-if="slist.length === 0 && !loading">
<image src="/static/image/empty.png" mode="aspectFit"></image>
<view>暂无数据!</view>
</view>
</view>
</scroll-view>
</view>
<!-- 加载提示 -->
<view v-if="loading" class="loading-mask">
<view class="loading-text">加载中...</view>
</view>
<u-popup :show="showlogin" :closeOnClickOverlay="false" :round="20" mode="center">
<view class="popupcontent">
<image class="log1" src="/static/image/log1.png"></image>
<view class="title">{{$t('home.loginmsg')}}</view>
<view class="yhxy">
<checkbox-group name="" style="width: 28px;display: inline-block;">
<label @tap="selfChangde()">
<checkbox :checked="selfChecked" activeBackgroundColor="#3F435B"
style="transform:scale(0.7);border-radius: 50%" />
</label>
</checkbox-group>
{{$t('home.argin')}}<text class="xy" @click="gouser">{{$t('home.UserAgreement')}}</text>{{$t('home.and')}}<text
class="xy" @click="goprivacy">{{$t('home.Privacy')}}</text>
</view>
</view>
<view class="cent">
<button class="btn1" @click="showlogin = !showlogin">{{$t('cancel')}}</button>
<button v-if="selfChecked" class="btn" open-type="getPhoneNumber" @click="showlogin = !showlogin"
@getphonenumber="getPhoneNumber">{{$t('login')}}</button>
<button v-else class="btn" @click="loginbtn">{{$t('login')}}</button>
</view>
</u-popup>
</view>
</template>
<script>
import { request } from '@/api/apiT.js'
import SquarePostCard from '@/components/SquarePostCard.vue'
import ClassifyCard from '@/components/ClassifyCard.vue'
export default {
components: {
SquarePostCard,
ClassifyCard
},
data() {
return {
showlogin: false,
tabs: [
{ key: 'latest', label: '最新发布' },
{ key: 'near', label: '距离最近' }
],
currentTab: 'latest',
flist: [],
slist: [],
selfChecked: false,
currentCategoryId: '', // 当前选中的分类id
loading: false, // 加载状态
refreshing: false, // 下拉刷新状态
loadingMore: false, // 上拉加载状态
page: 1, // 当前页码
pageSize: 10, // 每页数量
hasMore: true, // 是否还有更多数据
categoriesLoaded: false // 分类是否已加载完成
}
},
onLoad() {
// 加载第一级分类列表
},
onShow() {
this.loadCategoryList();
// this.syncTabbar()
// // 从详情页返回后,重新拉取当前分类数据
// if (this.categoriesLoaded) {
// this.refreshCurrentCategory()
// }
},
methods: {
goprivacy(){
uni.navigateTo({
url: '/pages/login/agreement',// 替换为你要跳转的页面路径
});
},
selfChangde(e) {
this.selfChecked = !this.selfChecked;
},
loginbtn(){
if(!this.selfChecked){
uni.showToast({
title:this.$t('home.arginimg'),
icon: 'none'
});
return false
}
},
getPhoneNumber(e) {
let that = this;
request({
url:'miniapp/phone',
method:'POST',
data:{
phone_code: e.detail.code,
}
}).then(res => {
if (res.data.code == -1) {
uni.showToast({
title: res.data.msg,
icon: 'none'
});
} else if (res.data.code == 200) {
//返回数据接收成功
uni.setStorageSync('is_login', true);
}
})
},
// 加载第一级分类列表
loadCategoryList() {
// TODO: 替换为实际的后端接口
let that = this;
request({
url:'common/post/category/list',
method:'GET'
}).then(res => {
if (res.data.code == 200) {
const records = res.data.data.records || [];
this.flist = [{
id: '',
name: '全部分类'
}, ...records];
this.categoriesLoaded = true;
// 默认加载第一个分类的数据
if (this.flist.length > 0) {
this.onCategoryChange(this.flist[0].id);
}
}
})
},
// 重新加载当前分类数据
refreshCurrentCategory() {
const targetCategory = this.currentCategoryId !== undefined ? this.currentCategoryId : (this.flist[0] && this.flist[0].id) || '';
this.loadCategoryData(targetCategory, false);
},
// 分类切换事件
onCategoryChange(categoryId) {
// 如果点击的是当前分类且已有数据,不重复加载
if (this.currentCategoryId === categoryId && this.slist.length > 0) {
return;
}
// 切换分类时,重置所有状态
this.currentCategoryId = categoryId;
this.page = 1;
this.hasMore = true;
this.loading = false;
this.loadingMore = false;
this.refreshing = false;
this.slist = []; // 先清空数据
this.loadCategoryData(categoryId, false);
},
// 根据分类id加载分类下的内容
loadCategoryData(categoryId, isLoadMore = false) {
// 防止重复请求:如果是加载更多,需要检查所有加载状态
if (isLoadMore) {
if (this.loadingMore || this.loading || this.refreshing) {
return;
}
} else {
// 下拉刷新或初次加载,检查加载状态
if (this.loading || this.loadingMore) {
return;
}
}
if (isLoadMore) {
this.loadingMore = true;
this.page += 1;
} else {
this.loading = true;
this.page = 1;
this.hasMore = true;
this.slist = []; // 清空之前的数据
}
request({
url:'common/post/list',
method:'GET',
data:{
category_id:categoryId,
current: this.page,
size: this.pageSize
}
}).then(res => {
this.loading = false;
this.loadingMore = false;
this.refreshing = false;
if (res.data.code == 200) {
const records = res.data.data.records || [];
const total = res.data.data.total || 0;
if (isLoadMore) {
// 上拉加载更多,追加数据并去重
const existingIds = new Set(this.slist.map(item => item.id));
const newRecords = records.filter(item => !existingIds.has(item.id));
this.slist = [...this.slist, ...newRecords];
} else {
// 下拉刷新或初次加载,替换数据
this.slist = records;
}
// 判断是否还有更多数据
this.hasMore = this.slist.length < total;
}
}).catch(() => {
this.loading = false;
this.loadingMore = false;
this.refreshing = false;
if (isLoadMore) {
this.page -= 1; // 加载失败,页码回退
}
})
},
// 下拉刷新
onRefresh() {
// 如果正在加载中,不执行刷新
if (this.loading || this.loadingMore) {
this.refreshing = false;
return;
}
this.refreshing = true;
this.loadCategoryData(this.currentCategoryId, false);
},
// 上拉加载更多
onLoadMore() {
if (!this.hasMore || this.loadingMore || this.loading || this.refreshing) {
return;
}
this.loadCategoryData(this.currentCategoryId, true);
},
// tab 切换
switchTab(key) {
if (this.currentTab === key) return
this.currentTab = key
this.onRefresh()
},
// 点击商品项
navToList(item) {
uni.navigateTo({
url: '/pages/square/detiles?id='+item.id
});
},
islogin(){
if(uni.getStorageSync('is_login')){
return true
}else{
return false
}
},
//收藏
startLink(item){
if(!this.islogin()){
this.showlogin = true
return false
}
const targetIndex = this.slist.findIndex(post => post.id === item.id)
if(targetIndex === -1){
return
}
const target = this.slist[targetIndex]
const collectUrl = item.is_favorite ? 'common/post/favorite/destroy' : 'common/post/favorite/store'
const method = item.is_favorite ? 'GET' : 'POST'
const data = {
post_id: item.id
}
if (item.is_favorite) {
data.user_id = item.user_id
}
request({
url: collectUrl,
method,
data
}).then(res => {
if(res.data.code === 200){
const newTarget = { ...target }
if(newTarget.is_favorite){
newTarget.is_favorite = 0
uni.showToast({
title: '取消收藏成功',
icon: 'none'
});
}else{
newTarget.is_favorite = 1
uni.showToast({
title: '收藏成功',
icon: 'none'
});
}
this.$set(this.slist, targetIndex, newTarget)
}else{
uni.showToast({
title:res.data.message,
icon: 'none'
});
}
})
},
//点赞
navTozan(item){
const targetIndex = this.slist.findIndex(post => post.id === item.id)
if(targetIndex === -1){
return
}
const target = this.slist[targetIndex]
const isLiked = Number(target.is_like) === 1
const likeUrl = isLiked ? 'common/post/like/destroy' : 'common/post/like/store'
const method = isLiked ? 'GET' : 'POST'
const data = {
post_id: target.id
}
if (isLiked) {
data.user_id = target.user_id
}
request({
url: likeUrl,
method,
data
}).then(res => {
if(res.data.code === 200){
const newTarget = { ...target }
if(isLiked){
newTarget.is_like = 0
newTarget.like = Math.max(0, (Number(newTarget.like) || 0) - 1)
uni.showToast({
title: '取消成功',
icon: 'none'
});
}else{
newTarget.is_like = 1
newTarget.like = (Number(newTarget.like) || 0) + 1
uni.showToast({
title: '点赞成功',
icon: 'none'
});
}
this.$set(this.slist, targetIndex, newTarget)
}else{
uni.showToast({
title:res.data.message,
icon: 'none'
});
}
})
},
//消息
navTomsg(item){
request({
url:'common/like/store',
method:'GET',
data:{
post_id:item.id
}
}).then(res => {
})
},
leftClick(){
uni.navigateBack()
},
syncTabbar(){
uni.setTabBarItem({ index:0, text:this.$t('tabbar.home') })
uni.setTabBarItem({ index:1, text:this.$t('tabbar.square') })
uni.setTabBarItem({ index:2, text:this.$t('tabbar.publish') })
uni.setTabBarItem({ index:3, text:this.$t('tabbar.mine') })
}
}
}
</script>
<style lang='scss' scoped>
page,
.page {
height: 100%;
/* background: #f7f7f7; */
}
.tabs {
height: 80rpx;
background: #fff;
display: flex;
align-items: center;
justify-content: space-around;
gap: 24rpx;
position: relative;
font-size: 28rpx;
color: #666;
}
.tab {
position: relative;
padding: 12rpx 20rpx 18rpx;
color: #333;
}
.tab::after {
content: '';
position: absolute;
left: 50%;
bottom: 4rpx;
width: 50rpx;
height: 4rpx;
background: #05b3a8;
border-radius: 4rpx;
transform: translateX(-50%) scaleX(0);
transition: transform 0.2s;
}
.tab.on {
color: #05b3a8;
font-weight: 600;
}
.tab.on::after {
transform: translateX(-50%) scaleX(1);
}
.divider {
width: 1px;
height: 26rpx;
background: #dcdcdc;
}
.content-con {
height: calc(100% - 80rpx);
display: flex;
}
.left-aside {
width: 200rpx;
background: #3BC6BD;
color: #000;
}
.f-item {
height: 96rpx;
/* padding-left: 28rpx; */
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
color: #000;
}
.category-icon image {
width: 32rpx;
height: 32rpx;
}
.cat-text {
font-size: 26rpx;
width: 80%;
text-align: center;
padding: 14rpx 0;
border-radius: 10rpx;
}
.f-item.active .cat-text{
background: #F5F6FA;
color: #000;
}
.right-aside {
flex: 1;
/* background: #fff; */
}
.list {
padding: 16rpx 16rpx 40rpx;
}
.load-more {
width: 100%;
text-align: center;
padding: 30rpx 0;
font-size: 24rpx;
color: #999;
}
.empty {
width: 100%;
text-align: center;
padding: 80rpx 0;
}
.empty image {
width: 200rpx;
height: 140rpx;
margin-bottom: 12rpx;
}
.loading-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
.loading-text {
color: #fff;
font-size: 28upx;
background-color: rgba(0, 0, 0, 0.7);
padding: 20upx 40upx;
border-radius: 8upx;
}
}
.popupcontent{
background-size: 100% 100%;
padding: 26px 15px;
text-align: center;
background-repeat: no-repeat;
.title{
color: #333333;
font-size: 15px;
margin-bottom: 15px;
}
}
.log1{
width: 180px;
height: 180px;
}
.yhxy{
color: #999999;
font-size: 12px;
margin-bottom: 5px;
.xy{
color: #3BC6BD;
}
}
.share{
background-color: #fff;
border: 1px solid #fff;
height: 34px;
line-height: 34px;
width: 30px;
position: absolute;
right: 0px;
padding: 0px;
top: 8px;
}
.share:after{
border: 1px solid #fff;
}
.share image{
width: 24px;
height: 24px;
}
.cent{
display: flex;
align-items: center;
justify-content: space-around;
padding: 0px 10px 20px;
.btn1{
background-color: #fff;
border: 1px solid #3BC6BD;
color: #3BC6BD;
border-radius: 50px;
width: 46%;
margin: 10px 0px 20px;
height: 50px;
line-height: 50px;
}
.btn{
background-color: #3BC6BD;
color: white;
border-radius: 50px;
width: 46%;
margin: 10px 0px 20px;
height: 50px;
line-height: 50px;
}
}
</style>
+322 -464
View File
@@ -1,489 +1,347 @@
<template>
<view class="home-page">
<z-paging ref="paging" :auto="false" v-model="contList" @query="queryList" empty-view-text="暂无数据!">
<!-- <template #top> -->
<!-- 轮播Banner -->
<swiper class="banner-swiper" circular autoplay :interval="4000" indicator-dots indicator-color="rgba(255,255,255,.4)"
indicator-active-color="#3F435B">
<swiper-item v-for="(banner,index) in bannerList" :key="index">
<image class="banner-img" :src="banner.image_url_pull" mode="aspectFill"></image>
<!-- <view class="banner-desc">
<text class="slogan-text">{{banner.slogan}}</text>
</view> -->
</swiper-item>
</swiper>
<!-- </template> -->
<!-- 内容网格 -->
<view class="content-grid">
<square-post-card
class="grid-item"
v-for="sitem in contList"
:key="sitem.id"
:item="sitem"
:isshow="false"
@view="navToList"
@comment="viewPost"
@like="viewPost" />
<view class="page">
<z-paging ref="paging" :auto="false" v-model="list" @query="queryList" empty-view-text="暂无数据!">
<!-- 顶部背景 -->
<view class="header">
<image src="/static/image/homebg.png" mode="aspectFill" class="bg"></image>
<!-- 弹幕组件 -->
<danmu :list="danmuList" :row="5" :visible="danmuVisible"></danmu>
<!-- 标题 -->
<view class="title">
<view>西门子样机</view>
<view>福利平台</view>
</view>
<!-- 右侧按钮 -->
<view class="right-btns">
<view class="circle-btn" :class="{ active: danmuVisible }" @click="toggleDanmu">
<image v-if="danmuVisible" src="/static/image/dan1.png" mode=""></image>
<image v-else src="/static/image/danm.png" mode=""></image>
</view>
<view class="circle-btn">
<image src="/static/image/shares.png" class="share" mode=""></image>
</view>
</view>
</view>
<!-- 主体内容 -->
<view class="main-box">
<view class="titles">
样机广场
</view>
<view class="tabs">
<view :class="['tab',activeTab==0?'on':'']" @tap="tabclick(0)">
<text>最新发布</text>
</view>
<view class="divider"></view>
<view :class="['tab',activeTab==1?'on':'']" @tap="tabclick(1)">
<text>距离最近</text>
</view>
</view>
<!-- 用户卡片 -->
<view class="card">
<post-card
v-for="sitem in list"
:key="sitem.id || sitem.username"
:item="sitem"
@userClick="goto"
@view="goto"
@contact="handleContact"
@comment="handleComment">
</post-card>
</view>
</view>
</z-paging>
<u-popup :show="show" :closeOnClickOverlay="false" :round="20" mode="center">
<view class="popupcontent">
<image class="log1" src="/static/image/log1.png"></image>
<view class="title">{{$t('home.loginmsg')}}</view>
<view class="yhxy">
<checkbox-group name="" style="width: 28px;display: inline-block;">
<label @tap="selfChangde()">
<checkbox :checked="selfChecked" activeBackgroundColor="#3F435B"
style="transform:scale(0.7);border-radius: 50%" />
</label>
</checkbox-group>
{{$t('home.argin')}}<text class="xy" @click="gouser">{{$t('home.UserAgreement')}}</text>{{$t('home.and')}}<text
class="xy" @click="goprivacy">{{$t('home.Privacy')}}</text>
</view>
</view>
<view class="cent">
<button class="btn1" @click="show = !show">{{$t('cancel')}}</button>
<button v-if="selfChecked" class="btn" open-type="getPhoneNumber" @click="show = !show"
@getphonenumber="getPhoneNumber">{{$t('login')}}</button>
<button v-else class="btn" @click="loginbtn">{{$t('login')}}</button>
</view>
</u-popup>
<u-picker :immediateChange="true" :cancel-text="$t('cancel')" :confirm-text="$t('confirm')" @close="close('showlang')"
:show="showlang" :columns="columns" @cancel="cancellang" @confirm="confirmlang" :title="$t('home.langbtn')" keyName="label"></u-picker>
</view>
</template>
<script>
import { request,showToast } from '@/api/apiT.js'
import SquarePostCard from '@/components/SquarePostCard.vue'
export default {
components: {
SquarePostCard
},
data() {
return {
title: 'Hello',
selfChecked: false,
home: this.$t('tabbar.home'),
is_login:uni.getStorageSync('is_login'),
show: false,
showlang: false,
searchKeyword: '',
bannerList: [],
contList: [],
columns: [
[{
label: this.$t('zh'),
id: 'zh'
}, {
label: this.$t('en'),
id: 'en'
},{
label: this.$t('ru'),
id: 'ru'
}]
],
}
},
onLoad() {
// if(uni.getStorageSync('token')){
// // this.$refs.paging && this.$refs.paging.reload()
// }else{
// this.mini_login()
// }
if (!uni.getStorageSync('token')) {
this.mini_login().then(() => {
this.bannerListfn()
this.$refs.paging && this.$refs.paging.reload()
})
}
},
onShow() {
uni.setTabBarItem({
index: 0,
text: this.$t('tabbar.home')
})
uni.setTabBarItem({
index:1,
text: this.$t('tabbar.square')
})
uni.setTabBarItem({
index: 2,
text: this.$t('tabbar.publish')
})
uni.setTabBarItem({
index: 3,
text: this.$t('tabbar.mine')
})
if(uni.getStorageSync('token')){
this.bannerListfn()
import danmu from '@/components/danmu/danmu.vue';
import PostCard from '@/components/PostCard.vue'
import { request,showToast } from '@/api/apiT.js'
export default {
components: { danmu, PostCard },
data() {
return {
activeTab: 0,
danmuVisible:true,
content:'',
list:[],
danmuList: [],
latitude:'',
longitude:''
};
},
onLoad() {
// 数据加载逻辑
if (!uni.getStorageSync('token')) {
this.mini_login().then(() => {
this.barragelist()
this.$refs.paging && this.$refs.paging.reload()
}
})
}else{
this.barragelist()
this.$refs.paging && this.$refs.paging.reload()
}
},
onShow() {
this.getFuzzy().then(res => {
this.latitude = res.latitude
this.longitude = res.longitude
console.log(res);
}).catch(err => console.error(err));
},
methods: {
getFuzzy() {
return new Promise((resolve, reject) => {
wx.getSetting({
success: (st) => {
const ok = st.authSetting['scope.userFuzzyLocation'];
const run = () => wx.getFuzzyLocation({
type: 'wgs84',
success: resolve,
fail: reject
});
if (ok) return run();
wx.authorize({
scope: 'scope.userFuzzyLocation',
success: run,
fail: () => wx.openSetting({ success: () => reject(new Error('未授权')) })
});
},
fail: reject
});
});
},
methods: {
async initPage(){
await this.mini_login()
await this.bannerListfn()
},
bannerListfn(){
request({
url:'common/banner/list',
method:'get',
data:{
module:'home_module'
}
}).then(res => {
this.bannerList = res.data.data
})
},
// 点击商品项
navToList(item) {
uni.navigateTo({
url: '/pages/square/detiles?id='+item.id
});
},
viewPost(item){
if(uni.getStorageSync('is_login')){
tabclick(item){
this.activeTab = item;
this.$refs.paging && this.$refs.paging.reload()
},
//获取弹幕
barragelist(){
request({
url: 'admin/barrage/list',
method: 'GET',
}).then(res => {
if (res.data.code === 200) {
this.danmuList = res.data.data.records
}
})
},
toggleDanmu() {
this.danmuVisible = !this.danmuVisible;
},
goto(item){
uni.navigateTo({
url: '/pages/square/detiles?id='+item.id
});
},
handleContact(item) {
uni.showToast({
title: '联系门店功能',
icon: 'none'
});
},
handleComment(item) {
uni.navigateTo({
url: '/pages/square/detiles?id=' + (item?.id || 1)
});
},
queryList(pageNo, pageSize, from) {
this.loading = true;
var that = this;
const params = {
current:pageNo,
size:10
};
const collectUrl = this.activeTab==0 ? 'common/post/list' : 'common/post/nearby'
// 添加排序参数(如果后端支持)
if(this.activeTab==1){
params.lng = this.longitude;
params.lat = this.latitude;
} else {
}
request({
url:collectUrl,
method:'get',
data: params
}).then(res => {
if(res.data.code ==200){
var datas = res.data.data.records;
this.$refs.paging.complete(datas);
}else{
this.show = true
}
},
cancellang() {
this.showlang = false
},
close(value){
this[value] = false
},
queryList(pageNo, pageSize) {
this.loading = true;
var that = this;
request({
url:'common/post/list',
method:'get',
data: {
current:pageNo,
size:10
}
}).then(res => {
if(res.data.code ==200){
var datas = res.data.data.records;
this.$refs.paging.complete(datas);
}else{
// this.$refs.paging.complete(null);
// that.listlength=0
}
uni.stopPullDownRefresh();
//
})
},
//语言切换事件
confirmlang(e) {
var that = this
this.$i18n.locale = e.value[0].id
uni.setLocale(e.value[0].id)
uni.setStorageSync('i18locale', e.value[0].id);
this.columns =[
[{
label: this.$t('zh'),
id: 'zh'
}, {
label: this.$t('en'),
id: 'en'
},{
label: this.$t('ru'),
id: 'ru'
}]
];
uni.setTabBarItem({
index: 0,
text: this.$t('tabbar.square')
})
uni.setTabBarItem({
index: 1,
text: this.$t('tabbar.publish')
})
uni.setTabBarItem({
index: 2,
text: this.$t('tabbar.mine')
})
this.showlang = false
var languages = e.value[0].id
if(e.value[0].id=='zh'){
languages= 'zh_CN'
}
request({
url:'user/update',
method:'POST',
data:{
language:languages
}
}).then(res => {
uni.showToast({
title: res.data.message,
icon: 'none'
});
})
},
goprivacy(){
uni.navigateTo({
url: '/pages/login/agreement',// 替换为你要跳转的页面路径
});
},
gouser(){
uni.navigateTo({
url: '/pages/login/agreementuser',// 替换为你要跳转的页面路径
});
},
addCar(){
if(uni.getStorageSync('is_login')){
uni.removeStorageSync('draftid')
uni.removeStorageSync('carinfo')
uni.navigateTo({
url: '/pages/carinfo/index'
});
}else{
this.show = true
}
},
selfChangde(e) {
this.selfChecked = !this.selfChecked;
},
loginbtn(){
if(!this.selfChecked){
uni.showToast({
title:this.$t('home.arginimg'),
icon: 'none'
});
return false
}
},
getPhoneNumber(e) {
let that = this;
request({
url:'miniapp/phone',
method:'POST',
data:{
phone_code: e.detail.code,
}
}).then(res => {
if (res.data.code == -1) {
uni.showToast({
title: res.data.msg,
icon: 'none'
});
} else if (res.data.code == 200) {
//返回数据接收成功
uni.setStorageSync('is_login', true);
}
})
},
mini_login() {
return new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: loginRes => {
// 一定要 return,不然 Promise 不会等待
return request({
url:'miniapp/login',
method:'POST',
data:{ code: loginRes.code }
}).then(res => {
if (res.data.code === 200) {
uni.setStorageSync('openid', res.data.data.openid);
uni.setStorageSync('token', res.data.data.token);
if (res.data.data.mobile) {
uni.setStorageSync('is_login', true);
} else {
uni.setStorageSync('is_login', false);
}
resolve(true); // 👍 一定在 Promise 中
} else {
uni.showToast({
title: res.data.msg,
icon: 'none'
});
reject(res);
}
})
},
fail: (err) => {
uni.showToast({
title: '获取 code 失败',
icon: 'none'
});
reject(err); // 👍 必须 reject
}
});
});
},
}
uni.stopPullDownRefresh();
//
})
},
mini_login() {
return new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: loginRes => {
// 一定要 return,不然 Promise 不会等待
return request({
url:'miniapp/login',
method:'POST',
data:{ code: loginRes.code }
}).then(res => {
if (res.data.code === 200) {
uni.setStorageSync('openid', res.data.data.openid);
uni.setStorageSync('token', res.data.data.token);
if (res.data.data.mobile) {
uni.setStorageSync('is_login', true);
} else {
uni.setStorageSync('is_login', false);
}
resolve(true); // 👍 一定在 Promise 中
} else {
uni.showToast({
title: res.data.msg,
icon: 'none'
});
reject(res);
}
})
},
fail: (err) => {
uni.showToast({
title: '获取 code 失败',
icon: 'none'
});
reject(err); // 👍 必须 reject
}
});
});
}
}
}
</script>
<style lang="scss">
.home-page{
min-height: 100vh;
background: #F5F6FA;
box-sizing: border-box;
padding-bottom: 120rpx;
}
.brand-header{
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
background: #F5F6FA;
}
.brand-left{
display: flex;
align-items: center;
}
.brand-text{
font-size: 28rpx;
color: #333;
margin-right: 10rpx;
}
.sun-icon{
width: 24rpx;
height: 24rpx;
background: #FFB800;
<style lang="scss" scoped>
.page {
min-height: 100vh;
}
.header {
position: relative;
height: 720rpx;
}
.bg {
width: 100%;
height: 100%;
border-bottom-left-radius: 30rpx;
border-bottom-right-radius: 30rpx;
}
.title {
position: absolute;
left: 40rpx;
top: 300rpx;
font-size: 68rpx;
line-height: 1.2;
color: #fff;
font-weight: bold;
}
.right-btns {
position: absolute;
right: 40rpx;
bottom: 100rpx;
.circle-btn {
margin-top: 20rpx;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.brand-right{
font-size: 32rpx;
font-weight: 600;
color: #3F435B;
}
.banner-swiper{
width: 100%;
height: 430rpx;
}
.banner-img{
width: 100%;
height: 100%;
}
.banner-desc{
position: absolute;
left: 30rpx;
bottom: 60rpx;
right: 30rpx;
}
.slogan-text{
display: block;
font-size: 48rpx;
font-weight: 600;
color: #51B6FF;
text-shadow: 0 2rpx 8rpx rgba(0,0,0,0.2);
}
.content-grid{
display: flex;
flex-wrap: wrap;
padding: 20rpx;
justify-content: space-between;
background: #fff;
}
.grid-item{
width: 48%;
overflow: hidden;
margin-bottom: 20rpx;
}
.grid-image{
width: 100%;
height: 320rpx;
background: #E5E5E5;
border-radius: 10rpx;
}
.grid-footer{
padding: 20rpx;
display: flex;
align-items: center;
justify-content: center;
image{
width: 48rpx;
height: 48rpx;
}
.share{
width: 36rpx;
height: 36rpx;
}
}
.grid-avatar{
width: 48rpx;
height: 48rpx;
border-radius: 50%;
margin-right: 12rpx;
flex-shrink: 0;
object-fit: cover;
display: block;
}
.grid-info{
flex: 1;
display: flex;
flex-direction: column;
width: 87%;
}
.grid-tag{
font-size: 24rpx;
color: #3F435B;
margin-bottom: 4rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 95%;
}
.grid-name{
font-size: 22rpx;
color: #8A8D97;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 88%;
}
.popupcontent{
background-size: 100% 100%;
padding: 26px 15px;
}
/* 激活状态(弹幕开启) */
.circle-btn.active {
background: #00c28a;
color: #fff;
}
.main-box {
margin: 20rpx 30rpx;
margin-top: -60rpx;
position: relative;
background: #fff;
border-radius: 30rpx;
.titles{
font-size: 34rpx;
text-align: center;
background-repeat: no-repeat;
.title{
color: #333333;
font-size: 15px;
margin-bottom: 15px;
}
color: #000;
font-weight: bold;
line-height: 88rpx;
}
.log1{
width: 180px;
height: 180px;
}
.tabs {
display: flex;
align-items: center;
justify-content: space-around;
gap: 20rpx;
margin-bottom: 30rpx;
.tab {
position: relative;
padding: 12rpx 24rpx;
font-size: 28rpx;
color: #000;
}
.yhxy{
color: #999999;
font-size: 12px;
margin-bottom: 5px;
.xy{
color: #297EFF;
}
.tab::after {
content: '';
position: absolute;
left: 50%;
bottom: -10rpx;
width: 60rpx;
height: 4rpx;
background: #3BC6BD;
border-radius: 4rpx;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.2s;
}
.cent{
display: flex;
align-items: center;
justify-content: space-around;
padding: 0px 10px 20px;
.btn1{
background-color: #fff;
border: 1px solid #206ff4;
color: #206ff4;
border-radius: 50px;
width: 46%;
margin: 10px 0px 20px;
height: 50px;
line-height: 50px;
}
.btn{
background-color: #206ff4;
color: white;
border-radius: 50px;
width: 46%;
margin: 10px 0px 20px;
height: 50px;
line-height: 50px;
}
.tab.on {
color: #3BC6BD;
font-weight: 600;
}
</style>
.tab.on::after {
opacity: 1;
}
.divider {
width: 1px;
height: 24rpx;
background: #dcdcdc;
}
}
.card {
padding: 10rpx;
}
</style>
+311 -373
View File
File diff suppressed because one or more lines are too long
+163 -6
View File
@@ -13,10 +13,41 @@
<view class="cell1">
<view>{{$t('mine.nickname')}}</view>
<view class="right select">
<input :placeholder="$t('mine.nickinput')" v-model="userinfo.nickname"></input>
<input :placeholder="$t('mine.nickinput')" v-model="userinfo.nickname" />
<uni-icons type="right" size="18"></uni-icons>
</view>
</view>
<view class="cell1">
<view>店铺名称</view>
<view class="right select">
<input placeholder="请填写名称" v-model="userinfo.shop_name" />
<uni-icons type="right" size="18"></uni-icons>
</view>
</view>
<view class="cell1">
<view>店铺电话</view>
<view class="right select">
<input placeholder="请填写店铺电话" v-model="userinfo.mobile" />
<uni-icons type="right" size="18"></uni-icons>
</view>
</view>
<view class="cell1">
<view>店铺位置</view>
<view class="right select">
<input placeholder="请选择店铺位置" v-model="userinfo.location_name" disabled="disabled" @focus="showMap = true" @click="showMap = true" />
<uni-icons type="right" size="18"></uni-icons>
</view>
</view>
<view v-show="showMap" style="height: 300px; margin: 0 20px 20px 20px;">
<map
style="width: 100%; height: 100%;"
:latitude="centerLat"
:longitude="centerLng"
:markers="markers"
scale="14"
@tap="onMapTap"
/>
</view>
</view>
<view class="foot">
<button class="btn" @click="bcFn">{{$t('button.save')}}</button>
@@ -28,6 +59,12 @@
export default {
data() {
return {
centerLat: 39.9042,
centerLng: 116.4074,
markers: [],
// 若需逆地理,请配置腾讯地图 key(可存储在此或通过 storage 注入)
qqMapKey: 'HQBBZ-SC76W-YUNRZ-34IAD-ARN6K-TZBZ4',
showMap: false,
imageStyles: {
width: 40,
height: 40,
@@ -38,8 +75,12 @@
userinfo:{
nickname:'',
avatar:'',
company:'',
phone:'',
shop_name:'',
mobile:'',
location_name:'',
latitude: null,
longitude: null
},
}
@@ -48,13 +89,124 @@
this.getinfo()
},
methods: {
getFuzzy() {
return new Promise((resolve, reject) => {
wx.getSetting({
success: (st) => {
const ok = st.authSetting['scope.userFuzzyLocation'];
const run = () => wx.getFuzzyLocation({
type: 'wgs84',
success: resolve,
fail: reject
});
if (ok) return run();
wx.authorize({
scope: 'scope.userFuzzyLocation',
success: run,
fail: () => wx.openSetting({ success: () => reject(new Error('未授权')) })
});
},
fail: reject
});
});
},
// 地图点选:更新“选点位置”marker
onMapTap(e) {
const { latitude, longitude } = e.detail || {};
if (typeof latitude !== 'number' || typeof longitude !== 'number') return;
// 用 id 2 表示“选点”
const picked = {
id: 2,
latitude,
longitude,
title: '选点位置'
};
// 只保留当前选点作为唯一标记
this.markers = [picked];
// 也可以把地图中心移动到选点
this.centerLat = latitude;
this.centerLng = longitude;
this.userinfo.latitude = latitude
this.userinfo.longitude = longitude
this.reverseGeocode(latitude, longitude)
},
getinfo(){
var that= this;
request({
url:'common/user/info',
method:'POST',
}).then(res => {
this.userinfo=res.data.data.user
this.userinfo = res.data.data.user || {}
const lat = parseFloat(this.userinfo.latitude)
const lng = parseFloat(this.userinfo.longitude)
if (!isNaN(lat) && !isNaN(lng)) {
this.centerLat = lat
this.centerLng = lng
this.markers = [{
id: 1,
latitude: lat,
longitude: lng,
title: this.userinfo.location_name || '店铺位置'
}]
} else {
this.initFuzzyLocation()
}
}).catch(() => {
this.initFuzzyLocation()
})
},
// 优先使用接口位置,没有时回退到模糊定位
initFuzzyLocation() {
try {
this.getFuzzy().then(res => {
this.centerLat = res.latitude;
this.centerLng = res.longitude;
console.log(res)
// 默认给一个“当前位置(模糊)”标记
this.markers = [{
id: 1,
latitude: res.latitude,
longitude: res.longitude,
title: '当前位置(模糊)'
}];
}).catch(err => console.error(err));
} catch (e) {
console.error(e);
}
},
// 逆地理:将经纬度转为位置文本,失败则回退为经纬度字符串
reverseGeocode(lat, lng) {
const key = this.qqMapKey || uni.getStorageSync('qqMapKey') || ''
if (!key) {
this.userinfo.location_name = `${lat.toFixed(6)}, ${lng.toFixed(6)}`
return
}
uni.request({
url: 'https://apis.map.qq.com/ws/geocoder/v1/',
method: 'GET',
data: {
location: `${lat},${lng}`,
key
},
success: (res) => {
if (res.data && res.data.status === 0) {
const result = res.data.result || {}
const formatted = (result.formatted_addresses && result.formatted_addresses.recommend) || result.address
this.userinfo.location_name = formatted || `${lat.toFixed(6)}, ${lng.toFixed(6)}`
} else {
this.userinfo.location_name = `${lat.toFixed(6)}, ${lng.toFixed(6)}`
}
},
fail: () => {
this.userinfo.location_name = `${lat.toFixed(6)}, ${lng.toFixed(6)}`
}
})
},
uploadfn(imgUrl){
@@ -71,7 +223,7 @@
});
},
delfn(){
that.userinfo.avatar =''
this.userinfo.avatar =''
},
select(e){
const tempFilePaths = e.tempFilePaths;
@@ -87,7 +239,12 @@
method:'POST',
data:{
nickname:this.userinfo.nickname,
avatar:this.userinfo.avatar
avatar:this.userinfo.avatar,
mobile:this.userinfo.mobile,
shop_name:this.userinfo.shop_name,
location_name:this.userinfo.location_name,
latitude:this.userinfo.latitude,
longitude:this.userinfo.longitude
}
}).then(res => {
uni.showToast({
+221 -396
View File
@@ -1,407 +1,232 @@
<template>
<view class="content">
<view class="page">
<uni-nav-bar leftText=" " :fixed="true" :border="false" leftIcon="back" @clickLeft="leftClick" color="#000000" backgroundColor="#fff" :statusBar="true" title="我的发布"></uni-nav-bar>
<view class="year-selector" @click="showYearPicker">
<text class="year-text">{{selectedYear}}年</text>
<uni-icons type="arrowdown" size="14" color="#666"></uni-icons>
</view>
<scroll-view scroll-y class="scroll-container"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
:lower-threshold="100"
@scrolltolower="onLoadMore">
<view class="post-list">
<view v-for="(group, dateKey) in groupedPosts" :key="dateKey" class="date-group">
<view class="date-item">
<view class="date-label">
<text class="date-day">{{group.day}}</text>
<text class="date-month">{{group.month}}月</text>
</view>
<view class="posts-container">
<view v-for="post in group.posts" :key="post.id" class="post-item" @click="viewPost(post)">
<image class="post-image" :src="post.media && post.media[0] ? post.media[0].url_pull : '/static/image/placeholder.png'" mode="aspectFill"></image>
<text class="post-tag">{{post.content || ''}}</text>
</view>
</view>
</view>
</view>
<!-- 加载更多提示 -->
<view v-if="hasMore && postList.length > 0" class="load-more">
<text v-if="loadingMore">加载中...</text>
<text v-else>上拉加载更多</text>
</view>
<view v-else-if="!hasMore && postList.length > 0" class="load-more">
<text>没有更多了</text>
</view>
<view v-if="postList.length === 0 && !loading && !loadingMore" class="empty-view">
<text>暂无发布内容</text>
</view>
<view>
<view class="tabs">
<u-tabs :list="list1" @click="click" lineColor="#3BC6BD" :scrollable="false"></u-tabs>
</view>
<scroll-view
scroll-y
class="list-scroll"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
:lower-threshold="100"
@scrolltolower="onLoadMore"
>
<view class="feed-list">
<post-card
v-for="sitem in slist"
:key="sitem.id"
:item="sitem"
:ismysq="true"
@userClick="goto"
@view="goto"
@contact="handleContact"
@comment="handleComment">
</post-card>
<!-- 状态提示 -->
<view v-if="hasMore" class="load-more">
<text v-if="loadingMore">加载中...</text>
<text v-else>上拉加载更多</text>
</view>
<view v-else-if="slist.length > 0" class="load-more">
<text>没有更多了</text>
</view>
<view class="empty" v-if="slist.length === 0 && !loading">
<image src="/static/image/empty.png" mode="aspectFit"></image>
<view>暂无数据!</view>
</view>
</view>
</scroll-view>
<!-- 加载提示 -->
<view v-if="loading" class="loading-mask">
<view class="loading-text">加载中...</view>
</view>
</scroll-view>
<!-- 年份选择器 -->
<u-picker
:show="showYearSelect"
:columns="yearColumns"
:defaultIndex="defaultYearIndex"
keyName="label"
@confirm="onYearConfirm"
@cancel="showYearSelect = false"
title="选择年份">
</u-picker>
<!-- 加载提示 -->
<view v-if="loading" class="loading-mask">
<view class="loading-text">加载中...</view>
</view>
</view>
</template>
<script>
import { request } from '@/api/apiT.js'
export default {
data() {
// 生成年份列表(当前年份前后5年)
const currentYear = new Date().getFullYear();
const years = [];
for (let i = currentYear - 5; i <= currentYear + 5; i++) {
years.push({ label: i.toString(), value: i });
}
return {
selectedYear: currentYear,
showYearSelect: false,
yearColumns: [years],
postList: [],
groupedPosts: {},
loading: false,
refreshing: false,
loadingMore: false, // 上拉加载状态
page: 1, // 当前页码
pageSize: 10, // 每页数量
hasMore: true, // 是否还有更多数据
total: 0, // 总数据量
}
},
computed: {
// 计算当前年份在选择器中的默认索引
defaultYearIndex() {
const currentYear = new Date().getFullYear();
const startYear = currentYear - 5;
return [currentYear - startYear]; // 当前年份在数组中的索引
}
},
onLoad() {
this.loadPostList();
},
methods: {
leftClick() {
// uni.navigateBack()
uni.reLaunch({
url: '/pages/index/mine',// 替换为你要跳转的页面路径
});
},
showYearPicker() {
this.showYearSelect = true;
},
onYearConfirm(e) {
if (e.value && e.value[0]) {
const selectedItem = typeof e.value[0] === 'object' ? e.value[0] : { value: e.value[0], label: e.value[0].toString() };
this.selectedYear = selectedItem.value || parseInt(selectedItem.label);
this.showYearSelect = false;
// 切换年份时重置分页
this.page = 1;
this.hasMore = true;
this.postList = [];
this.groupedPosts = {};
this.loadPostList(false);
}
},
// 加载帖子列表
loadPostList(isLoadMore = false) {
// 防止重复请求
if (isLoadMore) {
if (this.loadingMore || this.loading || this.refreshing || !this.hasMore) {
return;
}
this.loadingMore = true;
this.page += 1;
} else {
if (this.loading && !this.refreshing) {
return;
}
this.loading = true;
this.page = 1;
this.hasMore = true;
if (!isLoadMore && !this.refreshing) {
this.postList = [];
this.groupedPosts = {};
}
}
// 获取当前用户发布的帖子列表
request({
url: 'common/post/list',
method: 'GET',
data: {
// 只获取当前用户发布的帖子,后端应该根据token自动过滤
current: this.page,
size: this.pageSize,
scene:'self',
year:this.selectedYear
}
}).then(res => {
this.loading = false;
this.loadingMore = false;
this.refreshing = false;
if (res.data.code == 200) {
const records = res.data.data.records || [];
const total = res.data.data.total || 0;
this.total = total;
// 过滤出当前年份的数据
const yearFilteredRecords = records.filter(post => {
if (!post.created_at) return false;
const dateStr = post.created_at;
const postYear = new Date(dateStr).getFullYear();
return postYear === this.selectedYear;
});
if (isLoadMore) {
// 上拉加载更多,追加数据并去重
const existingIds = new Set(this.postList.map(item => item.id));
const newRecords = yearFilteredRecords.filter(item => !existingIds.has(item.id));
this.postList = [...this.postList, ...newRecords];
} else {
// 下拉刷新或初次加载,替换数据
this.postList = yearFilteredRecords;
}
// 重新分组
this.groupPostsByDate();
// 判断是否还有更多数据
// 如果当前页返回的原始数据少于pageSize,说明没有更多了
// 或者已经加载到最后一页
if (records.length < this.pageSize) {
this.hasMore = false;
} else {
// 计算理论上最多能加载多少页
const maxPages = Math.ceil(total / this.pageSize);
this.hasMore = this.page < maxPages;
}
}
}).catch(() => {
this.loading = false;
this.loadingMore = false;
this.refreshing = false;
if (isLoadMore) {
this.page -= 1; // 加载失败,页码回退
}
});
},
// 按日期分组帖子
groupPostsByDate() {
const grouped = {};
this.postList.forEach(post => {
const dateStr = post.created_at || post.create_time || post.createdAt;
if (!dateStr) return;
const date = new Date(dateStr);
const month = date.getMonth() + 1; // 月份从0开始,需要+1
const day = date.getDate();
// 生成日期key,格式:MM-DD,用于排序
const dateKey = `${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
if (!grouped[dateKey]) {
grouped[dateKey] = {
day: day,
month: month,
posts: []
};
}
grouped[dateKey].posts.push(post);
});
// 按日期倒序排序(最新的在前)
const sortedKeys = Object.keys(grouped).sort((a, b) => {
// 按完整日期排序(年-月-日)
const dateA = new Date(this.selectedYear, parseInt(a.split('-')[0]) - 1, parseInt(a.split('-')[1]));
const dateB = new Date(this.selectedYear, parseInt(b.split('-')[0]) - 1, parseInt(b.split('-')[1]));
return dateB - dateA; // 倒序:最新的在前
});
// 重新构建分组对象,保持顺序
const sortedGrouped = {};
sortedKeys.forEach(key => {
sortedGrouped[key] = grouped[key];
});
this.groupedPosts = sortedGrouped;
},
// 下拉刷新
onRefresh() {
// 如果正在加载中,不执行刷新
if (this.loading || this.loadingMore) {
this.refreshing = false;
return;
}
this.refreshing = true;
this.page = 1;
this.hasMore = true;
this.loadPostList(false);
},
// 上拉加载更多
onLoadMore() {
if (!this.hasMore || this.loadingMore || this.loading || this.refreshing) {
return;
}
this.loadPostList(true);
},
// 查看帖子详情
viewPost(post) {
// TODO: 跳转到帖子详情页
uni.navigateTo({
url: '/pages/square/detiles?id='+post.id
});
}
}
}
import { request } from '@/api/apiT.js'
import PostCard from '@/components/PostCard.vue'
import uTabs from '@/uni_modules/uview-ui/components/u-tabs/u-tabs.vue'
export default {
components: { PostCard,uTabs },
data() {
return {
list1: [{
name: '全部',
value:''
}, {
name: '已发布',
value:'1'
}, {
name: '审核中',
value:'2'
}, {
name: '未通过',
value:'3'
}, {
name: '已过期',
value:'4'
}],
status:'',
slist:[],
loading: false,
refreshing: false,
loadingMore: false,
page: 1,
pageSize: 10,
hasMore: true,
defaultTags: ['样机机', '9成新']
}
},
onShow() {
this.loadList()
// this.syncTabbar()
},
methods: {
leftClick() {
// uni.navigateBack()
uni.reLaunch({
url: '/pages/index/mine',// 替换为你要跳转的页面路径
});
},
goto(item){
uni.navigateTo({
url: '/pages/square/detiles?id='+item.id
});
},
click(item) {
this.page = 1
this.hasMore = true
this.slist = []
this.status=item.value
this.loadList()
},
loadList(isLoadMore = false) {
if (isLoadMore) {
if (this.loadingMore || this.loading || this.refreshing || !this.hasMore) return
this.loadingMore = true
this.page += 1
} else {
if (this.loading || this.loadingMore) return
this.loading = true
this.page = 1
this.hasMore = true
this.slist = []
}
request({
url: 'common/post/list',
method: 'GET',
data: {
current: this.page,
size: this.pageSize,
status: this.status
}
}).then(res => {
this.loading = false
this.loadingMore = false
this.refreshing = false
if (res.data.code === 200) {
const records = res.data.data.records || []
const total = res.data.data.total || 0
if (isLoadMore) {
this.slist = [...this.slist, ...records]
} else {
this.slist = records
}
this.hasMore = this.slist.length < total
}
}).catch(() => {
this.loading = false
this.loadingMore = false
this.refreshing = false
if (isLoadMore) {
this.page -= 1
}
})
},
onRefresh() {
if (this.loading || this.loadingMore) {
this.refreshing = false
return
}
this.refreshing = true
this.loadList(false)
},
onLoadMore() {
this.loadList(true)
},
navToList(item) {
uni.navigateTo({
url: '/pages/square/detiles?id=' + item.id
})
},
leftClick() {
uni.navigateBack()
}
}
}
</script>
<style lang="scss">
page,
.content {
height: 100%;
background-color: #fff;
}
.year-selector {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 30rpx;
background-color: #F5F5F5;
.year-text {
font-size: 28rpx;
color: #333;
}
.arrow-icon {
width: 24rpx;
height: 24rpx;
}
}
.scroll-container {
height: calc(100% - 168rpx); // 减去导航栏和年份选择器高度
}
.post-list {
padding: 20rpx 0;
}
.date-group {
margin-bottom: 40rpx;
}
.date-item {
display: flex;
padding: 0 30rpx;
}
.date-label {
display: flex;
// flex-direction: column;
// align-items: center;
margin-right: 30rpx;
min-width: 120rpx;
.date-day {
font-size: 48rpx;
font-weight: bold;
color: #333;
line-height: 1;
}
.date-month {
font-size: 24rpx;
color: #666;
margin-top: 8rpx;
}
}
.posts-container {
flex: 1;
display: flex;
flex-direction: column;
gap: 20rpx;
}
.post-item {
display: flex;
// align-items: center;
margin-bottom: 30rpx;
.post-image {
width: 200rpx;
height: 200rpx;
background-color: #E5E5E5;
border-radius: 10rpx;
margin-right: 30rpx;
flex-shrink: 0;
}
.post-tag {
font-size: 28rpx;
color: #333;
flex: 1;
}
}
.empty-view {
text-align: center;
padding: 100rpx 0;
color: #999;
font-size: 28rpx;
}
.load-more {
width: 100%;
text-align: center;
padding: 30rpx 0;
font-size: 26rpx;
color: #999;
}
.loading-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
.loading-text {
color: #fff;
font-size: 28rpx;
background-color: rgba(0, 0, 0, 0.7);
padding: 20rpx 40rpx;
border-radius: 8rpx;
}
}
page,
.page {
height: 100%;
background: #f8f8f8;
}
.tabs{
background: #fff;
}
.list-scroll {
height: calc(100vh - 120rpx);
}
.feed-list {
// padding: 20rpx;
margin: 20rpx 30rpx;
}
.load-more {
width: 100%;
text-align: center;
padding: 30rpx 0;
font-size: 26rpx;
color: #999;
}
.empty {
width: 100%;
text-align: center;
padding: 100rpx 0;
}
.empty image {
width: 252rpx;
height: 164rpx;
margin-bottom: 20rpx;
}
.loading-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
.loading-text {
color: #fff;
font-size: 28upx;
background-color: rgba(0, 0, 0, 0.7);
padding: 20upx 40upx;
border-radius: 8upx;
}
}
</style>
+20 -15
View File
@@ -2,21 +2,23 @@
<view class="home-page">
<z-paging ref="paging" v-model="contList" @query="queryList" empty-view-text="暂无消息">
<template #top>
<uni-nav-bar leftText=" " :fixed="true" :border="false" leftIcon="back" @clickLeft="leftClick" color="#000000" backgroundColor="#fff" :statusBar="true" title="最新消息"></uni-nav-bar>
<uni-nav-bar leftText=" " :fixed="true" :border="false" leftIcon="back" @clickLeft="leftClick" color="#000000" backgroundColor="#fff" :statusBar="true" title="我的信息"></uni-nav-bar>
</template>
<view class="msg-list">
<view class="msg-item" v-for="item in contList" :key="item.id">
<image class="msg-avatar" :src="item.sender_user.avatar_pull" mode="aspectFill"></image>
<view class="msg-body">
<view class="msg-header">
<text class="msg-name">{{item.sender_user.nickname}}</text>
<text class="msg-time">{{item.created_at}}</text>
<view class="msg-cont">
<image class="msg-avatar" :src="item.sender_user.avatar_pull" mode="aspectFill"></image>
<view class="msg-body">
<view class="msg-header">
<text class="msg-name">{{item.sender_user.nickname}}</text>
<!-- <text class="msg-time">{{item.created_at}}</text> -->
</view>
</view>
<text class="msg-text">
<text>{{ item.type==1?'点赞了': item.type==2?'收藏了':'评论了' }} </text>
<text>{{ formatContent(item.post && item.post.content) }}</text>
</text>
</view>
<view class="msg-text">
<text>{{ formatContent(item.post && item.post.content) }}</text>
</view>
</view>
</view>
</z-paging>
@@ -80,15 +82,18 @@
padding: 20rpx;
}
.msg-item{
display: flex;
align-items: center;
padding: 24rpx 20rpx;
margin-bottom: 20rpx;
border-bottom: 1px solid #e4e4e4;
background: #fff;
border-radius: 10rpx;
}
.msg-cont{
display: flex;
align-items: center;
}
.msg-avatar{
width: 96rpx;
height: 96rpx;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
margin-right: 20rpx;
}
+9 -16
View File
@@ -6,15 +6,12 @@
</template>
<!-- 内容网格 -->
<view class="content-grid">
<square-post-card
class="grid-item"
v-for="sitem in contList"
:key="sitem.id"
:item="sitem.post"
:isshow="false"
@view="navToList"
@comment="viewPost"
@like="viewPost" />
<classify-card
v-for="item in contList"
:key="item.id"
:item="item.post"
@view="navToList"
/>
</view>
</z-paging>
</view>
@@ -22,10 +19,10 @@
<script>
import { request,showToast } from '@/api/apiT.js'
import SquarePostCard from '@/components/SquarePostCard.vue'
import ClassifyCard from '@/components/ClassifyCard.vue'
export default {
components: {
SquarePostCard
ClassifyCard
},
data() {
return {
@@ -87,11 +84,7 @@
padding-bottom: 120rpx;
}
.content-grid{
display: flex;
flex-wrap: wrap;
padding: 20rpx;
justify-content: space-between;
background: #fff;
margin-top: 20rpx;
}
.grid-item{
width: 48%;
+154
View File
@@ -0,0 +1,154 @@
<template>
<view class="container">
<uni-nav-bar leftText=" " :fixed="true" :border="false" leftIcon="back" @clickLeft="leftClick" color="#000000" backgroundColor="#fff" :statusBar="true" title="回复我的"></uni-nav-bar>
<view class="reply-list">
<view class="reply-card" v-for="(reply, index) in replyList" :key="index">
<!-- 主回复 -->
<view class="reply-item">
<view class="reply-heard">
<image :src="reply.avatar" class="avatar"></image>
<view class="reply-content">
<view class="username">{{ reply.username }}</view>
<view class="time">{{ reply.time }}</view>
</view>
</view>
<view class="reply-content">
<text class="reply-text">{{ reply.content }}</text>
</view>
</view>
<!-- 子回复 -->
<view class="sub-reply" v-for="(subReply, subIndex) in reply.subReplies" :key="subIndex">
<view class="reply-heard">
<image :src="subReply.avatar" class="avatar"></image>
<view class="reply-header">
<view class="username">{{ subReply.username }}</view>
</view>
</view>
<view class="reply-content">
<text class="reply-text">{{ subReply.content }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
replyList: [
{
avatar: '/static/image/user.png',
username: '用户名用户名',
time: '2025年12月04日10:56:44',
content: '这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复',
subReplies: [
{
avatar: '/static/image/user.png',
username: '田户名田户名',
time: '2025年12月04日10:56:44',
content: '西门子全新款饮机品牌西门子全新款饮机品牌西门子全新...'
}
]
},
{
avatar: '/static/image/user.png',
username: '用户名用户名',
time: '2025年12月04日10:56:44',
content: '这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复这是回复',
subReplies: [
{
avatar: '/static/image/user.png',
username: '用户名用户名',
time: '2025年12月04日10:56:44',
content: '西门子全新款饮机品牌西门子全新款饮机品牌西门子全新...'
}
]
}
]
}
},
methods: {
leftClick() {
uni.reLaunch({
url: '/pages/index/mine',// 替换为你要跳转的页面路径
});
}
}
}
</script>
<style scoped>
.container {
min-height: 100vh;
}
.reply-list {
padding: 0;
}
.reply-card {
background-color: #fff;
margin: 10px;
border-radius: 8px;
padding: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.reply-item {
/* display: flex; */
margin-bottom: 12px;
}
.reply-heard{
display: flex;
gap: 10px;
}
.sub-reply {
/* margin-left: 30px; */
padding:8px 12px;
background-color: #F5F6FA;
border-radius: 6px;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
flex-shrink: 0;
}
.reply-content {
flex: 1;
min-width: 0;
}
.reply-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
}
.username {
font-size: 13px;
color: #333;
font-weight: 500;
}
.time {
font-size: 12px;
color: #999;
}
.reply-text {
font-size: 13px;
color: #666;
line-height: 1.5;
word-break: break-all;
display: block;
}
</style>
+225 -81
View File
@@ -1,68 +1,87 @@
<template>
<view class="publish-page">
<uni-nav-bar leftText=" " :fixed="true" :border="false" @clickLeft="leftClick" color="#000000" backgroundColor="#fff" :statusBar="true" title="发布页"></uni-nav-bar>
<uni-nav-bar leftText=" " :fixed="true" :border="false" @clickLeft="leftClick" color="#000000" backgroundColor="#fff" :statusBar="true" title="样机发布"></uni-nav-bar>
<view class="form-card">
<view class="section-label">选择发布标签</view>
<picker mode="selector" :range="categoryOptions" range-key="name" @change="onCategoryChange">
<view class="select-input gradient-bg">
<view class="category-content">
<view class="fridge-icon">
<image :src="iconurl" mode=""></image>
</view>
<text class="category-text">{{form.category || '请选择发布标签'}}</text>
</view>
<u-icon name="arrow-down" color="#fff" size="20"></u-icon>
<view class="select-input gradient-bg" @click="showSelect1 = true">
<view class="category-content">
<text class="category-text">{{form.category || '请输入/选择型号'}}</text>
</view>
</picker>
<view class="mag-top">
<textarea v-model="form.content" class="content-textarea" placeholder="请输入文字内容"
:maxlength="maxWords" @input="handleContentInput" />
<u-icon name="arrow-down" color="#3BC8BE" size="20"></u-icon>
</view>
<view class="media-upload-wrapper mag-top">
<uni-file-picker fileMediatype="image" limit="9" @select="selectMedia" @delete="delfn" :imageStyles="imageStyles">
<view class="upload-icon-wrapper">
<image src="/static/image/photo.png" mode="" class="addimg"></image>
<text class="upload-text">添加图片</text>
</view>
</uni-file-picker>
<!-- <view class="upload-area" @click="selectMedia" v-if="form.media.length < maxMedia">
<view class="upload-icon-wrapper">
<view class="camera-icon">
<image src="/static/image/photo.png" mode=""></image>
<view class="conts mag-top">
<view class="">
<textarea v-model="form.content" class="content-textarea" placeholder="请输入文字内容"
:maxlength="maxWords" @input="handleContentInput" />
</view>
<view class="media-upload-wrapper">
<uni-file-picker fileMediatype="image" limit="9" @select="selectMedia" @delete="delfn" :imageStyles="imageStyles">
<view class="upload-icon-wrapper">
<image src="/static/image/photo.png" mode="" class="addimg"></image>
</view>
<text class="upload-text">添加图片</text>
</view>
</view> -->
<!-- <view class="media-preview" v-for="(item, index) in form.media" :key="index">
<image :src="item.url" mode="aspectFill" class="preview-image"></image>
<view class="delete-btn" @click.stop="deleteMedia(index)">
<u-icon name="close" size="18" color="#fff"></u-icon>
</view>
</view> -->
</uni-file-picker>
</view>
<view class="help-text">
<u-icon name="info-circle" size="18" color="#A6ABB7"></u-icon>
<text>文字不多于2000字,图片不多于9张</text>
</view>
</view>
<view class="help-text">
<u-icon name="info-circle" size="18" color="#A6ABB7"></u-icon>
<text>文字不多于2000字,图片不多于9张</text>
<view class="mag-top">
<picker mode="selector" :range="conditionOptions" range-key="name" @change="onCategoryChange">
<view class="select-input gradient-bg">
<view class="category-content">
<text class="category-text">{{form.agename || '请选择样机新旧程度'}}</text>
</view>
<u-icon name="arrow-down" color="#3BC8BE" size="20"></u-icon>
</view>
</picker>
</view>
</view>
<view class="agreement">
<label class="agree-label" @click="toggleAgreement">
<checkbox :checked="agree" style="transform:scale(0.8)"></checkbox>
<text>我已阅读并同意</text>
<text class="link" @click.stop="viewAgreement">《发布协议》</text>
</label>
</view>
<!-- <button class="primary-btn gradient-btn" :class="{disabled: !canSubmit}" :disabled="!canSubmit" @click="submitForm">立即发布</button> -->
<button class="primary-btn gradient-btn" @click="submitForm">立即发布</button>
<u-popup :show="show" :closeOnClickOverlay="false" :round="10" mode="center">
<view class="cont-text" >
<view class="" v-html="viewAgreementcont">
<view class="mag-top">
<input class="uni-input" focus v-model="form.price" placeholder="请输入折扣价格" />
</view>
<view class="detils">
<view v-html="productcontents">
</view>
</view>
</view>
<!-- 型号选择弹层 -->
<view v-if="showSelect1" class="select-popup">
<view class="popup-mask" @click="showSelect1=false"></view>
<view class="popup-panel">
<view class="popup-header">
<input class="popup-search" v-model="searchKey1" placeholder="搜索型号" />
<text class="popup-close" @click="showSelect1=false">取消</text>
</view>
<scroll-view scroll-y class="popup-list">
<view
v-for="item in filteredCategories"
:key="item.id"
class="popup-item"
@click="selectCategory(item)"
>
{{ item.model }}
</view>
<view v-if="!filteredCategories.length" class="popup-empty">暂无数据</view>
</scroll-view>
</view>
</view>
<view class="foot">
<view class="agreement">
<label class="agree-label" @click="toggleAgreement">
<checkbox :checked="agree" style="transform:scale(0.8)"></checkbox>
<text>我已阅读并同意</text>
<text class="link" @click.stop="viewAgreement">《发布协议》</text>
</label>
<button class="primary-btn gradient-btn" @click="submitForm">立即发布</button>
</view>
</view>
<u-popup :show="show" :closeOnClickOverlay="false" :round="10" mode="center">
<view class="cont-text" >
<rich-text :nodes="viewAgreementcont"></rich-text>
</view>
<view class="cent">
<button class="btn1" @click="show = !show">取消</button>
<button class="btn" @click="agreementbtn">确定</button>
@@ -94,17 +113,36 @@
</template>
<script>
import { request,showToast } from '@/api/apiT.js'
import { request,showToast } from '@/api/apiT.js'
export default {
name: 'PublishPage',
data(){
return {
showSelect1:false,
showSelect2:false,
searchKey1:'',
productcontents:'',
categoryOptions:[],
conditionOptions:[
{name:'全新',value:'10'},
{name:'9成新',value:'9'},
{name:'8成新',value:'8'},
{name:'7成新',value:'7'},
{name:'6成新',value:'6'},
{name:'5成新',value:'5'},
{name:'4成新',value:'4'},
{name:'3成新',value:'3'},
{name:'2成新',value:'2'},
{name:'1成新',value:'1'},
],
form:{
agename:'',
age:'',
category:'',
category_id:'',
sku_id:'',
content:'',
media:[]
media:[],
price:''
},
selfChecked: false,
showlogin:false,
@@ -119,16 +157,38 @@ export default {
computed:{
canSubmit(){
return this.agree && this.form.content.trim().length>0 && this.form.media.length>0
}
},
filteredCategories(){
if(!this.searchKey1) return this.categoryOptions
return this.categoryOptions.filter(i => (i.model || '').toLowerCase().includes(this.searchKey1.toLowerCase()))
}
},
onLoad() {
this.getcategory()
},
onShow() {
this.syncTabbar()
// this.getset()
// this.syncTabbar()
},
methods:{
getlocation(){
wx.getFuzzyLocation({
type: 'wgs84',
success(res) {
console.log("模糊定位:", res)
},
fail(err) {
console.log("模糊定位失败:", err)
}
})
},
selectCategory(item){
this.form.category = item.model || ''
this.form.sku_id = item.id || ''
this.productcontents = item.desc || ''
console.log(this.productcontents)
this.showSelect1 = false
},
goprivacy(){
uni.navigateTo({
url: '/pages/login/agreement',// 替换为你要跳转的页面路径
@@ -176,16 +236,14 @@ export default {
}
})
},
//商品下拉
getcategory(){
request({
url:'common/post/category/list',
url:'common/sku/preset',
method:'GET'
}).then(res => {
if (res.data.code == 200) {
this.categoryOptions = res.data.data.records || [];
this.form.category = res.data.data.records[0].name
this.form.category_id = res.data.data.records[0].id
this.iconurl = res.data.data.records[0].icon_preset_pull
this.categoryOptions = res.data.data|| [];
}
})
},
@@ -201,10 +259,9 @@ export default {
onCategoryChange(e){
console.log(e)
const index = e.detail.value; // picker 返回的 index
const selectCity = this.categoryOptions[index]; // 直接取整条数据
this.iconurl = selectCity.icon_preset_pull
this.form.category = selectCity.name
this.form.category_id = selectCity.id
const selectCity = this.conditionOptions[index]; // 直接取整条数据
this.form.agename = selectCity.name
this.form.age = selectCity.name
},
handleContentInput(e){
if(e.detail.value.length>this.maxWords){
@@ -214,7 +271,7 @@ export default {
uploadfn(imgUrl){
return new Promise((resolve, reject) => {
uni.uploadFile({
url: 'https://bsh.info.hb.hnzhwlkj.cn/upload/image?scene=postlist',
url: 'https://cb.hnxinyouhe.com/upload/image?scene=postlist',
filePath: imgUrl,
name: 'image',
header:{'content-type': 'application/x-www-form-urlencoded','Fdesk-Auth-Token': 'Bearer ' + uni.getStorageSync('token'),'Fdesk-Language': uni.getStorageSync('i18locale')?uni.getStorageSync('i18locale'):'zh','Fdesk-Login-Type': 'wechat','Fdesk-System-Code':'wechat'},
@@ -314,6 +371,9 @@ export default {
</script>
<style lang="scss">
.detils{
margin-top: 40rpx;
}
.cent{
display: flex;
align-items: center;
@@ -356,7 +416,7 @@ export default {
.form-card{
background: #fff;
padding: 30rpx;
margin-bottom: 24rpx;
margin-bottom: 400rpx;
box-shadow: 0 12rpx 30rpx rgba(0,0,0,0.04);
}
.section-label{
@@ -374,7 +434,19 @@ export default {
justify-content: space-between;
}
.gradient-bg{
background: linear-gradient(90deg, #206ff4 0%, #42e0a2 100%);
background: #F4F4F4;
color: #999999;
}
.conts{
background: #F4F4F4;
color: #999999;
padding: 20rpx;
border-radius: 10rpx;
}
.uni-input{
background: #F4F4F4;
padding: 20rpx 25rpx;
border-radius: 10rpx;
}
.category-content{
display: flex;
@@ -418,7 +490,7 @@ export default {
}
.category-text{
font-size: 28rpx;
color: #fff;
color: #999999;
font-weight: 500;
}
.mag-top{
@@ -458,8 +530,8 @@ export default {
margin-bottom: 12rpx;
}
.addimg{
width: 26px;
height: 22px;
width: 215rpx;
height: 215rpx;
}
.upload-text{
font-size: 26rpx;
@@ -496,10 +568,14 @@ export default {
align-items: center;
gap: 8rpx;
}
.agreement{
margin: 20rpx 0 40rpx;
padding: 20px 12px;
.foot{
background: #fff;
position: fixed;
bottom: 1rpx;
width: 100%;
}
.agreement{
margin: 20px 12px;
}
.agree-label{
display: flex;
@@ -515,15 +591,83 @@ export default {
}
.primary-btn{
color: #fff;
border-radius: 999px;
border-radius: 20rpx;
height: 88rpx;
line-height: 88rpx;
font-size: 32rpx;
border: none;
margin: 40px 12px;
}
.gradient-btn{
background: linear-gradient(180deg, #206ff4 0%, #42e0a2 100%);
background: #3BC8BE;
margin-top: 20rpx;
}
.select-popup{
position: fixed;
inset: 0;
z-index: 999;
}
.popup-mask{
position: absolute;
inset: 0;
background: rgba(0,0,0,0.35);
}
.popup-panel{
position: absolute;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
max-height: 40vh; /* 控制整体可视高度 */
display: flex;
flex-direction: column;
padding: 20rpx;
box-sizing: border-box;
}
.popup-header{
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.popup-search{
flex: 1;
height: 72rpx;
border-radius: 10rpx;
background: #f4f4f4;
padding: 0 20rpx;
font-size: 28rpx;
color: #333;
margin-right: 12rpx;
box-sizing: border-box;
}
.popup-title{
font-size: 28rpx;
color: #333;
}
.popup-close{
font-size: 28rpx;
color: #3BC8BE;
}
.popup-list{
flex: 1;
max-height: calc(40vh - 140rpx); /* 预留头部高度,保证可滚动 */
}
.popup-item{
padding: 20rpx 12rpx;
border-bottom: 1px solid #f0f0f0;
font-size: 28rpx;
color: #333;
}
.popup-item:last-child{
border-bottom: none;
}
.popup-empty{
text-align: center;
color: #999;
font-size: 26rpx;
padding: 30rpx 0;
}
.cont-text{
max-width: 350px;
+789 -771
View File
File diff suppressed because it is too large Load Diff
+298 -440
View File
@@ -1,449 +1,307 @@
<template>
<view class="content">
<uni-nav-bar leftText=" " :fixed="true" :border="false" @clickLeft="leftClick" color="#000000" backgroundColor="#fff" :statusBar="true" title="广场"></uni-nav-bar>
<view class="content-con">
<scroll-view scroll-y class="left-aside">
<view v-for="item in flist" :key="item.id" class="f-item b-b" :class="{active: item.id === currentCategoryId}"
@click="onCategoryChange(item.id)">
<view class="category-icon" v-if="item.icon_active_pull">
<image :src="item.id === currentCategoryId?item.icon_active_pull:item.icon_inactive_pull" mode=""></image>
️</view>
{{item.name}}
</view>
</scroll-view>
<scroll-view scroll-y class="right-aside"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
:lower-threshold="100"
@scrolltolower="onLoadMore">
<view class="t-lists">
<square-post-card
class="t-item"
v-for="sitem in slist"
:key="sitem.id"
:item="sitem"
:isshow="true"
@view="navToList"
@comment="navTomsg"
@like="navTozan" />
<!-- 加载更多提示 -->
<view v-if="hasMore" class="load-more">
<text v-if="loadingMore">加载中...</text>
<text v-else>上拉加载更多</text>
</view>
<view v-else-if="slist.length > 0" class="load-more">
<text>没有更多了</text>
</view>
<view class="empty" v-if="slist.length==0">
<image src="/static/image/empty.png" mode=""></image>
<view class="">
暂无数据!
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 加载提示 -->
<view v-if="loading" class="loading-mask">
<view class="loading-text">加载中...</view>
</view>
<view class="page">
<uni-nav-bar :fixed="true" :border="false" color="#000" backgroundColor="#fff" :statusBar="true" title="广场" />
<!-- Tabs -->
<view class="tabs">
<view :class="['tab', currentTab==='latest'?'on':'']" @click="changeTab('latest')">最新发布</view>
<view class="divider"></view>
<view :class="['tab', currentTab==='near'?'on':'']" @click="changeTab('near')">距离最近</view>
</view>
</template>
<!-- Feed list -->
<scroll-view
scroll-y
class="list-scroll"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
:lower-threshold="100"
@scrolltolower="onLoadMore"
>
<view class="feed-list">
<post-card
v-for="sitem in slist"
:key="sitem.id || sitem.username"
:item="sitem"
@userClick="goto"
@view="goto"
@contact="handleContact"
@comment="handleComment">
</post-card>
<!-- 状态提示 -->
<view v-if="hasMore" class="load-more">
<text v-if="loadingMore">加载中...</text>
<text v-else>上拉加载更多</text>
</view>
<view v-else-if="slist.length > 0" class="load-more">
<text>没有更多了</text>
</view>
<view class="empty" v-if="slist.length === 0 && !loading">
<image src="/static/image/empty.png" mode="aspectFit"></image>
<view>暂无数据!</view>
</view>
</view>
</scroll-view>
<!-- 加载提示 -->
<view v-if="loading" class="loading-mask">
<view class="loading-text">加载中...</view>
</view>
</view>
</template>
<script>
import { request,showToast } from '@/api/apiT.js'
import SquarePostCard from '@/components/SquarePostCard.vue'
export default {
components: {
SquarePostCard
},
data() {
return {
flist: [],
slist: [],
currentCategoryId: '', // 当前选中的分类id
loading: false, // 加载状态
refreshing: false, // 下拉刷新状态
loadingMore: false, // 上拉加载状态
page: 1, // 当前页码
pageSize: 10, // 每页数量
hasMore: true, // 是否还有更多数据
categoriesLoaded: false // 分类是否已加载完成
}
},
onLoad() {
// 加载第一级分类列表
},
onShow() {
this.loadCategoryList();
this.syncTabbar()
// // 从详情页返回后,重新拉取当前分类数据
// if (this.categoriesLoaded) {
// this.refreshCurrentCategory()
// }
},
methods: {
// 加载第一级分类列表
loadCategoryList() {
// TODO: 替换为实际的后端接口
let that = this;
request({
url:'common/post/category/list',
method:'GET'
}).then(res => {
if (res.data.code == 200) {
const records = res.data.data.records || [];
this.flist = [{
id: '',
name: '全部分类'
}, ...records];
this.categoriesLoaded = true;
// 默认加载第一个分类的数据
if (this.flist.length > 0) {
this.onCategoryChange(this.flist[0].id);
}
}
})
},
// 重新加载当前分类数据
refreshCurrentCategory() {
const targetCategory = this.currentCategoryId !== undefined ? this.currentCategoryId : (this.flist[0] && this.flist[0].id) || '';
this.loadCategoryData(targetCategory, false);
},
// 分类切换事件
onCategoryChange(categoryId) {
// 如果点击的是当前分类且已有数据,不重复加载
if (this.currentCategoryId === categoryId && this.slist.length > 0) {
return;
}
// 切换分类时,重置所有状态
this.currentCategoryId = categoryId;
this.page = 1;
this.hasMore = true;
this.loading = false;
this.loadingMore = false;
this.refreshing = false;
this.slist = []; // 先清空数据
this.loadCategoryData(categoryId, false);
},
// 根据分类id加载分类下的内容
loadCategoryData(categoryId, isLoadMore = false) {
// 防止重复请求:如果是加载更多,需要检查所有加载状态
if (isLoadMore) {
if (this.loadingMore || this.loading || this.refreshing) {
return;
}
} else {
// 下拉刷新或初次加载,检查加载状态
if (this.loading || this.loadingMore) {
return;
}
}
if (isLoadMore) {
this.loadingMore = true;
this.page += 1;
} else {
this.loading = true;
this.page = 1;
this.hasMore = true;
this.slist = []; // 清空之前的数据
}
request({
url:'common/post/list',
method:'GET',
data:{
category_id:categoryId,
current: this.page,
size: this.pageSize
}
}).then(res => {
this.loading = false;
this.loadingMore = false;
this.refreshing = false;
if (res.data.code == 200) {
const records = res.data.data.records || [];
const total = res.data.data.total || 0;
if (isLoadMore) {
// 上拉加载更多,追加数据并去重
const existingIds = new Set(this.slist.map(item => item.id));
const newRecords = records.filter(item => !existingIds.has(item.id));
this.slist = [...this.slist, ...newRecords];
} else {
// 下拉刷新或初次加载,替换数据
this.slist = records;
}
// 判断是否还有更多数据
this.hasMore = this.slist.length < total;
}
}).catch(() => {
this.loading = false;
this.loadingMore = false;
this.refreshing = false;
if (isLoadMore) {
this.page -= 1; // 加载失败,页码回退
}
})
},
// 下拉刷新
onRefresh() {
// 如果正在加载中,不执行刷新
if (this.loading || this.loadingMore) {
this.refreshing = false;
return;
}
this.refreshing = true;
this.loadCategoryData(this.currentCategoryId, false);
},
// 上拉加载更多
onLoadMore() {
if (!this.hasMore || this.loadingMore || this.loading || this.refreshing) {
return;
}
this.loadCategoryData(this.currentCategoryId, true);
},
// 点击商品项
navToList(item) {
uni.navigateTo({
url: '/pages/square/detiles?id='+item.id
});
},
//点赞
navTozan(item){
const targetIndex = this.slist.findIndex(post => post.id === item.id)
if(targetIndex === -1){
return
}
const target = this.slist[targetIndex]
const isLiked = Number(target.is_like) === 1
const likeUrl = isLiked ? 'common/post/like/destroy' : 'common/post/like/store'
const method = isLiked ? 'GET' : 'POST'
const data = {
post_id: target.id
}
if (isLiked) {
data.user_id = target.user_id
}
request({
url: likeUrl,
method,
data
}).then(res => {
if(res.data.code === 200){
const newTarget = { ...target }
if(isLiked){
newTarget.is_like = 0
newTarget.like = Math.max(0, (Number(newTarget.like) || 0) - 1)
uni.showToast({
title: '取消成功',
icon: 'none'
});
}else{
newTarget.is_like = 1
newTarget.like = (Number(newTarget.like) || 0) + 1
uni.showToast({
title: '点赞成功',
icon: 'none'
});
}
this.$set(this.slist, targetIndex, newTarget)
}else{
uni.showToast({
title:res.data.message,
icon: 'none'
});
}
})
},
//消息
navTomsg(item){
request({
url:'common/like/store',
method:'GET',
data:{
post_id:item.id
}
}).then(res => {
})
},
leftClick(){
uni.navigateBack()
},
syncTabbar(){
uni.setTabBarItem({ index:0, text:this.$t('tabbar.home') })
uni.setTabBarItem({ index:1, text:this.$t('tabbar.square') })
uni.setTabBarItem({ index:2, text:this.$t('tabbar.publish') })
uni.setTabBarItem({ index:3, text:this.$t('tabbar.mine') })
}
import { request } from '@/api/apiT.js'
import PostCard from '@/components/PostCard.vue'
export default {
components: { PostCard },
data() {
return {
tabs: [
{ key: 'latest', label: '最新发布' },
{ key: 'near', label: '距离最近' }
],
currentTab: 'latest',
slist:[
{
username:'用户名用户名',
time:'2025 年 12 月 4 日 10:56',
content:`西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子
西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子西门子全新嵌饮机品牌
西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子`,
tag:['#散饮机','#9成新'],
location:'门店位置',
image:[
{img_src:'/static/image/bx.jpg'},
{img_src:'/static/image/bx.jpg'},
{img_src:'/static/image/bx.jpg'},
{img_src:'/static/image/bx.jpg'},
{img_src:'/static/image/bx.jpg'},
{img_src:'/static/image/bx.jpg'}
]
},{
username:'用户名用户名',
time:'2025 年 12 月 4 日 10:56',
content:`西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子
西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子西门子全新嵌饮机品牌
西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子`,
tag:['#散饮机','#9成新'],
location:'门店位置',
image:[
{img_src:'/static/image/bx.jpg'},
{img_src:'/static/image/bx.jpg'},
{img_src:'/static/image/bx.jpg'}
]
},{
username:'用户名用户名',
time:'2025 年 12 月 4 日 10:56',
content:`西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子
西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子西门子全新嵌饮机品牌
西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子全新嵌饮机品牌西门子`,
tag:['#散饮机','#9成新'],
location:'门店位置',
image:[
{img_src:'/static/image/bx.jpg'},
{img_src:'/static/image/bx.jpg'}
]
}
}
],
loading: false,
refreshing: false,
loadingMore: false,
page: 1,
pageSize: 10,
hasMore: true,
defaultTags: ['样机机', '9成新']
}
},
onShow() {
this.loadList()
// this.syncTabbar()
},
methods: {
syncTabbar() {
uni.setTabBarItem({ index: 0, text: this.$t('tabbar.home') })
uni.setTabBarItem({ index: 1, text: this.$t('tabbar.square') })
uni.setTabBarItem({ index: 2, text: this.$t('tabbar.publish') })
uni.setTabBarItem({ index: 3, text: this.$t('tabbar.mine') })
},
changeTab(key) {
if (this.currentTab === key) return
this.currentTab = key
this.page = 1
this.hasMore = true
this.slist = []
this.loadList()
},
loadList(isLoadMore = false) {
if (isLoadMore) {
if (this.loadingMore || this.loading || this.refreshing || !this.hasMore) return
this.loadingMore = true
this.page += 1
} else {
if (this.loading || this.loadingMore) return
this.loading = true
this.page = 1
this.hasMore = true
this.slist = []
}
const sortKey = this.currentTab === 'near' ? 'distance' : 'latest'
request({
url: 'common/post/list',
method: 'GET',
data: {
current: this.page,
size: this.pageSize,
sort: sortKey
}
}).then(res => {
this.loading = false
this.loadingMore = false
this.refreshing = false
if (res.data.code === 200) {
const records = res.data.data.records || []
const total = res.data.data.total || 0
if (isLoadMore) {
this.slist = [...this.slist, ...records]
} else {
this.slist = records
}
this.hasMore = this.slist.length < total
}
}).catch(() => {
this.loading = false
this.loadingMore = false
this.refreshing = false
if (isLoadMore) {
this.page -= 1
}
})
},
onRefresh() {
if (this.loading || this.loadingMore) {
this.refreshing = false
return
}
this.refreshing = true
this.loadList(false)
},
onLoadMore() {
this.loadList(true)
},
navToList(item) {
uni.navigateTo({
url: '/pages/square/detiles?id=' + item.id
})
},
leftClick() {
uni.navigateBack()
}
},
computed: {
underlineTransform() {
const index = this.tabs.findIndex(t => t.key === this.currentTab)
return `translateX(${index * 50}%)`
}
}
}
</script>
<style lang='scss'>
.empty{
text-align: center;
margin-top: 30%;
width: 100%;
}
.empty image{
width: 252px;
height: 164px;
}
page,
.content {
height: 100%;
background-color: #f8f8f8;
position: relative;
}
.loading-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
.loading-text {
color: #fff;
font-size: 28upx;
background-color: rgba(0, 0, 0, 0.7);
padding: 20upx 40upx;
border-radius: 8upx;
}
}
.content-con {
height: calc(100% - 128rpx);
background-color: #f8f8f8;
display: flex;
}
.left-aside {
flex-shrink: 0;
width: 200upx;
height: 100%;
background-color: #F7F7F7;
}
.f-item {
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
height: 90rpx;
font-size: 26rpx;
color: #666;
position: relative;
padding-left: 24rpx;
&.active {
color: #0071FC;
background: #fff;
&:before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
height: 36upx;
width: 8upx;
background-color: #0071FC;
border-radius: 0 4px 4px 0;
opacity: .8;
}
}
}
.iconview{
margin-right: 10px;
display: inline-block;
font-size: 22rpx;
}
.icons {
width: 16px;
height: 16px;
margin-right: 5px;
}
.right-aside {
flex: 1;
overflow: hidden;
/* padding-left: 20upx; */
/* padding-top: 20upx; */
background: #fff;
}
.t-lists {
display: flex;
flex-wrap: wrap;
padding: 20rpx;
justify-content: space-between;
}
.t-item{
width: 48%;
/* background: #fff; */
/* border-radius: 24rpx; */
overflow: hidden;
margin-bottom: 20rpx;
/* box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.06); */
}
.grid-image{
width: 100%;
height: 320rpx;
background: #E5E5E5;
border-radius: 10rpx;
}
.grid-footer{
padding: 20rpx;
display: flex;
align-items: center;
}
.grid-avatar{
width: 48rpx;
height: 48rpx;
border-radius: 50%;
margin-right: 12rpx;
}
.grid-info{
flex: 1;
display: flex;
flex-direction: column;
width: 70%;
}
.grid-tag{
font-size: 24rpx;
color: #3F435B;
margin-bottom: 4rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 87%;
}
.category-icon image{
height: 16px;
width: 16px;
margin-right: 10rpx;
}
.load-more{
width: 100%;
text-align: center;
padding: 30rpx 0;
font-size: 26rpx;
color: #999;
}
<style lang="scss">
page,
.page {
height: 100%;
background: #f8f8f8;
}
.tabs {
height: 80rpx;
background: #fff;
display: flex;
align-items: center;
justify-content: space-around;
gap: 24rpx;
position: relative;
font-size: 28rpx;
color: #666;
}
.tab {
position: relative;
padding: 12rpx 20rpx 18rpx;
color: #333;
}
.tab::after {
content: '';
position: absolute;
left: 50%;
bottom: 4rpx;
width: 50rpx;
height: 4rpx;
background: #05b3a8;
border-radius: 4rpx;
transform: translateX(-50%) scaleX(0);
transition: transform 0.2s;
}
.tab.on {
color: #05b3a8;
font-weight: 600;
}
.tab.on::after {
transform: translateX(-50%) scaleX(1);
}
.divider {
width: 1px;
height: 26rpx;
background: #dcdcdc;
}
.list-scroll {
height: calc(100vh - 120rpx);
}
.feed-list {
// padding: 20rpx;
margin: 20rpx 30rpx;
// background: #fff;
}
.load-more {
width: 100%;
text-align: center;
padding: 30rpx 0;
font-size: 26rpx;
color: #999;
}
.empty {
width: 100%;
text-align: center;
padding: 100rpx 0;
}
.empty image {
width: 252rpx;
height: 164rpx;
margin-bottom: 20rpx;
}
.loading-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
.loading-text {
color: #fff;
font-size: 28upx;
background-color: rgba(0, 0, 0, 0.7);
padding: 20upx 40upx;
border-radius: 8upx;
}
}
</style>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 894 B

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 881 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1013 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB