文件
fenleixinxi/uniapp/components/fc-post-card/fc-post-card.vue

369 行
8.9 KiB
Vue

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<template>
<view
class="fc-post-card"
:class="{ 'fc-post-card--compact': compact }"
@click="$emit('click', post)"
>
<!-- 缩略图:有真实实拍图展示实拍大图;无图时展示分类图标微名片(分类名统一由下方分类标签承载,避免重复) -->
<view class="fc-post-card__thumb" :class="{ 'fc-post-card__thumb--badge': !thumbUrl }">
<image v-if="thumbUrl" class="fc-post-card__thumb-img" :src="thumbUrl" mode="aspectFill" />
<view v-else class="fc-post-card__deco-badge">
<view class="fc-post-card__deco-halo">
<image class="fc-post-card__deco-icon" :src="categoryIconUrl(post.categoryId)" mode="aspectFit" />
</view>
</view>
</view>
<!-- 内容主体 -->
<view class="fc-post-card__body">
<view class="fc-post-card__title fc-ellipsis-2">{{ post.title }}</view>
<view v-if="post.isTop || post.categoryName || (post.tags && post.tags.length)" class="fc-post-card__tags">
<text v-if="post.isTop" class="fc-tag fc-post-card__tag fc-post-card__tag--top">置顶</text>
<!-- 分类标签:列表卡片固定展示,图标 + 分类名 -->
<view v-if="post.categoryName" class="fc-tag fc-post-card__tag fc-post-card__tag--cat">
<image class="fc-post-card__tag-icon" :src="categoryIconUrl(post.categoryId)" mode="aspectFit" />
<text>{{ post.categoryName }}</text>
</view>
<text v-for="t in (post.tags || []).slice(0, 2)" :key="t" class="fc-tag fc-post-card__tag">{{ t }}</text>
</view>
<view class="fc-post-card__meta">
<text class="fc-post-card__area">{{ post.area || '同城' }}</text>
<text v-if="distanceText" class="fc-post-card__dot">·</text>
<text v-if="distanceText" class="fc-post-card__distance">{{ distanceText }}</text>
<text class="fc-post-card__dot">·</text>
<text class="fc-post-card__time">{{ timeText }}</text>
</view>
<view class="fc-post-card__bottom">
<view class="fc-price fc-price--card">
<template v-if="post.price > 0">
<text class="fc-price__symbol">¥</text>
<text class="fc-post-card__amount">{{ post.price }}</text>
<text class="fc-post-card__unit">{{ post.priceUnit || '元' }}</text>
</template>
<text v-else class="fc-post-card__neg">面议</text>
</view>
<view class="fc-post-card__stats">
<view class="fc-post-card__stat">
<image class="fc-post-card__stat-icon" :src="iconUrl('eye')" mode="aspectFit" />
<text class="fc-post-card__stat-num">{{ post.views || 0 }}</text>
</view>
<view class="fc-post-card__stat">
<image class="fc-post-card__stat-icon" :src="iconUrl('comment-gray')" mode="aspectFit" />
<text class="fc-post-card__stat-num">{{ post.commentCount || 0 }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { iconUrl, categoryIconUrl } from '@/common/store/ui.js'
import { fromNow, formatKm, assetUrl } from '@/common/utils/format.js'
export default {
name: 'FcPostCard',
props: {
/** 帖子数据模型 */
post: { type: Object, required: true },
/** 是否开启紧凑模式(适用于双联分栏、弹层等窄容器环境) */
compact: { type: Boolean, default: false },
},
computed: {
/** 相对发布时间描述 */
timeText() {
return fromNow(this.post.createdAt)
},
/** 距离文案(如「1.2km」);契约下发的 distance 为 km,信息无坐标时为 null 不展示 */
distanceText() {
return this.post.distance == null ? '' : formatKm(this.post.distance)
},
/**
* 帖子封面图:优先使用实际上传封面或第一张实拍图,无图时回退为空由图标微卡片兜底
*/
thumbUrl() {
if (this.post.cover) {
return assetUrl(this.post.cover)
}
if (this.post.media && Array.isArray(this.post.media) && this.post.media.length) {
const first = this.post.media[0]
if (typeof first === 'string' && !first.match(/\.(mp4|mov|avi)$/i)) {
return assetUrl(first)
}
}
return ''
},
},
methods: {
/** 暴露图标地址解析工具 */
iconUrl,
/** 暴露分类图标解析工具 */
categoryIconUrl,
},
}
</script>
<style lang="scss" scoped>
.fc-post-card {
display: flex;
padding: 24rpx;
margin: 0 24rpx 20rpx;
background: #ffffff;
border-radius: $fc-radius-lg;
box-shadow: 0 4rpx 16rpx rgba(4, 48, 30, 0.04), 0 10rpx 28rpx rgba(4, 48, 30, 0.03);
border: 1rpx solid rgba(4, 48, 30, 0.06);
cursor: pointer;
box-sizing: border-box;
transition: all 0.2s ease;
&:hover {
box-shadow: 0 8rpx 24rpx rgba(4, 48, 30, 0.1);
}
&:active {
transform: scale(0.985);
}
}
/* 缩略图容器 */
.fc-post-card__thumb {
width: 180rpx;
height: 180rpx;
flex: 0 0 180rpx;
margin-right: 22rpx;
border-radius: 16rpx;
overflow: hidden;
background: #f1f5f9;
display: flex;
align-items: center;
justify-content: center;
position: relative;
/* 🌟 无实拍图时的分类主题徽章微卡片,精致现代 */
&--badge {
background: linear-gradient(145deg, #eefbf4 0%, #e1f7ec 100%);
border: 1rpx solid rgba(0, 224, 112, 0.18);
}
}
.fc-post-card__thumb-img {
width: 100%;
height: 100%;
}
.fc-post-card__deco-badge {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.fc-post-card__deco-halo {
width: 84rpx;
height: 84rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.88);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 14rpx rgba(4, 48, 30, 0.08);
}
.fc-post-card__deco-icon {
width: 50rpx;
height: 50rpx;
}
/* 内容主体 */
.fc-post-card__body {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
justify-content: space-between;
}
.fc-post-card__title {
font-size: 30rpx;
font-weight: 600;
color: $fc-text;
line-height: 1.4;
margin-bottom: 6rpx;
word-break: break-all;
}
.fc-post-card__tags {
display: flex;
flex-wrap: nowrap;
overflow: hidden;
gap: 8rpx;
margin-bottom: 8rpx;
}
.fc-post-card__tag {
margin: 0;
font-size: 20rpx;
padding: 2rpx 10rpx;
white-space: nowrap;
flex-shrink: 0;
}
/* 分类标签:浅绿底 + 墨绿字(亮绿背景禁用白字),与全局 fc-tag 规范一致 */
.fc-post-card__tag--cat {
gap: 6rpx;
padding: 2rpx 14rpx 2rpx 10rpx;
font-weight: 600;
}
.fc-post-card__tag-icon {
width: 22rpx;
height: 22rpx;
flex-shrink: 0;
}
.fc-post-card__tag--top {
background: linear-gradient(135deg, #00d26a 0%, #00bd5f 100%);
color: #ffffff;
font-weight: 700;
border-radius: 6rpx;
padding: 2rpx 10rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 210, 106, 0.35);
}
.fc-post-card__meta {
display: flex;
align-items: center;
font-size: 22rpx;
color: $fc-text-placeholder;
margin-bottom: 8rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.fc-post-card__dot {
margin: 0 8rpx;
}
.fc-post-card__distance {
color: $fc-primary-text;
font-weight: 600;
}
.fc-post-card__bottom {
display: flex;
align-items: flex-end;
justify-content: space-between;
padding-top: 4rpx;
gap: 12rpx;
}
.fc-price--card {
display: flex;
align-items: baseline;
white-space: nowrap !important;
flex-shrink: 0;
}
.fc-post-card__amount {
font-size: 36rpx;
font-weight: 700;
line-height: 1;
white-space: nowrap !important;
}
.fc-post-card__neg {
font-size: 28rpx;
font-weight: 700;
color: $fc-price;
white-space: nowrap !important;
line-height: 1;
}
.fc-post-card__unit {
margin-left: 4rpx;
font-size: 22rpx;
color: $fc-text-secondary;
font-weight: 400;
white-space: nowrap !important;
}
.fc-post-card__stats {
display: flex;
align-items: center;
flex-shrink: 0;
white-space: nowrap;
}
.fc-post-card__stat {
display: flex;
align-items: center;
margin-left: 16rpx;
}
.fc-post-card__stat-icon {
width: 26rpx;
height: 26rpx;
flex-shrink: 0;
opacity: 0.65;
}
.fc-post-card__stat-num {
margin-left: 6rpx;
font-size: 22rpx;
color: $fc-text-placeholder;
white-space: nowrap;
}
/* 🌟 紧凑模式:双联分栏专属适配 */
.fc-post-card--compact {
margin: 0 0 16rpx 0;
padding: 16rpx 14rpx;
border-radius: 16rpx;
box-shadow: 0 2rpx 10rpx rgba(4, 48, 30, 0.05);
.fc-post-card__thumb {
width: 136rpx;
height: 136rpx;
flex: 0 0 136rpx;
margin-right: 14rpx;
border-radius: 12rpx;
}
.fc-post-card__deco-halo {
width: 64rpx;
height: 64rpx;
}
.fc-post-card__deco-icon {
width: 38rpx;
height: 38rpx;
}
.fc-post-card__title {
font-size: 27rpx;
line-height: 1.35;
margin-bottom: 4rpx;
max-height: 72rpx;
}
.fc-post-card__tags {
margin-bottom: 4rpx;
gap: 6rpx;
}
.fc-post-card__tag {
font-size: 18rpx;
padding: 2rpx 8rpx;
}
.fc-post-card__tag--cat {
padding: 2rpx 12rpx 2rpx 8rpx;
}
.fc-post-card__tag-icon {
width: 20rpx;
height: 20rpx;
}
.fc-post-card__meta {
font-size: 20rpx;
margin-bottom: 4rpx;
}
.fc-post-card__bottom {
padding-top: 2rpx;
gap: 8rpx;
}
.fc-post-card__amount {
font-size: 32rpx;
}
.fc-post-card__neg {
font-size: 26rpx;
}
.fc-post-card__stat {
margin-left: 10rpx;
}
.fc-post-card__stat-icon {
width: 22rpx;
height: 22rpx;
}
.fc-post-card__stat-num {
font-size: 20rpx;
}
}
</style>