article
This commit is contained in:
1 parent
c6eb00b406
commit
1c91f62ff6
58 files changed
+3888
-69
No files matched your search
@@ -20,6 +20,7 @@ import { brandGlyph } from "@/common/brand";
|
||||
import { buildChunkFileName, uploadFile } from "@/common/http.interceptor";
|
||||
import { userTypeGlyph, userTypePillBg } from "@/common/userTypeIcons";
|
||||
import { useIdCardVerify, useThemeGradient, useThemeTokens } from "@/composables";
|
||||
import { useArticles } from "@/composables/useArticles";
|
||||
import { formatFileSize, formatFileTime, useFamilyTreeExchange } from "@/composables/useFamilyTreeExchange";
|
||||
import { usePersonPickConfig } from "@/composables/usePickerConfigs";
|
||||
import { usePolicyOptions } from "@/composables/usePolicyOptions";
|
||||
@@ -37,8 +38,9 @@ import familyPersonService from "@/service/master/familyPersonService";
|
||||
import familyTreeService from "@/service/master/familyTreeService";
|
||||
import dmsFileService from "@/service/master/dmsFileService";
|
||||
import { useLoginProfileStore } from "@/stores";
|
||||
import { pageUrl } from "@/router/routes";
|
||||
import { chooseFiles } from "@/utils/chooseFiles";
|
||||
import { absoluteFileUrl } from "@/utils/fileUrl";
|
||||
import { absoluteFileUrl, backupFileUrl } from "@/utils/fileUrl";
|
||||
|
||||
/** ready:组件挂载完成通知(时序口径见文件底部 onMounted 注释) */
|
||||
const emit = defineEmits<{ (e: "ready"): void }>();
|
||||
@@ -239,6 +241,60 @@ async function onRetry(): Promise<void> {
|
||||
await loadTrees();
|
||||
}
|
||||
|
||||
// ===== 有家文化传承(文章)卡片:与族谱列表并列,数据源见 composables/useArticles =====
|
||||
const articlesApi = useArticles();
|
||||
const articleKeyword = ref("");
|
||||
const showArticleSearch = ref(false);
|
||||
const articleCollapsed = ref(false);
|
||||
/** 首页「+」新建弹层可见(编辑层不另开页,直接底部弹出,同相册/群编辑) */
|
||||
const articleEditorVisible = ref(false);
|
||||
/** 首页卡只展示最新 5 条(列表已按置顶、创建时间排序) */
|
||||
const homeArticles = computed(() => articlesApi.list.value.slice(0, 5));
|
||||
|
||||
/** 无封面时的书卷字形:内联 SVG(同 treeGlyph 套路),白描卷轴 */
|
||||
const articleGlyph = `data:image/svg+xml,${encodeURIComponent([
|
||||
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48' fill='none' stroke='#ffffff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'>",
|
||||
"<path d='M10 12c0-2 1.6-3.6 3.6-3.6H30v30H13.6C11.6 38.4 10 36.8 10 34.8Z'/>",
|
||||
"<path d='M30 8.4h6.4c2 0 3.6 1.6 3.6 3.6v22.8c0 2-1.6 3.6-3.6 3.6H30'/>",
|
||||
"<path d='M15 16h10M15 22h10'/>",
|
||||
"</svg>",
|
||||
].join(""))}`;
|
||||
|
||||
async function loadArticles(): Promise<void> {
|
||||
await articlesApi.loadList();
|
||||
}
|
||||
|
||||
function onToggleArticleSearch(): void {
|
||||
showArticleSearch.value = !showArticleSearch.value;
|
||||
}
|
||||
|
||||
async function onArticleSearch(): Promise<void> {
|
||||
await articlesApi.loadList(articleKeyword.value);
|
||||
}
|
||||
|
||||
function onCreateArticle(): void {
|
||||
// 首页「+」直接弹底部编辑层(不另开页):新建空白整包,保存走 useArticles.save
|
||||
articlesApi.startCreate();
|
||||
articleEditorVisible.value = true;
|
||||
}
|
||||
|
||||
function closeArticleEditor(): void {
|
||||
articleEditorVisible.value = false;
|
||||
void articlesApi.cancelEdit();
|
||||
}
|
||||
|
||||
async function onArticleSaved(): Promise<void> {
|
||||
articleEditorVisible.value = false;
|
||||
await loadArticles();
|
||||
}
|
||||
|
||||
function onOpenArticle(name: string): void {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({ url: `${pageUrl("articlePage")}?name=${encodeURIComponent(name)}` });
|
||||
}
|
||||
|
||||
// ===== 详情态数据(原 family-tree-operation 全量照搬) =====
|
||||
const data = ref<FamilyTreeData>(new FamilyTreeData());
|
||||
const tree = computed(() => data.value.FamilyTree);
|
||||
@@ -1520,6 +1576,7 @@ defineExpose({
|
||||
reloadIfList(): void {
|
||||
if (!inDetail.value) {
|
||||
loadTrees();
|
||||
void loadArticles();
|
||||
void fetchStorage();
|
||||
}
|
||||
},
|
||||
@@ -1528,7 +1585,8 @@ defineExpose({
|
||||
|
||||
<template>
|
||||
<!-- 列表态显示 tabbar,进详情(或弹层展开)收起;未认证底部弹层也在列表态弹出,须一并隐 tabbar,否则盖住底部按钮 -->
|
||||
<app-page :hide-nav="true" :show-tabbar="!inDetail && !idPopup">
|
||||
<!-- 文章底部编辑层同理:不隐 tabbar 会盖住弹层底部的「取消/保存」(tabbar 与弹层不在同一层叠上下文,z-index 压不过) -->
|
||||
<app-page :hide-nav="true" :show-tabbar="!inDetail && !idPopup && !articleEditorVisible">
|
||||
<view class="tree-page" :style="[{ background: themeBackground }, themeVars]">
|
||||
<!-- ===== 列表态:软底分区面板(标题 + 搜索/新建/伸缩圆钮组 + 白底族谱卡) ===== -->
|
||||
<!-- 隐藏原生滚动条:否则 Chrome 滚动条占右侧 ~17px,面板左右留白不对称 -->
|
||||
@@ -1569,7 +1627,7 @@ defineExpose({
|
||||
<text class="home-card__stitle">{{ t("storage.title") }}</text>
|
||||
</view>
|
||||
<text class="home-card__spct" :class="{ 'home-card__spct--over': storageOver }">{{ storagePercentText
|
||||
}}</text>
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="home-card__bar">
|
||||
<view class="home-card__bar-fill" :class="{ 'home-card__bar-fill--over': storageOver }"
|
||||
@@ -1634,6 +1692,69 @@ defineExpose({
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
<!-- ===== 有家文化传承:与族谱面板并列的文章卡 ===== -->
|
||||
<view class="tree-panel article-panel">
|
||||
<view class="tree-panel__head">
|
||||
<text class="tree-panel__title">{{ t("article.title") }}</text>
|
||||
<view class="tree-panel__acts">
|
||||
<view class="tree-panel__act" @tap="onToggleArticleSearch">
|
||||
<u-icon name="search" size="34" :color="primary" />
|
||||
</view>
|
||||
<view class="tree-panel__act tree-panel__act--add" @tap="onCreateArticle">
|
||||
<u-icon name="plus" size="30" color="#ffffff" />
|
||||
</view>
|
||||
<view class="tree-panel__act" @tap="articleCollapsed = !articleCollapsed">
|
||||
<u-icon :name="articleCollapsed ? 'arrow-up' : 'arrow-down'" size="34" :color="primary" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="showArticleSearch" class="tree-panel__search">
|
||||
<search-input v-model="articleKeyword" :placeholder="t('article.searchPlaceholder')"
|
||||
@search="onArticleSearch" />
|
||||
</view>
|
||||
<view v-if="!articleCollapsed" class="tree-panel__list">
|
||||
<view v-if="articlesApi.loading.value" class="tree-page__state">
|
||||
<text class="tree-page__state-text">{{ t("FilePage.loading") }}</text>
|
||||
</view>
|
||||
<view v-else-if="articlesApi.errorText.value" class="tree-page__state">
|
||||
<text class="tree-page__state-text tree-page__state-text--error">{{ articlesApi.errorText.value }}</text>
|
||||
<text class="tree-page__retry" @click="loadArticles">{{ t("FilePage.retryBut") }}</text>
|
||||
</view>
|
||||
<template v-else>
|
||||
<view v-if="!homeArticles.length" class="tree-page__empty">
|
||||
<text class="tree-page__state-text">{{ t("article.empty") }}</text>
|
||||
</view>
|
||||
<view v-for="row in homeArticles" :key="row.ArticleName" class="article-card"
|
||||
@tap="onOpenArticle(row.ArticleName)">
|
||||
<view class="article-card__cover">
|
||||
<image v-if="row.CoverUrl || row.ArticleCoverFilename" class="article-card__cover-img"
|
||||
:src="absoluteFileUrl(row.CoverUrl) || backupFileUrl('ArticleCover', row.ArticleCoverFilename)"
|
||||
mode="aspectFill" />
|
||||
<image v-else class="article-card__glyph" :src="articleGlyph" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="article-card__main">
|
||||
<view class="article-card__titlerow">
|
||||
<text class="article-card__name">{{ row.ArticleTitle || t("article.unnamed") }}</text>
|
||||
<text v-if="row.TopIndc === 'Y'" class="article-card__chip article-card__chip--top">{{
|
||||
t("article.tagTop") }}</text>
|
||||
<text v-if="row.ApprovedIndc !== 'Y'" class="article-card__chip article-card__chip--pending">{{
|
||||
t("article.tagPending") }}</text>
|
||||
</view>
|
||||
<text class="article-card__desc">{{ articlesApi.plainText(row.Brief, 100) || t("article.noBrief")
|
||||
}}</text>
|
||||
<view class="article-card__meta">
|
||||
<image v-if="row.AuthorAvatarUrl" class="article-card__avatar"
|
||||
:src="absoluteFileUrl(row.AuthorAvatarUrl)" mode="aspectFill" />
|
||||
<view v-else class="article-card__avatar article-card__avatar--empty" />
|
||||
<text class="article-card__author">{{ row.AuthorLocalName || t("article.unknownAuthor") }}</text>
|
||||
<text class="article-card__date">{{ articlesApi.formatDate(row.CreatedDatetime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="30" color="#c0c4cc" />
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- ===== 详情态:族谱管理 ===== -->
|
||||
@@ -1665,7 +1786,7 @@ defineExpose({
|
||||
<u-switch v-if="data.IsManager" :model-value="tree.PublicIndc === 'Y'" size="40"
|
||||
:active-color="primary" @change="onPublicToggle" />
|
||||
<text v-if="data.IsManager" class="ft-badge ft-badge--admin">{{ t("FamilyTreePage.managerTag")
|
||||
}}</text>
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -1772,7 +1893,7 @@ defineExpose({
|
||||
<!-- 当前族员:从概览移入,夫妻/子女列表都是相对此人 -->
|
||||
<view v-if="loaded && currentPerson" class="ft-card">
|
||||
<view class="ft-card__head"><text class="ft-card__title">{{ t("FamilyTreePage.currentPerson")
|
||||
}}</text></view>
|
||||
}}</text></view>
|
||||
<view class="person-hero">
|
||||
<view class="person-hero__avatar"
|
||||
:style="avatarFallbackStyle(!!currentPerson?.AvatarUrl, currentPerson?.Gender)">
|
||||
@@ -1787,7 +1908,7 @@ defineExpose({
|
||||
</view>
|
||||
<view class="person-hero__chips">
|
||||
<text v-if="currentPerson?.PersonId" class="person-hero__chip">{{ personChipText(currentPerson)
|
||||
}}</text>
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -1830,12 +1951,12 @@ defineExpose({
|
||||
<!-- 新增入口与行尾操作同一把闸:当前锚点不可编(未绑定/出支系)时隐藏 -->
|
||||
<view class="ft-card__head">
|
||||
<view class="ft-card__title-group"><text class="ft-card__title">{{ t("FamilyTreePage.spouseTitle")
|
||||
}}</text><text class="ft-card__count">{{ spouseMaps.length }}</text></view>
|
||||
}}</text><text class="ft-card__count">{{ spouseMaps.length }}</text></view>
|
||||
<view v-if="data.IsCanAmend" class="ft-card__add" @tap="openQuickCreate('spouse')"><u-icon
|
||||
name="plus" size="26" :color="primary" /><text>{{ t("FamilyTreePage.addSpouse") }}</text></view>
|
||||
</view>
|
||||
<view v-if="!visibleSpouses.length" class="ft-empty"><text>{{ t("FamilyTreePage.spouseEmpty")
|
||||
}}</text></view>
|
||||
}}</text></view>
|
||||
<view v-for="record in visibleSpouses" :key="record.SpouseId" class="ft-mrow">
|
||||
<view class="ft-av"
|
||||
:style="avatarFallbackStyle(!!personAvatar(record.SpouseId), personGender(record.SpouseId, record.Gender))">
|
||||
@@ -1871,13 +1992,13 @@ defineExpose({
|
||||
<view class="ft-card">
|
||||
<view class="ft-card__head">
|
||||
<view class="ft-card__title-group"><text class="ft-card__title">{{ t("FamilyTreePage.childTitle")
|
||||
}}</text><text class="ft-card__count">{{ childMaps.length }}</text></view>
|
||||
}}</text><text class="ft-card__count">{{ childMaps.length }}</text></view>
|
||||
<view v-if="data.IsCanAmend" class="ft-card__add" @tap="openQuickCreate('child')"><u-icon
|
||||
name="plus" size="26" :color="primary" /><text>{{ t("FamilyTreePage.addChildBtn") }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!visibleChildren.length" class="ft-empty"><text>{{ t("FamilyTreePage.childEmpty")
|
||||
}}</text></view>
|
||||
}}</text></view>
|
||||
<view v-for="record in visibleChildren" :key="record.ChildrenId" class="ft-mrow">
|
||||
<view class="ft-av"
|
||||
:style="avatarFallbackStyle(!!personAvatar(record.ChildrenId), personGender(record.ChildrenId, record.Gender))">
|
||||
@@ -1917,7 +2038,7 @@ defineExpose({
|
||||
<view v-if="data.IsManager && pendingPermissions.length" class="ft-card">
|
||||
<view class="ft-card__head">
|
||||
<view class="ft-card__title-group"><text class="ft-card__title">{{ t("FamilyTreePage.permPending")
|
||||
}}</text><text class="ft-card__count">{{ pendingPermissions.length }}</text></view>
|
||||
}}</text><text class="ft-card__count">{{ pendingPermissions.length }}</text></view>
|
||||
</view>
|
||||
<view v-for="record in pendingPermissions" :key="record.UserName" class="ft-perm-row">
|
||||
<view class="ft-perm-row__main"><text class="ft-perm-row__name">{{ record.UserLocalName ||
|
||||
@@ -1941,10 +2062,10 @@ defineExpose({
|
||||
<view v-if="data.IsManager" class="ft-card">
|
||||
<view class="ft-card__head">
|
||||
<view class="ft-card__title-group"><text class="ft-card__title">{{ t("FamilyTreePage.permManager")
|
||||
}}</text><text class="ft-card__count">{{ managerPermissions.length }}</text></view>
|
||||
}}</text><text class="ft-card__count">{{ managerPermissions.length }}</text></view>
|
||||
</view>
|
||||
<view v-if="!managerPermissions.length" class="ft-empty"><text>{{ t("FamilyTreePage.permEmpty")
|
||||
}}</text></view>
|
||||
}}</text></view>
|
||||
<view v-for="record in managerPermissions" :key="record.UserName" class="ft-perm-row">
|
||||
<view class="ft-perm-row__main"><text class="ft-perm-row__name">{{ record.UserLocalName ||
|
||||
record.UserName
|
||||
@@ -2179,7 +2300,7 @@ defineExpose({
|
||||
<view class="person-hero__line">
|
||||
<text class="rel-name">{{ record.SpouseSurname }}{{ record.SpouseGenealogyName }}</text>
|
||||
<text class="person-hero__gender">{{ genderText(personGender(record.SpouseId, record.Gender))
|
||||
}}</text>
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="person-hero__chips">
|
||||
<text class="person-hero__chip">
|
||||
@@ -2216,7 +2337,7 @@ defineExpose({
|
||||
<text class="sub-label">{{ t("FamilyTreePage.marryDate") }}</text>
|
||||
<view class="sub-picker">
|
||||
<text class="sub-picker-text">{{ datePart(record.MarryDate) || t("AdminPage.notSetTip")
|
||||
}}</text>
|
||||
}}</text>
|
||||
<u-icon name="arrow-right" size="24" color="#c4c8cf" />
|
||||
</view>
|
||||
</view>
|
||||
@@ -2232,7 +2353,7 @@ defineExpose({
|
||||
<view class="sub-picker">
|
||||
<text class="sub-picker-text">{{
|
||||
datePart(record.DivorceDate) || t("AdminPage.notSetTip")
|
||||
}}</text>
|
||||
}}</text>
|
||||
<u-icon name="arrow-right" size="24" color="#c4c8cf" />
|
||||
</view>
|
||||
</view>
|
||||
@@ -2318,7 +2439,7 @@ defineExpose({
|
||||
<view class="person-hero__line">
|
||||
<text class="rel-name">{{ record.ChildrenSurname }}{{ record.ChildrenGenealogyName }}</text>
|
||||
<text class="person-hero__gender">{{ genderText(personGender(record.ChildrenId, record.Gender))
|
||||
}}</text>
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="person-hero__chips">
|
||||
<text class="person-hero__chip">
|
||||
@@ -2429,7 +2550,7 @@ defineExpose({
|
||||
<text class="rel-name">{{ record.UserLocalName || record.UserName }}</text>
|
||||
<text class="rel-sub">{{
|
||||
record.ManagerIndc === "Y" ? t("FamilyTreePage.managerTag") : t("FamilyTreePage.memberTag")
|
||||
}}</text>
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="perm-ops">
|
||||
<view class="perm-op perm-op--danger" @tap="rejectPermission(record)">
|
||||
@@ -2606,7 +2727,7 @@ defineExpose({
|
||||
<!-- 服务器文件列表限高滚动:文件多了不挤掉密码框/警告/按钮 -->
|
||||
<scroll-view v-if="!localFileName" scroll-y class="ft-exchange__list">
|
||||
<view v-if="importFilesLoading" class="ft-empty"><text>{{ t("FamilyTreePage.exchangeFileLoading")
|
||||
}}</text></view>
|
||||
}}</text></view>
|
||||
<view v-else-if="!importFiles.length" class="ft-empty"><text>{{ t(imScope === "SUB" ?
|
||||
"FamilyTreePage.exchangeFileEmptySub" : "FamilyTreePage.exchangeFileEmpty") }}</text></view>
|
||||
<view v-for="file in importFiles" :key="file.FileName" class="ft-exchange__row"
|
||||
@@ -2627,7 +2748,7 @@ defineExpose({
|
||||
<view v-if="backupLoading" class="ft-empty"><text>{{ t("FamilyTreePage.exchangeFileLoading") }}</text>
|
||||
</view>
|
||||
<view v-else-if="!backupFiles.length" class="ft-empty"><text>{{ t("FamilyTreePage.exchangeBackupEmpty")
|
||||
}}</text></view>
|
||||
}}</text></view>
|
||||
<view v-for="bk in backupFiles" :key="bk.FileName" class="ft-exchange__row"
|
||||
:class="{ 'ft-exchange__row--on': pickedBackup === bk.FileName }" @tap="pickedBackup = bk.FileName">
|
||||
<view class="ft-exchange__row-main">
|
||||
@@ -2693,11 +2814,15 @@ defineExpose({
|
||||
<view class="ft-sheet__foot">
|
||||
<view class="ft-sheet__btn" @tap="cancelTreeEdit"><text>{{ t("AdminPage.cancelBut") }}</text></view>
|
||||
<view class="ft-sheet__btn ft-sheet__btn--primary" @tap="saveTreeEdit"><text>{{ t("AdminPage.saveBut")
|
||||
}}</text>
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 有家文化传承:首页「+」新建底部编辑层(不另开页) -->
|
||||
<article-editor :visible="articleEditorVisible" :api="articlesApi" @close="closeArticleEditor"
|
||||
@saved="onArticleSaved" />
|
||||
</view>
|
||||
</app-page>
|
||||
</template>
|
||||
@@ -3264,6 +3389,127 @@ defineExpose({
|
||||
color: #bfc3c9;
|
||||
}
|
||||
|
||||
/* ===== 有家文化传承文章卡(复用 tree-panel 面板几何,卡体换横向图文) ===== */
|
||||
.article-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
margin: 0 0 20rpx;
|
||||
padding: 24rpx;
|
||||
border-radius: 24rpx;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 6rpx 18rpx rgba(31, 35, 41, 0.05);
|
||||
}
|
||||
|
||||
.article-card:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.article-card__cover {
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
overflow: hidden;
|
||||
border-radius: 20rpx;
|
||||
background: var(--fg-gradient, linear-gradient(135deg, #4a9dff, #2b8fff));
|
||||
}
|
||||
|
||||
.article-card__cover-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.article-card__glyph {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
|
||||
.article-card__main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.article-card__titlerow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.article-card__name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.article-card__chip {
|
||||
flex: none;
|
||||
padding: 2rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 20rpx;
|
||||
color: #5c626b;
|
||||
background-color: #f2f3f5;
|
||||
}
|
||||
|
||||
.article-card__chip--top {
|
||||
color: var(--fg-primary, #2b8fff);
|
||||
background-color: var(--fg-soft, #eaf4ff);
|
||||
}
|
||||
|
||||
.article-card__chip--pending {
|
||||
color: #d48806;
|
||||
background-color: #fff7e6;
|
||||
}
|
||||
|
||||
.article-card__desc {
|
||||
font-size: 24rpx;
|
||||
line-height: 34rpx;
|
||||
color: #8f959e;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.article-card__meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.article-card__avatar {
|
||||
flex: none;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #f2f3f5;
|
||||
}
|
||||
|
||||
.article-card__avatar--empty {
|
||||
background: var(--fg-soft, #eaf4ff);
|
||||
}
|
||||
|
||||
.article-card__author {
|
||||
font-size: 22rpx;
|
||||
color: #5c626b;
|
||||
}
|
||||
|
||||
.article-card__date {
|
||||
margin-left: auto;
|
||||
font-size: 20rpx;
|
||||
color: #bfc3c9;
|
||||
}
|
||||
|
||||
/* ===== 详情头卡 + 圆形动作钮(与共享页同套) ===== */
|
||||
.detail-hero {
|
||||
position: relative;
|
||||
|
||||
Reference in new issue
Block a user