新增标签胶囊、摘要文字、阅读区、查看更多、标题字号等多组CSS变量,将组件样式控制权交给外部配置,同时将默认值对齐后端design.js的nestedDefaults。重构列表项间距、分割线颜色等硬编码样式为变量驱动,统一组件样式管理逻辑。
693 lines
22 KiB
Vue
693 lines
22 KiB
Vue
<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]" :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]" :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 || {};
|
||
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 || {};
|
||
// 摘要文字色(默认值与后端 design.js nestedDefaults 对齐)
|
||
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 色
|
||
'--article-tag-bg-color': tag.bgColor || '#f0f9ff',
|
||
'--article-tag-text-color': tag.textColor || '#1e6bb8',
|
||
'--article-tag-border-color': tag.borderColor || '#b3d8ff',
|
||
// 摘要文字色
|
||
'--article-abstract-color': abs.textColor || '#888888',
|
||
// 阅读区文字色
|
||
'--article-meta-color': rw.color || '#999ca7',
|
||
// 阅读区图标大小(rpx)
|
||
'--article-meta-icon-size': r(rw.iconSize, 24) + 'rpx',
|
||
// 查看更多字号(rpx)
|
||
'--article-view-more-font-size': (typeof vm.fontSize === 'number' ? vm.fontSize : r(vm.fontSize, 26)) + 'rpx',
|
||
// 查看更多图标大小(rpx)
|
||
'--article-view-more-icon-size': r(vm.iconSize, 24) + 'rpx',
|
||
// 列表项间距(rpx)
|
||
'--article-item-gap': r(v.itemGap, 24) + 'rpx',
|
||
// 分割线颜色(主要用于 style-4)
|
||
'--article-divider-color': v.dividerColor || '#eeeeee',
|
||
// 标题字号 4 风格独立
|
||
'--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 }, 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 }, 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 },
|
||
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 算单行高度 × 实际显示行数 + 行间距
|
||
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 = 200; break; // 图160 + padding 上下20*2 = 200
|
||
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 + 30; // N 行 + 30rpx 行间距总空间
|
||
}
|
||
// 注: :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-abstract-color: #888888;
|
||
--article-meta-color: #999ca7;
|
||
--article-meta-icon-size: 24rpx;
|
||
--article-view-more-font-size: 26rpx;
|
||
--article-view-more-icon-size: 24rpx;
|
||
--article-item-gap: 24rpx;
|
||
--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, 24rpx);
|
||
/* 背景 / 圆角通过 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;
|
||
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;
|
||
font-size: $font-size-tag;
|
||
|
||
.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);
|
||
font-size: 24rpx;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ========== 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);
|
||
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;
|
||
}
|
||
|
||
font-size: 20rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ========== 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> |