添加朋友圈功能
This commit is contained in:
@@ -197,3 +197,46 @@ export const getUserTradeRecordList = data => {
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 发布朋友圈 */
|
||||||
|
export const addUserMoments = data => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/userMoments',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取朋友圈列表 */
|
||||||
|
export const getUserMomentsList = data => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/userMoments/list',
|
||||||
|
method: 'get',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 点赞 */
|
||||||
|
export const likeUserMoments = id => {
|
||||||
|
return http({
|
||||||
|
url: `/api/service/userMoments/like/${id}`,
|
||||||
|
method: 'put'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 发布评论 */
|
||||||
|
export const addUserMomentsComment = data => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/userMoments/comment',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除 */
|
||||||
|
export const deleteUserMoments = id => {
|
||||||
|
return http({
|
||||||
|
url: `/api/service/userMoments/${id}`,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,72 +1,171 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { onPageScroll } from '@dcloudio/uni-app'
|
|
||||||
import { navigateTo } from '@/utils/router'
|
import { navigateTo } from '@/utils/router'
|
||||||
|
import {
|
||||||
|
addUserMomentsComment,
|
||||||
|
deleteUserMoments,
|
||||||
|
likeUserMoments,
|
||||||
|
getUserMomentsList
|
||||||
|
} from '@/api/my-index'
|
||||||
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||||
|
import { useAuthUser } from '@/composables/useAuthUser'
|
||||||
|
import { formatRelativeTime } from '@/utils/dateUtils'
|
||||||
|
import { useUI } from '@/utils/use-ui'
|
||||||
|
|
||||||
|
const { userInfo } = useAuthUser()
|
||||||
|
const { showDialog, showToast } = useUI()
|
||||||
|
|
||||||
|
const placeholderStyle = `font-family: PingFang SC, PingFang SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999999;
|
||||||
|
line-height: 40rpx;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;`
|
||||||
const MAX_SCROLL = 446
|
const MAX_SCROLL = 446
|
||||||
|
|
||||||
|
const paging = ref(null)
|
||||||
const cbNavBar = ref({})
|
const cbNavBar = ref({})
|
||||||
|
const dataList = ref([])
|
||||||
const topIcon = reactive({
|
const topIcon = reactive({
|
||||||
leftColor: '#ffffff',
|
leftColor: '#ffffff',
|
||||||
rightColor: '#ffffff'
|
rightColor: '#ffffff'
|
||||||
})
|
})
|
||||||
|
const formData = reactive({
|
||||||
|
type: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 15
|
||||||
|
})
|
||||||
|
const contentData = ref('')
|
||||||
|
const inputId = ref('')
|
||||||
|
|
||||||
onPageScroll(e => {
|
const onScroll = e => {
|
||||||
cbNavBar.value?.updateScroll(e.scrollTop)
|
cbNavBar.value?.updateScroll(e.detail.scrollTop)
|
||||||
if (e.scrollTop > MAX_SCROLL - 220) {
|
if (e.detail.scrollTop > MAX_SCROLL - 220) {
|
||||||
topIcon.leftColor = '#000'
|
topIcon.leftColor = '#000'
|
||||||
topIcon.rightColor = '#000'
|
topIcon.rightColor = '#000'
|
||||||
} else {
|
} else {
|
||||||
topIcon.leftColor = '#ffffff'
|
topIcon.leftColor = '#ffffff'
|
||||||
topIcon.rightColor = '#ffffff'
|
topIcon.rightColor = '#ffffff'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getData = async (pageNum, pageSize) => {
|
||||||
|
try {
|
||||||
|
const res = await getUserMomentsList({
|
||||||
|
pageNum,
|
||||||
|
pageSize
|
||||||
|
})
|
||||||
|
paging.value.complete(res.rows)
|
||||||
|
} catch (error) {
|
||||||
|
paging.value.complete(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onLike = async item => {
|
||||||
|
await likeUserMoments(item.id)
|
||||||
|
// item.likeCount += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 点击查看大图 */
|
||||||
|
const onImage = current => {
|
||||||
|
uni.previewImage({
|
||||||
|
urls: [current], // 图片路径数组(本地或网络)
|
||||||
|
current: current // 当前显示的图片(可选,默认为第一张)
|
||||||
|
})
|
||||||
|
closeComment()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭评论框 */
|
||||||
|
const closeComment = () => {
|
||||||
|
contentData.value = ''
|
||||||
|
inputId.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 发布评论 */
|
||||||
|
const onComment = async item => {
|
||||||
|
console.log('发布评论')
|
||||||
|
const data = {
|
||||||
|
content: contentData.value,
|
||||||
|
id: item.id
|
||||||
|
}
|
||||||
|
const res = await addUserMomentsComment(data)
|
||||||
|
closeComment()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除动态 */
|
||||||
|
const onDeleteItem = async id => {
|
||||||
|
const res = await showDialog('提示', '确定要删除吗?')
|
||||||
|
if (!res) return
|
||||||
|
await deleteUserMoments(id)
|
||||||
|
await showToast('删除成功', 'success')
|
||||||
|
dataList.value = dataList.value.filter(item => item.id !== id)
|
||||||
|
}
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
getData(1, formData.pageSize)
|
||||||
})
|
})
|
||||||
|
onLoad(async () => {})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="dynamic">
|
<z-paging
|
||||||
<nav-bar
|
ref="paging"
|
||||||
ref="cbNavBar"
|
v-model="dataList"
|
||||||
isTopBg
|
:default-page-no="formData.pageNum"
|
||||||
target-color="#f9f9f9"
|
:default-page-size="formData.pageSize"
|
||||||
:max-scroll="MAX_SCROLL"
|
safe-area-inset-bottom
|
||||||
>
|
use-safe-area-placeholder
|
||||||
<template #back>
|
:show-loading-more-no-more-view="false"
|
||||||
<uni-icons
|
@query="getData"
|
||||||
:color="topIcon.leftColor"
|
@scroll="onScroll"
|
||||||
type="left"
|
>
|
||||||
size="24"
|
<template #top>
|
||||||
></uni-icons>
|
<nav-bar
|
||||||
</template>
|
ref="cbNavBar"
|
||||||
<template #right>
|
isTopBg
|
||||||
<uni-icons
|
target-color="#f9f9f9"
|
||||||
:color="topIcon.rightColor"
|
:max-scroll="MAX_SCROLL"
|
||||||
type="camera"
|
>
|
||||||
size="24"
|
<template #back>
|
||||||
@click="navigateTo('/pages/discover/dynamic/release')"
|
<uni-icons
|
||||||
></uni-icons>
|
:color="topIcon.leftColor"
|
||||||
</template>
|
type="left"
|
||||||
</nav-bar>
|
size="24"
|
||||||
|
></uni-icons>
|
||||||
|
</template>
|
||||||
|
<template #right>
|
||||||
|
<uni-icons
|
||||||
|
:color="topIcon.rightColor"
|
||||||
|
type="camera"
|
||||||
|
size="24"
|
||||||
|
@click="navigateTo('/pages/discover/dynamic/release')"
|
||||||
|
></uni-icons>
|
||||||
|
</template>
|
||||||
|
</nav-bar>
|
||||||
|
</template>
|
||||||
<view class="top-bg-img">
|
<view class="top-bg-img">
|
||||||
<image
|
<image
|
||||||
src="https://wx4.sinaimg.cn/mw690/006i0nC8ly1hqugav3k6yj31o01o0aod.jpg"
|
:src="userInfo.avatar"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
class="img"
|
class="img"
|
||||||
|
@click="onImage(userInfo.avatar)"
|
||||||
></image>
|
></image>
|
||||||
<!-- 用户信息 -->
|
<!-- 用户信息 -->
|
||||||
<view class="user-info">
|
<view class="user-info">
|
||||||
<text>名字</text>
|
<text>{{ userInfo.userName }}</text>
|
||||||
<image
|
<image
|
||||||
src="https://img2.baidu.com/it/u=3408192385,656358498&fm=253&app=138&f=JPEG?w=760&h=760"
|
:src="userInfo.avatar"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
class="avatar"
|
class="avatar"
|
||||||
|
@click="onImage(userInfo.avatar)"
|
||||||
></image>
|
></image>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 动态列表 -->
|
<!-- 动态列表 -->
|
||||||
<view class="dynamic-list">
|
<view class="dynamic-list" @click="closeComment">
|
||||||
<view v-for="item in 4" class="list">
|
<view v-for="item in dataList" :key="item.id" class="list">
|
||||||
<image
|
<image
|
||||||
src="https://img1.baidu.com/it/u=2645961124,1296922095&fm=253&app=138&f=JPEG?w=800&h=1530"
|
src="https://img1.baidu.com/it/u=2645961124,1296922095&fm=253&app=138&f=JPEG?w=800&h=1530"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
@@ -74,7 +173,7 @@
|
|||||||
></image>
|
></image>
|
||||||
<view class="content">
|
<view class="content">
|
||||||
<text class="name">名字</text>
|
<text class="name">名字</text>
|
||||||
<text class="text">这是一条朋友圈的标题</text>
|
<text class="text">{{ item.content }}</text>
|
||||||
<view class="img-list">
|
<view class="img-list">
|
||||||
<image
|
<image
|
||||||
src="https://p4.itc.cn/images01/20220619/46660ed163164c14be90e605a73ee5e8.jpeg"
|
src="https://p4.itc.cn/images01/20220619/46660ed163164c14be90e605a73ee5e8.jpeg"
|
||||||
@@ -84,20 +183,44 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- 地址 -->
|
<!-- 地址 -->
|
||||||
<view class="address">
|
<view class="address">
|
||||||
<text>19分钟前</text>
|
<text>{{ formatRelativeTime(item.createTime) }}</text>
|
||||||
<text>重庆市渝北xxx寄街道</text>
|
<!-- <text>重庆市渝北xxx寄街道</text> -->
|
||||||
</view>
|
</view>
|
||||||
<!-- 点赞评论 -->
|
<!-- 点赞评论 -->
|
||||||
<view class="like-box">
|
<view class="like-box">
|
||||||
<view class="like">
|
<view class="like" @click.stop="onLike(item)">
|
||||||
<uni-icons
|
<uni-icons
|
||||||
type="hand-up"
|
type="hand-up"
|
||||||
size="20"
|
size="20"
|
||||||
color="#747474"
|
color="#747474"
|
||||||
></uni-icons>
|
></uni-icons>
|
||||||
<text>22</text>
|
<text v-if="item.likeCount > 0">{{ item.likeCount }}</text>
|
||||||
</view>
|
</view>
|
||||||
<uni-icons type="chat" size="20" color="#747474"></uni-icons>
|
<uni-icons
|
||||||
|
type="chat"
|
||||||
|
size="20"
|
||||||
|
color="#747474"
|
||||||
|
@click.stop="inputId = item.id"
|
||||||
|
></uni-icons>
|
||||||
|
|
||||||
|
<uni-icons
|
||||||
|
type="trash"
|
||||||
|
size="20"
|
||||||
|
color="#d95d5d"
|
||||||
|
style="margin-left: 86rpx"
|
||||||
|
@click.stop="onDeleteItem(item.id)"
|
||||||
|
></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view v-if="inputId === item.id" class="input-box">
|
||||||
|
<input
|
||||||
|
v-model="contentData"
|
||||||
|
focus
|
||||||
|
confirm-type="done"
|
||||||
|
placeholder="评论"
|
||||||
|
:placeholder-style="placeholderStyle"
|
||||||
|
@confirm.stop="onComment(item)"
|
||||||
|
/>
|
||||||
|
<button @click.stop="onComment(item)">发布</button>
|
||||||
</view>
|
</view>
|
||||||
<!-- 评论内容 -->
|
<!-- 评论内容 -->
|
||||||
<view class="comment">
|
<view class="comment">
|
||||||
@@ -109,7 +232,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</z-paging>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
|
import { addUserMoments } from '@/api/my-index'
|
||||||
|
import { useUI } from '@/utils/use-ui'
|
||||||
|
import { navigateBack } from '@/utils/router'
|
||||||
|
|
||||||
const placeholderStyle = `font-family: PingFang SC, PingFang SC;
|
const placeholderStyle = `font-family: PingFang SC, PingFang SC;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -9,13 +12,31 @@ line-height: 40rpx;
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
text-transform: none;`
|
text-transform: none;`
|
||||||
|
|
||||||
|
const { showToast } = useUI()
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
txt: '',
|
txt: '',
|
||||||
listImg: []
|
listImg: []
|
||||||
})
|
})
|
||||||
|
|
||||||
const onUpData = () => {
|
const onUpData = async () => {
|
||||||
console.log(formData)
|
if (!formData.txt) {
|
||||||
|
showToast('请输入内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await addUserMoments({
|
||||||
|
content: formData.txt,
|
||||||
|
privacy: 0,
|
||||||
|
images: formData.listImg.map(v => {
|
||||||
|
return {
|
||||||
|
imageUrl: v
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
await showToast('发布成功', 'success')
|
||||||
|
|
||||||
|
navigateBack()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #999999;
|
color: #999999;
|
||||||
margin-right: 16rpx;
|
margin-right: 16rpx;
|
||||||
&:last-child {
|
&:nth-child(2) {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #0c587e;
|
color: #0c587e;
|
||||||
}
|
}
|
||||||
@@ -121,6 +121,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.input-box {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 14rpx;
|
||||||
|
border: 2rpx solid #19ac31;
|
||||||
|
padding: 12rpx 18rpx;
|
||||||
|
input {
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
width: 120rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
background: #19ac31;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
&::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 38rpx;
|
height: 38rpx;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: -2rpx;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-radius: 32rpx 32rpx 0rpx 0rpx;
|
border-radius: 32rpx 32rpx 0rpx 0rpx;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
:default-page-size="formData.pageSize"
|
:default-page-size="formData.pageSize"
|
||||||
safe-area-inset-bottom
|
safe-area-inset-bottom
|
||||||
use-safe-area-placeholder
|
use-safe-area-placeholder
|
||||||
|
:show-loading-more-no-more-view="false"
|
||||||
@query="getData"
|
@query="getData"
|
||||||
>
|
>
|
||||||
<template #top>
|
<template #top>
|
||||||
|
|||||||
@@ -6,10 +6,13 @@
|
|||||||
import { navigateTo } from '@/utils/router'
|
import { navigateTo } from '@/utils/router'
|
||||||
|
|
||||||
const list = ref([])
|
const list = ref([])
|
||||||
|
const loading = ref(true)
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
|
loading.value = true
|
||||||
const res = await getMyGroupList()
|
const res = await getMyGroupList()
|
||||||
list.value = res.data
|
list.value = res.data
|
||||||
console.log(res.data)
|
console.log(res.data)
|
||||||
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const onGo = id => {
|
const onGo = id => {
|
||||||
@@ -22,7 +25,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="shop-together">
|
<view v-if="!loading" class="shop-together">
|
||||||
|
<cb-empty v-if="!loading && list.length === 0"></cb-empty>
|
||||||
<view
|
<view
|
||||||
v-for="item in list"
|
v-for="item in list"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
|||||||
@@ -23,3 +23,60 @@ export const formatMonthDay = date => {
|
|||||||
|
|
||||||
return `${month}.${day}`
|
return `${month}.${day}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将时间字符串转换为相对时间描述
|
||||||
|
* @param {string} timeStr - 后端返回的时间字符串,如 '2026-01-12 22:51:54'
|
||||||
|
* @returns {string} 相对时间描述,如 '刚刚'、'3分钟前'、'昨天' 等
|
||||||
|
*/
|
||||||
|
export function formatRelativeTime(timeStr) {
|
||||||
|
// 兼容 iOS 不支持 '-' 分隔的日期格式,需转为标准 ISO 格式
|
||||||
|
const normalizedTimeStr = timeStr.replace(/-/g, '/')
|
||||||
|
|
||||||
|
const serverTime = new Date(normalizedTimeStr)
|
||||||
|
const now = new Date()
|
||||||
|
|
||||||
|
// 时间差(毫秒)
|
||||||
|
const diffMs = now - serverTime
|
||||||
|
|
||||||
|
// 如果时间在未来,直接返回原始时间或处理异常
|
||||||
|
if (diffMs < 0) {
|
||||||
|
return timeStr // 或者 return '未来时间';
|
||||||
|
}
|
||||||
|
|
||||||
|
const seconds = Math.floor(diffMs / 1000)
|
||||||
|
const minutes = Math.floor(seconds / 60)
|
||||||
|
const hours = Math.floor(minutes / 60)
|
||||||
|
const days = Math.floor(hours / 24)
|
||||||
|
|
||||||
|
// 判断是否是今天
|
||||||
|
const isSameDay = (date1, date2) => {
|
||||||
|
return (
|
||||||
|
date1.getFullYear() === date2.getFullYear() &&
|
||||||
|
date1.getMonth() === date2.getMonth() &&
|
||||||
|
date1.getDate() === date2.getDate()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否是昨天
|
||||||
|
const isYesterday = (date1, date2) => {
|
||||||
|
const yesterday = new Date(date2)
|
||||||
|
yesterday.setDate(date2.getDate() - 1)
|
||||||
|
return isSameDay(date1, yesterday)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seconds < 60) {
|
||||||
|
return '刚刚'
|
||||||
|
} else if (minutes < 60) {
|
||||||
|
return `${minutes}分钟前`
|
||||||
|
} else if (hours < 24 && isSameDay(serverTime, now)) {
|
||||||
|
return `${hours}小时前`
|
||||||
|
} else if (isYesterday(serverTime, now)) {
|
||||||
|
return '昨天'
|
||||||
|
} else if (days < 7) {
|
||||||
|
return `${days}天前`
|
||||||
|
} else {
|
||||||
|
// 超过一周,返回原始日期(可选格式化为 YYYY-MM-DD)
|
||||||
|
return timeStr.split(' ')[0] // 或使用更美观的格式
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user