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