Files
lucky_shop/components-diy/diy-article.vue
ZF sun f7f1b1191e fix(diy-article): 修复无图文章样式异常,新增字号样式配置
1. 为无图文章项添加no-image类适配样式,统一与有图项的视觉高度
2. 新增标签、摘要、阅读区的字号CSS变量支持,替换硬编码字号
3. 调整列表项间距默认值和swiper行间距,优化布局紧凑度
4. 补充默认字号配置项,兼容老数据缺失字段的情况
2026-06-15 10:03:26 +08:00

734 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<x-skeleton data-component-name="diy-article" type="list" :loading="loading" :configs="skeletonConfig">
<view class="article-wrap" :style="warpWrapStyle">
<!-- 注: :class 用内联数组,与 ns-newform 对齐;computed listClass/itemClass 已移除(避免数组 toString 用逗号 join 的问题) -->
<view :class="['list-wrap', value.style || 'style-1']">
<!-- 自动垂直滚动(默认开启,兼容老数据:无 scrollMode/swiperConfig 字段也走这里) -->
<swiper v-if="list.length > 0 && useScroll" class="auto-scroll-swiper"
:style="swiperWrapStyle"
:vertical="swiperConfig.vertical !== false"
:autoplay="swiperConfig.autoplay !== false"
:circular="swiperConfig.circular !== false"
:interval="swiperConfig.interval || 3000"
:duration="swiperConfig.duration || 500"
:display-multiple-items="safeDisplayMultipleItems">
<swiper-item v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)">
<view class="swiper-item-content">
<view :class="['item', value.ornament && value.ornament.type, !item.cover_img && value.style !== 'style-4' ? 'no-image' : '']" :style="itemStyle">
<!-- 封面图:无图时隐藏图位;style-4 纯文字始终不显示 -->
<view class="article-img" v-if="item.cover_img && value.style !== 'style-4'">
<image class="cover-img" :src="$util.img(item.cover_img)" mode="aspectFill"
@error="imgError(index)" show-menu-by-longpress></image>
</view>
<view class="info-wrap">
<text class="title">{{ item.article_title }}</text>
<text class="desc"
v-if="abstractStyle.support && abstractStyle.control && item.article_abstract">
{{ item.article_abstract }}
</text>
<view class="tag-list"
v-if="tagStyle.support && tagStyle.control && item.tag_names">
<text class="tag" v-for="(tn, ti) in tagArr(item.tag_names)" :key="ti">{{ tn }}</text>
</view>
<view class="read-wrap">
<block v-if="item.category_name">
<text class="category-icon"></text>
<text class="category-name">{{ item.category_name }}</text>
</block>
<view class="num" v-if="readNumStyle.support && readNumStyle.control">
<text class="iconfont iconchakan"></text>
<text>{{ item.read_num || 0 }}</text>
</view>
<view class="num" v-if="dianzanStyle.support && dianzanStyle.control">
<text class="iconfont icondianzan"></text>
<text>{{ item.dianzan_num || 0 }}</text>
</view>
<text class="date">{{ $util.timeStampTurnTime(item.create_time, 'date') }}</text>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
<!-- 非滚动模式(scrollMode==='none')时的普通列表 -->
<template v-if="!useScroll">
<view v-for="(item, index) in list" :key="index" :class="['item', value.ornament && value.ornament.type, !item.cover_img && value.style !== 'style-4' ? 'no-image' : '']" :style="itemStyle"
@tap.stop="toDetail(item)">
<view class="article-img" v-if="item.cover_img && value.style !== 'style-4'">
<image class="cover-img" :src="$util.img(item.cover_img)" mode="aspectFill"
@error="imgError(index)" show-menu-by-longpress></image>
</view>
<view class="info-wrap">
<text class="title">{{ item.article_title }}</text>
<text class="desc"
v-if="abstractStyle.support && abstractStyle.control && item.article_abstract">
{{ item.article_abstract }}
</text>
<view class="tag-list"
v-if="tagStyle.support && tagStyle.control && item.tag_names">
<text class="tag" v-for="(tn, ti) in tagArr(item.tag_names)" :key="ti">{{ tn }}</text>
</view>
<view class="read-wrap">
<block v-if="item.category_name">
<text class="category-icon"></text>
<text class="category-name">{{ item.category_name }}</text>
</block>
<view class="num" v-if="readNumStyle.support && readNumStyle.control">
<text class="iconfont iconchakan"></text>
<text>{{ item.read_num || 0 }}</text>
</view>
<view class="num" v-if="dianzanStyle.support && dianzanStyle.control">
<text class="iconfont icondianzan"></text>
<text>{{ item.dianzan_num || 0 }}</text>
</view>
<text class="date">{{ $util.timeStampTurnTime(item.create_time, 'date') }}</text>
</view>
</view>
</view>
<!-- 查看更多(仅非滚动模式显示) -->
<view class="view-more" v-if="viewMore.support && viewMore.control && list.length > 0"
:style="viewMoreStyle" @tap.stop="toListPage">
<text>{{ viewMore.text || '查看更多' }}</text>
<text class="iconfont icondiy-right-arrow"></text>
</view>
</template>
<view v-if="list.length === 0 && !loading" class="empty-placeholder">
<text class="empty-text">暂无相关文章</text>
</view>
</view>
</view>
</x-skeleton>
</template>
<script>
// 依赖 ./minx.js 提供 componentRefresh、$util 等装修通用能力
import DiyMinx from './minx.js';
// 文章
export default {
name: 'diy-article',
props: {
value: {
type: Object,
default: () => ({})
}
},
mixins: [DiyMinx],
data() {
return {
list: [],
loading: true,
skeletonConfig: {
gridRows: 1,
gridRowsGap: '40rpx',
headWidth: '160rpx',
headHeight: '160rpx',
textRows: 2
}
};
},
created() {
this.getList();
},
watch: {
// 组件刷新监听
componentRefresh() {
this.getList();
}
},
computed: {
// ========== 容器样式(与 ns-newform 对齐: $util.objToStyle + CSS 变量) ==========
// 根容器:背景 + CSS 变量(向下传递,H5/MP/Harmony 均支持继承)
// 注: 用 objToStyle 自动过滤 undefined/空值,避免输出 background-color:; 这类脏 CSS
warpWrapStyle() {
const v = this.value || {};
// ========== 单位约定 ==========
// 数据里所有「字号/图标/间距/圆角」类数字字段统一存为 rpx(uni-app 响应式像素);
// 老数据若是字符串("24rpx")或缺字段,经 r() 归一化为数字,再拼 'rpx' 后缀;
// 老数据若用 px 存(早期 DIY 圆角),需在设计面板里手动改为 rpx 量级再保存(否则会被 r() 截断)。
// 后端预览(PC 浏览器)做 /2 折算,把 rpx 转 px。
const r = (val, fb) => this._num(val, fb);
const isRootRound = v.componentAngle === 'round';
const isItemRound = v.elementAngle === 'round';
// 标签胶囊 3 色 + 字号(默认值与后端 design.js nestedDefaults 对齐)
const tag = v.tagStyle || {};
// 摘要文字色 + 字号
const abs = v.abstractStyle || {};
// 查看更多(字号 + 图标大小)
const vm = v.viewMore || {};
// 阅读区(文字色 + 图标大小 + 文字大小)
const rw = v.readWrapStyle || {};
// 标题字号(每风格独立 1/2/3/4)
const tfs1 = r(v.titleFontSize1, 30);
const tfs2 = r(v.titleFontSize2, 30);
const tfs3 = r(v.titleFontSize3, 24);
const tfs4 = r(v.titleFontSize4, 28);
return this.$util.objToStyle({
'background-color': v.componentBgColor || 'transparent',
// CSS 变量(向下传递给所有子元素,通过 var() 读取)
'--article-component-bg-color': v.componentBgColor || 'transparent',
'--article-element-bg-color': v.elementBgColor || 'transparent',
'--article-top-around-radius': isRootRound ? `${r(v.topAroundRadius, 0) * 2}rpx` : '0rpx',
'--article-bottom-around-radius': isRootRound ? `${r(v.bottomAroundRadius, 0) * 2}rpx` : '0rpx',
'--article-top-element-around-radius': isItemRound ? `${r(v.topElementAroundRadius, 0) * 2}rpx` : '0rpx',
'--article-bottom-element-around-radius': isItemRound ? `${r(v.bottomElementAroundRadius, 0) * 2}rpx` : '0rpx',
// 标签胶囊 3 色 + 字号(rpx)
'--article-tag-bg-color': tag.bgColor || '#f0f9ff',
'--article-tag-text-color': tag.textColor || '#1e6bb8',
'--article-tag-border-color': tag.borderColor || '#b3d8ff',
'--article-tag-font-size': r(tag.fontSize, 22) + 'rpx',
// 摘要文字色 + 字号(rpx)
'--article-abstract-color': abs.textColor || '#888888',
'--article-abstract-font-size': r(abs.fontSize, 24) + 'rpx',
// 阅读区文字色 + 图标大小 + 文字大小(rpx)
'--article-meta-color': rw.color || '#999ca7',
'--article-meta-icon-size': r(rw.iconSize, 24) + 'rpx',
'--article-meta-font-size': r(rw.fontSize, 24) + 'rpx',
// 查看更多字号 + 图标大小(rpx,统一走 r() 兜底,不再 typeof 区分)
'--article-view-more-font-size': r(vm.fontSize, 26) + 'rpx',
'--article-view-more-icon-size': r(vm.iconSize, 24) + 'rpx',
// 列表项间距(rpx)
'--article-item-gap': r(v.itemGap, 16) + 'rpx',
// 分割线颜色(主要用于 style-4)
'--article-divider-color': v.dividerColor || '#eeeeee',
// 标题字号 4 风格独立(rpx)
'--article-title-fs-1': tfs1 + 'rpx',
'--article-title-fs-2': tfs2 + 'rpx',
'--article-title-fs-3': tfs3 + 'rpx',
'--article-title-fs-4': tfs4 + 'rpx'
});
},
// 单项容器样式:背景 + 装饰(box-shadow / border)
// 注: 单项的 border-radius 由 CSS 变量向下传递(无需重复声明)
itemStyle() {
const v = this.value || {};
const ornament = v.ornament || {};
const style = {
'background-color': v.elementBgColor || 'transparent'
};
if (ornament.type === 'shadow') {
style['box-shadow'] = `0 0 10rpx ${ornament.color || 'rgba(0, 0, 0, 0.1)'}`;
}
if (ornament.type === 'stroke') {
style['border'] = `2rpx solid ${ornament.color || 'transparent'}`;
}
return this.$util.objToStyle(style);
},
// swiper 容器高度(由 swiperHeight 计算属性驱动)
swiperWrapStyle() {
return this.$util.objToStyle({
height: `${this.swiperHeight}rpx`
});
},
// 查看更多样式(颜色由 viewMore.color 控制)
viewMoreStyle() {
return this.$util.objToStyle({
color: (this.viewMore && this.viewMore.color) || '#999999'
});
},
// ========== 字段显示配置(带默认值,避免老数据缺字段时报错) ==========
abstractStyle() {
return Object.assign({ support: true, control: true, fontSize: 24 }, this.value.abstractStyle || {});
},
readNumStyle() {
return Object.assign({ support: true, control: true }, this.value.readNumStyle || {});
},
dianzanStyle() {
return Object.assign({ support: true, control: false }, this.value.dianzanStyle || {});
},
tagStyle() {
return Object.assign({ support: true, control: true, fontSize: 22 }, this.value.tagStyle || {});
},
viewMore() {
return Object.assign({ support: true, control: false, text: '查看更多', color: '#999999', iconSize: 24 },
this.value.viewMore || {});
},
// 阅读区样式(文字色 + 图标大小 + 文字大小)
readWrapStyle() {
return Object.assign({ color: '#999ca7', iconSize: 24, fontSize: 24 },
this.value.readWrapStyle || {});
},
// ========== 滚动配置(同时兼容老数据 swiperConfig 和新字段 scrollMode) ==========
// 老数据 swiperConfig 字段:{ vertical, autoplay, circular, interval, duration, displayMultipleItems, swiperHeight }
// 新数据 scrollMode 字段:'none' | 'vertical'(默认 vertical,与老行为一致)
swiperConfig() {
return this.value.swiperConfig || {};
},
scrollMode() {
return this.value.scrollMode || 'vertical';
},
// 是否使用滚动模式
useScroll() {
return this.scrollMode !== 'none' && this.list.length > 1;
},
// 安全的 display-multiple-items(确保不大于实际 list 长度,避免 swiper 报错)
safeDisplayMultipleItems() {
var displayCount = this.swiperConfig.displayMultipleItems || 3;
var actualCount = this.list.length;
return actualCount > 0 ? Math.min(displayCount, actualCount) : 1;
},
// swiper 高度(rpx)优先级:
// 1. 老/新数据显式设置了 swiperConfig.swiperHeight(>0) → 配置值
// 2. 否则按 style 算单行高度 × 显示行数 + 行间距
// 注:风格1 perRow=210,正好容纳:
// 有图 item 200(图160+info 160 被 stretch+padding40)
// 无图 item ~200(标题/摘要单行截断+space-between 上下分布+padding40)
// swiper 整体 = 210*3+10 = 640rpx,行间紧凑,有图无图视觉高度统一。
swiperHeight() {
// 1. 显式设置了 swiperHeight (>0)
var configHeight = parseInt(this.swiperConfig.swiperHeight) || 0;
if (configHeight > 0) return configHeight;
// 2. 自动算:每行高度 × 显示行数 + 间距
// 注:swiper 内 item 已去掉 margin-bottom,swiper-item 高度 = perRow
var perRow;
switch (this.value.style) {
case 'style-1': perRow = 210; break; // 容纳有图200/无图200+10rpx 缓冲
case 'style-2': perRow = 540; break; // 图320 + info 220
case 'style-3': perRow = 300; break; // 图200 + info 100
case 'style-4': perRow = 120; break; // 纯文字
default: perRow = 200; break;
}
return perRow * this.safeDisplayMultipleItems + 10; // N 行 + 10rpx 行间距总空间(更紧凑)
}
// 注: :class 一律在模板里用 :class="['static', dynamic]" 内联数组写法
// 不再用 listClass/itemClass computed (computed 返回 array 时,uni-app WXML 编译
// 输出 {{[listClass]}},运行时 toString 用逗号 join,产生 "list-wrap,style-1" 错误 class)
},
methods: {
// 数字保底:与 diy-form._num 对齐(undefined/空串/非数字时返回兜底值)
_num(v, fb) {
if (v === '' || v === null || v === undefined) return fb;
var n = parseInt(v);
return isNaN(n) ? fb : n;
},
// 把逗号分隔的 tag_names 转为数组(过滤空)
tagArr(str) {
return (str || '').split(',').filter(Boolean);
},
getList() {
this.loading = true;
var data = {
num: this.value.count || 8,
sort_way: this.value.sortWay || 'latest'
};
var sources = this.value.sources || 'initial';
if (sources === 'diy') {
data.num = 0;
data.article_id_arr = (this.value.articleIds || []).toString();
} else if (sources === 'category' && this.value.categoryId) {
data.category_id = this.value.categoryId;
} else if (sources === 'tag' && this.value.tagId) {
data.tag_id = this.value.tagId;
}
this.$api.sendRequest({
url: '/api/article/lists',
data: data,
success: res => {
this.list = (res.code == 0 && res.data) ? res.data : [];
this.loading = false;
},
fail: () => {
this.list = [];
this.loading = false;
}
});
},
toDetail(item) {
this.$util.redirectTo('/pages_tool/article/detail', {
article_id: item.article_id
});
},
// "查看更多" 跳列表页(若按分类/按标签则带筛选参数)
toListPage() {
var query = {};
var sources = this.value.sources || 'initial';
if (sources === 'category' && this.value.categoryId) {
query.category_id = this.value.categoryId;
} else if (sources === 'tag' && this.value.tagId) {
query.tag_id = this.value.tagId;
}
this.$util.redirectTo('/pages_tool/article/list', query);
},
imgError(index) {
// 图加载失败 → 清空 cover_img 触发 v-if 隐藏图位(无图就隐藏策略)
if (!this.list[index]) return;
this.$set(this.list, index, {
...this.list[index],
cover_img: ''
});
}
}
};
</script>
<style lang="scss">
/* 与 ns-newform 对齐: 在 .article-wrap 上通过内联 style 写入 CSS 变量,
子元素通过 var() 读取。CSS 变量在 H5 / 微信小程序 WXSS / 鸿蒙元服务均支持继承。 */
.article-wrap {
/* CSS 变量默认值兜底(老数据 / 静态预览场景下用以下值;默认值与后端 design.js nestedDefaults 对齐) */
--article-component-bg-color: transparent;
--article-element-bg-color: transparent;
--article-top-around-radius: 0rpx;
--article-bottom-around-radius: 0rpx;
--article-top-element-around-radius: 0rpx;
--article-bottom-element-around-radius: 0rpx;
--article-tag-bg-color: #f0f9ff;
--article-tag-text-color: #1e6bb8;
--article-tag-border-color: #b3d8ff;
--article-tag-font-size: 22rpx;
--article-abstract-color: #888888;
--article-abstract-font-size: 24rpx;
--article-meta-color: #999ca7;
--article-meta-icon-size: 24rpx;
--article-meta-font-size: 24rpx;
--article-view-more-font-size: 26rpx;
--article-view-more-icon-size: 24rpx;
--article-item-gap: 16rpx;
--article-divider-color: #eeeeee;
--article-title-fs-1: 30rpx;
--article-title-fs-2: 30rpx;
--article-title-fs-3: 24rpx;
--article-title-fs-4: 28rpx;
/* 根容器圆角:由 --article-top/bottom-around-radius 驱动 */
border-top-left-radius: var(--article-top-around-radius, 0);
border-top-right-radius: var(--article-top-around-radius, 0);
border-bottom-left-radius: var(--article-bottom-around-radius, 0);
border-bottom-right-radius: var(--article-bottom-around-radius, 0);
.list-wrap {
// 垂直滚动 swiper
.auto-scroll-swiper {
width: 100%;
// swiper-item-content 占满 swiper-item,不另加 padding(避免与 item padding 叠加导致裁切)
.swiper-item-content {
padding: 0;
box-sizing: border-box;
}
// swiper 内 item 去掉 margin-bottom,避免被 swiper-item 高度裁切;
// 行间距由 swiperHeight 多余空间制造(perRow × 3 + 30)
.item {
margin-bottom: 0 !important;
}
}
.item {
margin-bottom: var(--article-item-gap, 16rpx);
/* 背景 / 圆角通过 CSS 变量从 .article-wrap 继承(由 warpWrapStyle 注入) */
background-color: var(--article-element-bg-color, transparent);
border-top-left-radius: var(--article-top-element-around-radius, 0);
border-top-right-radius: var(--article-top-element-around-radius, 0);
border-bottom-left-radius: var(--article-bottom-element-around-radius, 0);
border-bottom-right-radius: var(--article-bottom-element-around-radius, 0);
&:last-of-type {
margin-bottom: 0;
}
}
.style-1 .item {
padding: 20rpx;
}
.tag-list {
margin-top: 10rpx;
line-height: 1;
.tag {
display: inline-block;
/* 3 色由 CSS 变量驱动(来自 warpWrapStyle 注入;老数据走默认值) */
background: var(--article-tag-bg-color, #f0f9ff);
color: var(--article-tag-text-color, #1e6bb8);
border: 2rpx solid var(--article-tag-border-color, #b3d8ff);
border-radius: 20rpx;
padding: 2rpx 14rpx;
margin: 0 8rpx 8rpx 0;
/* 标签字号由 CSS 变量驱动 */
font-size: var(--article-tag-font-size, 22rpx);
}
}
.read-wrap {
display: flex;
/* 文字色由 CSS 变量驱动(来自 warpWrapStyle 注入;老数据走默认值) */
color: var(--article-meta-color, #999ca7);
align-items: center;
margin-top: 10rpx;
line-height: 1;
flex-wrap: wrap;
/* 阅读区文字大小由 CSS 变量驱动(覆盖原 SCSS 变量 $font-size-tag) */
font-size: var(--article-meta-font-size, 24rpx);
.category-icon {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: $base-color;
margin-right: 10rpx;
}
.category-name {
margin-right: 10rpx;
}
.num {
display: inline-flex;
align-items: center;
margin-left: 16rpx;
.iconfont {
/* 图标大小由 CSS 变量驱动 */
font-size: var(--article-meta-icon-size, 24rpx);
margin-right: 6rpx;
}
}
.date {
margin-left: 16rpx;
}
}
.view-more {
text-align: center;
padding: 20rpx 0;
/* 字号由 CSS 变量驱动;颜色由 viewMoreStyle 注入(支持点击重置) */
font-size: var(--article-view-more-font-size, 26rpx);
.iconfont {
/* 图标大小由 CSS 变量驱动 */
font-size: var(--article-view-more-icon-size, 24rpx);
margin-left: 8rpx;
vertical-align: middle;
}
}
// ========== style-1: 横向卡片(图左文右) ==========
&.style-1 {
.item {
display: flex;
padding: 20rpx;
.article-img {
margin-right: 20rpx;
width: 160rpx;
height: 160rpx;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.cover-img {
width: 100%;
height: 100%;
}
}
.info-wrap {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
.title {
font-weight: bold;
margin-bottom: 10rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
/* 标题字号由 CSS 变量驱动(每风格独立) */
font-size: var(--article-title-fs-1, 30rpx);
line-height: 1.4;
}
.desc {
/* 颜色由 CSS 变量驱动(来自 warpWrapStyle 注入;老数据走默认值) */
color: var(--article-abstract-color, #888888);
/* 摘要字号由 CSS 变量驱动 */
font-size: var(--article-abstract-font-size, 24rpx);
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
}
// ========== 无图紧凑样式:让 item 高度跟有图一致 (~200rpx) ==========
// 背景:无图时 info-wrap 无 image 撑高,内容自然展开 ~232rpx + padding 40 = 272rpx,
// 远超有图时的 200rpx,导致 swiper-item 内行间空白巨大或被裁。
// 解决:无图时 info-wrap 显式 min-height: 160rpx(与有图时 image 撑高一致),
// 标题/摘要单行截断 + 去掉多余 margin,内容 ~124rpx 在 160rpx 容器内用
// space-between 上下分布(标题顶/摘要中/阅读底),与有图场景视觉高度统一。
.item.no-image {
.info-wrap {
min-height: 160rpx;
.title {
-webkit-line-clamp: 1;
margin-bottom: 0;
}
.desc {
-webkit-line-clamp: 1;
}
.tag-list {
margin-top: 0;
}
.read-wrap {
margin-top: 0;
}
}
}
}
// ========== style-2: 大图卡片(上图下文) ==========
&.style-2 {
.item {
padding: 0;
overflow: hidden;
.article-img {
width: 100%;
height: 320rpx;
overflow: hidden;
.cover-img {
width: 100%;
height: 100%;
}
}
.info-wrap {
padding: 20rpx;
.title {
font-weight: bold;
/* 标题字号由 CSS 变量驱动(每风格独立) */
font-size: var(--article-title-fs-2, 30rpx);
line-height: 1.4;
margin-bottom: 10rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.desc {
/* 颜色由 CSS 变量驱动(来自 warpWrapStyle 注入;老数据走默认值) */
color: var(--article-abstract-color, #888888);
/* 摘要字号由 CSS 变量驱动 */
font-size: var(--article-abstract-font-size, 24rpx);
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
margin-bottom: 10rpx;
}
}
}
}
// ========== style-3: 三列网格 ==========
&.style-3 {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
&::after {
content: '';
flex: auto;
}
.item {
width: 32%;
padding: 0;
overflow: hidden;
display: flex;
flex-direction: column;
.article-img {
width: 100%;
height: 200rpx;
overflow: hidden;
.cover-img {
width: 100%;
height: 100%;
}
}
.info-wrap {
padding: 10rpx 14rpx;
.title {
/* 标题字号由 CSS 变量驱动(每风格独立) */
font-size: var(--article-title-fs-3, 24rpx);
font-weight: bold;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.read-wrap {
.category-icon,
.category-name {
display: none;
}
// style-3 不再硬覆写字号,跟随 --article-meta-font-size(用户配多小就多小)
}
}
}
}
// ========== style-4: 纯文字列表(无封面) ==========
&.style-4 {
.item {
padding: 20rpx;
margin-bottom: 0;
/* 分割线颜色由 CSS 变量驱动 */
border-bottom: 2rpx dashed var(--article-divider-color, #eeeeee);
&:last-of-type {
border-bottom: 0;
}
.info-wrap {
.title {
/* 标题字号由 CSS 变量驱动(每风格独立) */
font-size: var(--article-title-fs-4, 28rpx);
font-weight: bold;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
// 空数据占位
.empty-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 0;
.empty-text {
color: #999;
font-size: 26rpx;
}
}
}
}
</style>