feat(review-tablet): 优化目录交互与文件识别展示

pull/874/head
Codewoc 2026-04-11 21:22:38 +08:00
parent 06cd96067d
commit 2566d1e311
2 changed files with 232 additions and 324 deletions

View File

@ -45,3 +45,8 @@ export const getFileOpenUrl = (fileId: number): Promise<ReviewTabletOpenUrlVO> =
url: '/project/review-tablet/file-open-url', url: '/project/review-tablet/file-open-url',
params: { fileId } params: { fileId }
}) })
export const getAgendaOpenUrl = (): Promise<string> =>
request.get({
url: '/project/review-tablet/agenda-open-url'
})

View File

@ -15,7 +15,17 @@
<div class="left-title">专家评审资料</div> <div class="left-title">专家评审资料</div>
<el-button text :icon="Refresh" @click="loadCatalog" /> <el-button text :icon="Refresh" @click="loadCatalog" />
</div> </div>
<div class="left-sub-row">
<div class="left-sub">当前评审会议编目{{ projectCount }}</div> <div class="left-sub">当前评审会议编目{{ projectCount }}</div>
<el-button
class="agenda-entry-btn"
size="small"
:loading="agendaLoading"
:disabled="treeData.length === 0"
@click="openTodayAgenda"
>今日议程</el-button
>
</div>
<el-input <el-input
v-model="keyword" v-model="keyword"
@ -57,12 +67,13 @@
</div> </div>
<div v-else class="tree-item tree-file"> <div v-else class="tree-item tree-file">
<div class="node-main"> <div class="node-main">
<el-icon class="node-icon file-icon"><Document /></el-icon> <el-icon class="node-icon file-icon" :class="resolveFileIconClass(data.fileType)">
<component :is="resolveFileIcon(data.fileType)" />
</el-icon>
<el-tooltip :content="data.label" placement="top-start" :show-after="300"> <el-tooltip :content="data.label" placement="top-start" :show-after="300">
<span class="node-label">{{ data.label }}</span> <span class="node-label">{{ data.label }}</span>
</el-tooltip> </el-tooltip>
</div> </div>
<span class="node-ext">{{ data.fileType?.toUpperCase() }}</span>
</div> </div>
</template> </template>
</el-tree> </el-tree>
@ -72,12 +83,10 @@
<section class="center-pane"> <section class="center-pane">
<header class="center-header"> <header class="center-header">
<div class="center-title">{{ activeFileName || '请选择左侧资料进行预览' }}</div> <div class="center-title" :class="{ 'is-file-title': isFilePreviewing }">{{ centerTitle }}</div>
<div class="center-sub">{{ <div v-if="centerSub" class="center-sub">{{ centerSub }}</div>
activeFileDesc || '预览区支持 PDF/Office 等资料在线查看'
}}</div>
<div class="center-actions"> <div class="center-actions">
<el-button text :icon="Refresh" :disabled="!activeFileId" @click="refreshPreview" <el-button text :icon="Refresh" :disabled="!canRefreshPreview" @click="refreshPreview"
>刷新预览</el-button >刷新预览</el-button
> >
<el-button text :icon="Link" :disabled="!newWindowPreviewUrl" @click="openInNewWindow" <el-button text :icon="Link" :disabled="!newWindowPreviewUrl" @click="openInNewWindow"
@ -88,48 +97,7 @@
</header> </header>
<div class="center-body" v-loading="previewLoading"> <div class="center-body" v-loading="previewLoading">
<div v-if="showAgendaOverview" class="agenda-overview"> <div v-if="!activeFileId && !isAgendaPreview && !previewLoading" class="welcome-panel">
<div class="agenda-head">
<div class="agenda-title">今日议程</div>
<div class="agenda-sub">{{ agendaMeetingLabel }}</div>
</div>
<div class="agenda-content">
<div class="agenda-projects-panel">
<div class="agenda-panel-title">项目信息</div>
<div class="agenda-list">
<div v-for="item in agendaOverviewList" :key="item.id" class="agenda-item">
<div class="agenda-card">
<div class="agenda-index">{{ item.seqNoText }}</div>
<div class="agenda-main">
<div class="agenda-name">{{ item.projectTitle }}</div>
<div class="agenda-meta">
<span>{{ item.agendaCategory || '未设置议程分类' }}</span>
<span>{{ item.reporter || '未填写汇报人' }}</span>
<span v-if="item.reporterUnit">{{ item.reporterUnit }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="agenda-experts-panel">
<div class="agenda-panel-title">专家信息</div>
<div v-if="agendaExpertInfos.length > 0" class="agenda-experts">
<div v-for="expert in agendaExpertInfos" :key="expert.key" class="agenda-expert">
<div class="agenda-expert-name">{{ expert.name }}</div>
<div class="agenda-expert-meta">
<span v-if="expert.organization">{{ expert.organization }}</span>
<span v-if="expert.title">{{ expert.title }}</span>
<span v-if="expert.position">{{ expert.position }}</span>
<span v-if="expert.remark">{{ expert.remark }}</span>
</div>
</div>
</div>
<div v-else class="agenda-experts-empty">暂无专家信息</div>
</div>
</div>
</div>
<div v-else-if="!activeFileId && !previewLoading" class="welcome-panel">
<div class="welcome-title">欢迎查阅评审资料</div> <div class="welcome-title">欢迎查阅评审资料</div>
<div class="welcome-sub">请从左侧目录中选择文件开始浏览</div> <div class="welcome-sub">请从左侧目录中选择文件开始浏览</div>
</div> </div>
@ -427,14 +395,18 @@ import {
ArrowLeftBold, ArrowLeftBold,
ArrowRightBold, ArrowRightBold,
Close, Close,
DataBoard,
Document, Document,
Files,
Folder, Folder,
InfoFilled, InfoFilled,
Link, Link,
Loading, Loading,
Picture,
Promotion, Promotion,
Refresh, Refresh,
Search, Search,
Tickets,
WarningFilled WarningFilled
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
@ -449,6 +421,7 @@ import {
import { import {
getFileOpenUrl, getFileOpenUrl,
getProjectFiles, getProjectFiles,
getAgendaOpenUrl,
getActiveCatalog, getActiveCatalog,
type ReviewTabletCatalogVO, type ReviewTabletCatalogVO,
type ReviewTabletOpenUrlVO type ReviewTabletOpenUrlVO
@ -518,6 +491,7 @@ const pageLoading = ref(false)
const previewLoading = ref(false) const previewLoading = ref(false)
const previewError = ref('') const previewError = ref('')
const keyword = ref('') const keyword = ref('')
const agendaLoading = ref(false)
const leftCollapsed = ref(false) const leftCollapsed = ref(false)
const rightCollapsed = ref(false) const rightCollapsed = ref(false)
const sideTab = ref<SideTab>('ai') const sideTab = ref<SideTab>('ai')
@ -532,6 +506,7 @@ const fileCache = ref<Record<number, ReviewMeetingFileRespVO[]>>({})
const activeProjectId = ref<number>() const activeProjectId = ref<number>()
const activeFileId = ref<number>() const activeFileId = ref<number>()
const activeFileNode = ref<FileTreeNode>() const activeFileNode = ref<FileTreeNode>()
const isAgendaPreview = ref(false)
const previewUrl = ref('') const previewUrl = ref('')
const previewPayload = ref<ReviewTabletOpenUrlVO>() const previewPayload = ref<ReviewTabletOpenUrlVO>()
const previewFrameLoading = ref(false) const previewFrameLoading = ref(false)
@ -541,65 +516,19 @@ const previewRequestToken = ref(0)
const treeProps = { label: 'label', children: 'children' } const treeProps = { label: 'label', children: 'children' }
const projectCount = computed(() => treeData.value.length) const projectCount = computed(() => treeData.value.length)
const showAgendaOverview = computed(
() =>
!previewLoading.value &&
!activeFileId.value &&
treeData.value.length > 0 &&
expandedKeys.value.length === 0
)
const agendaOverviewList = computed(() =>
treeData.value.map((item) => ({
id: item.id,
projectTitle: item.label,
agendaCategory: item.source.agendaCategory,
expertInfos: (item.source.expertInfos || []).map((expert, index) => ({
key: `${item.id}-${index}-${expert.name || 'expert'}`,
name: expert.name,
organization: expert.organization,
title: expert.title,
position: expert.position,
remark: expert.remark ? `备注:${expert.remark}` : ''
})),
reporter: item.source.reporter,
reporterUnit: item.source.reporterUnit,
seqNoText: item.seqNo ? String(item.seqNo).padStart(2, '0') : '--'
}))
)
const agendaExpertInfos = computed(() => {
const seen = new Set<string>()
return agendaOverviewList.value
.flatMap((item) => item.expertInfos)
.filter((expert) => {
const key = [
expert.name,
expert.organization,
expert.title,
expert.position,
expert.remark
].join('|')
if (seen.has(key)) return false
seen.add(key)
return true
})
})
const agendaMeetingLabel = computed(() => {
const first = treeData.value[0]?.source
if (!first) return '当日评审项目总览'
const timeSegments = [first.meetingName]
if (first.meetingStartTime && first.meetingEndTime) {
timeSegments.push(
`${formatDate(new Date(first.meetingStartTime), 'HH:mm')} - ${formatDate(
new Date(first.meetingEndTime),
'HH:mm'
)}`
)
}
return timeSegments.filter(Boolean).join(' · ')
})
const activeFileName = computed( const activeFileName = computed(
() => activeFileNode.value?.label || previewPayload.value?.fileName || '' () => activeFileNode.value?.label || previewPayload.value?.fileName || ''
) )
const isFilePreviewing = computed(() => !!activeFileId.value && !isAgendaPreview.value)
const centerTitle = computed(() =>
isAgendaPreview.value ? '今日议程' : activeFileName.value || '请选择左侧资料进行预览'
)
const centerSub = computed(() =>
isAgendaPreview.value
? ''
: activeFileDesc.value || '预览区支持 PDF/Office 等资料在线查看'
)
const canRefreshPreview = computed(() => isAgendaPreview.value || !!activeFileId.value)
const activeProjectTitle = computed(() => { const activeProjectTitle = computed(() => {
if (!activeProjectId.value) return '' if (!activeProjectId.value) return ''
return ( return (
@ -828,7 +757,50 @@ const loadPreview = async (fileId: number) => {
} }
} }
const loadAgendaPreview = async () => {
const requestToken = ++previewRequestToken.value
clearPreviewFrameTimeout()
previewLoading.value = true
previewFrameLoading.value = true
previewError.value = ''
previewUrl.value = ''
previewPayload.value = undefined
previewFrameKey.value += 1
agendaLoading.value = true
try {
const openUrl = await getAgendaOpenUrl()
if (requestToken !== previewRequestToken.value) return
if (!openUrl) {
previewFrameLoading.value = false
previewError.value = '当前会议暂无议程文件,请先在会议新建阶段生成议程'
return
}
previewUrl.value = openUrl
startPreviewFrameTimeout(requestToken)
} catch {
if (requestToken !== previewRequestToken.value) return
previewFrameLoading.value = false
previewError.value = '议程文件预览加载失败,请稍后重试'
} finally {
agendaLoading.value = false
if (requestToken === previewRequestToken.value) {
previewLoading.value = false
}
}
}
const openTodayAgenda = async () => {
if (treeData.value.length === 0) return
isAgendaPreview.value = true
activeFileId.value = undefined
activeFileNode.value = undefined
await nextTick()
treeRef.value?.setCurrentKey(undefined)
await loadAgendaPreview()
}
const selectFileNode = async (fileNode: FileTreeNode) => { const selectFileNode = async (fileNode: FileTreeNode) => {
isAgendaPreview.value = false
activeProjectId.value = fileNode.projectId activeProjectId.value = fileNode.projectId
activeFileId.value = fileNode.fileId activeFileId.value = fileNode.fileId
activeFileNode.value = fileNode activeFileNode.value = fileNode
@ -845,6 +817,10 @@ const openInNewWindow = () => {
} }
const refreshPreview = async () => { const refreshPreview = async () => {
if (isAgendaPreview.value) {
await loadAgendaPreview()
return
}
if (activeFileId.value) await loadPreview(activeFileId.value) if (activeFileId.value) await loadPreview(activeFileId.value)
} }
@ -878,6 +854,7 @@ const handleNodeExpand = async (data: TreeNode) => {
} }
const resetPreviewSelection = async () => { const resetPreviewSelection = async () => {
isAgendaPreview.value = false
clearPreviewFrameTimeout() clearPreviewFrameTimeout()
previewRequestToken.value += 1 previewRequestToken.value += 1
previewError.value = '' previewError.value = ''
@ -899,12 +876,22 @@ const handleNodeCollapse = (data: TreeNode) => {
} }
} }
const handleNodeClick = async (data: TreeNode) => { const handleNodeClick = async (data: TreeNode, node: any) => {
if (data.type === 'project') { if (data.type === 'project') {
persistCurrentProjectAiState() persistCurrentProjectAiState()
const switched = activeProjectId.value !== data.projectId const switched = activeProjectId.value !== data.projectId
activeProjectId.value = data.projectId activeProjectId.value = data.projectId
const isExpanded = !!node?.expanded
if (!isExpanded) {
await loadProjectFiles(data) await loadProjectFiles(data)
}
if (node) {
if (isExpanded) {
node.collapse()
} else {
node.expand()
}
}
if (isAiTabActive.value && (switched || !aiSummary.value)) { if (isAiTabActive.value && (switched || !aiSummary.value)) {
restoreOrLoadProjectAi(data.projectId) restoreOrLoadProjectAi(data.projectId)
} }
@ -924,6 +911,7 @@ const loadCatalog = async () => {
clearPreviewFrameTimeout() clearPreviewFrameTimeout()
clearOfficeWarmupTimers() clearOfficeWarmupTimers()
previewRequestToken.value += 1 previewRequestToken.value += 1
isAgendaPreview.value = false
previewError.value = '' previewError.value = ''
previewUrl.value = '' previewUrl.value = ''
previewPayload.value = undefined previewPayload.value = undefined
@ -1241,6 +1229,26 @@ const formatFileSize = (bytes: number): string => {
const isOfficeFile = (fileType?: string) => const isOfficeFile = (fileType?: string) =>
['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'].includes((fileType || '').toLowerCase()) ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'].includes((fileType || '').toLowerCase())
const resolveFileIcon = (fileType?: string) => {
const normalized = (fileType || '').toLowerCase()
if (['ppt', 'pptx'].includes(normalized)) return Tickets
if (['xls', 'xlsx', 'csv'].includes(normalized)) return DataBoard
if (['doc', 'docx', 'txt', 'rtf'].includes(normalized)) return Document
if (normalized === 'pdf') return Files
if (['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'].includes(normalized)) return Picture
return Document
}
const resolveFileIconClass = (fileType?: string) => {
const normalized = (fileType || '').toLowerCase()
if (['ppt', 'pptx'].includes(normalized)) return 'file-icon--ppt'
if (['xls', 'xlsx', 'csv'].includes(normalized)) return 'file-icon--excel'
if (['doc', 'docx', 'txt', 'rtf'].includes(normalized)) return 'file-icon--doc'
if (normalized === 'pdf') return 'file-icon--pdf'
if (['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'].includes(normalized)) return 'file-icon--image'
return ''
}
const clearOfficeWarmupTimers = () => { const clearOfficeWarmupTimers = () => {
officeWarmupTimers.forEach((timer) => clearTimeout(timer)) officeWarmupTimers.forEach((timer) => clearTimeout(timer))
officeWarmupTimers = [] officeWarmupTimers = []
@ -1426,9 +1434,63 @@ loadCatalog()
} }
.left-sub { .left-sub {
margin-top: 8px;
color: var(--tablet-text-soft); color: var(--tablet-text-soft);
font-size: 14px; font-size: 14px;
line-height: 1.4;
}
.left-sub-row {
margin-top: 8px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.agenda-entry-btn {
flex-shrink: 0;
height: 30px;
padding: 0 14px;
border-radius: 6px;
border: 1px solid #b8cbea;
background: #dbe7fa;
color: #2d5ca3;
font-size: 13px;
font-weight: 600;
letter-spacing: 0.3px;
box-shadow:
0 5px 12px rgba(60, 99, 182, 0.16),
inset 0 1px 0 rgba(255, 255, 255, 0.88);
transition:
transform 0.18s ease,
box-shadow 0.18s ease,
border-color 0.18s ease,
color 0.18s ease,
background 0.18s ease;
}
.agenda-entry-btn:hover:not(.is-disabled) {
border-color: #9eb9e2;
color: #244b88;
background: #cfe0fa;
box-shadow:
0 7px 16px rgba(45, 92, 163, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
transform: translateY(-1px);
}
.agenda-entry-btn:active:not(.is-disabled) {
transform: translateY(0);
box-shadow:
0 4px 10px rgba(45, 92, 163, 0.16),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
}
.agenda-entry-btn.is-disabled {
border-color: var(--tablet-border);
background: var(--tablet-surface-muted);
color: #8695ab;
box-shadow: none;
} }
.left-search { .left-search {
@ -1487,17 +1549,15 @@ loadCatalog()
} }
.node-label { .node-label {
overflow: hidden; display: block;
display: -webkit-box; overflow: visible;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
white-space: normal; white-space: normal;
line-height: 1.4; line-height: 1.4;
font-size: 15px; font-size: 15px;
font-weight: 500; font-weight: 500;
color: var(--tablet-text); color: var(--tablet-text);
word-break: break-all; word-break: break-word;
overflow-wrap: anywhere;
} }
.node-icon { .node-icon {
@ -1515,6 +1575,26 @@ loadCatalog()
color: #5d7395; color: #5d7395;
} }
.file-icon--doc {
color: #2f67be;
}
.file-icon--ppt {
color: #d86a34;
}
.file-icon--excel {
color: #2f9d62;
}
.file-icon--pdf {
color: #cc4a4a;
}
.file-icon--image {
color: #7b5ac8;
}
.node-badge { .node-badge {
min-width: 20px; min-width: 20px;
padding: 2px 6px; padding: 2px 6px;
@ -1527,14 +1607,6 @@ loadCatalog()
flex-shrink: 0; flex-shrink: 0;
} }
.node-ext {
font-size: 12px;
line-height: 1.3;
color: var(--tablet-text-soft);
flex-shrink: 0;
margin-top: 2px;
}
.center-header { .center-header {
padding: 14px 16px 10px; padding: 14px 16px 10px;
border-bottom: 1px solid var(--tablet-border); border-bottom: 1px solid var(--tablet-border);
@ -1543,7 +1615,7 @@ loadCatalog()
.center-title { .center-title {
font-size: 42px; font-size: 42px;
line-height: 1; line-height: 1.1;
color: var(--tablet-text); color: var(--tablet-text);
font-weight: 700; font-weight: 700;
white-space: nowrap; white-space: nowrap;
@ -1551,6 +1623,16 @@ loadCatalog()
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.center-title.is-file-title {
font-size: clamp(20px, 2.2vw, 30px);
line-height: 1.3;
white-space: normal;
overflow: visible;
text-overflow: clip;
overflow-wrap: anywhere;
word-break: break-word;
}
.center-sub { .center-sub {
margin-top: 10px; margin-top: 10px;
color: var(--tablet-text-soft); color: var(--tablet-text-soft);
@ -1577,169 +1659,6 @@ loadCatalog()
position: relative; position: relative;
} }
.agenda-overview {
height: 100%;
box-sizing: border-box;
padding: 18px 20px;
display: flex;
flex-direction: column;
gap: 12px;
overflow-y: auto;
background:
radial-gradient(circle at top right, rgba(196, 213, 239, 0.4), transparent 28%),
linear-gradient(180deg, var(--tablet-panel-soft) 0%, var(--tablet-panel) 100%);
}
.agenda-head {
display: flex;
flex-direction: column;
gap: 6px;
}
.agenda-title {
font-size: 24px;
font-weight: 700;
color: var(--tablet-text);
}
.agenda-sub {
font-size: 13px;
color: var(--tablet-text-soft);
}
.agenda-list {
min-width: 0;
display: flex;
flex-direction: column;
gap: 6px;
}
.agenda-content {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(320px, 0.9fr);
gap: 14px;
align-items: stretch;
min-height: 0;
}
.agenda-project {
min-width: 0;
background: var(--tablet-panel);
border-radius: 14px;
}
.agenda-projects-panel,
.agenda-experts-panel {
min-width: 0;
box-sizing: border-box;
padding: 10px 12px;
border-radius: 12px;
border: 1px solid var(--tablet-border);
box-shadow: 0 8px 24px rgba(139, 158, 191, 0.06);
}
.agenda-projects-panel {
height: 100%;
background: var(--tablet-panel);
}
.agenda-experts-panel {
background: var(--tablet-panel);
}
.agenda-item {
min-width: 0;
}
.agenda-panel-title {
margin-bottom: 8px;
font-size: 13px;
font-weight: 700;
color: var(--tablet-accent);
}
.agenda-card {
display: grid;
grid-template-columns: 48px minmax(0, 1fr);
gap: 10px;
align-items: start;
padding: 8px 10px;
border-radius: 10px;
border: 1px solid var(--tablet-border-strong);
background: var(--tablet-surface-strong);
}
.agenda-index {
height: 28px;
border-radius: 8px;
background: linear-gradient(135deg, #d5e3f8, #e5edf9);
color: var(--tablet-accent);
font-size: 14px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
.agenda-main {
min-width: 0;
display: flex;
flex-direction: column;
gap: 3px;
}
.agenda-name {
font-size: 15px;
line-height: 1.35;
color: var(--tablet-text);
font-weight: 600;
word-break: break-word;
}
.agenda-meta {
display: flex;
flex-wrap: wrap;
gap: 4px 10px;
color: var(--tablet-text-soft);
font-size: 11px;
line-height: 1.4;
}
.agenda-experts {
display: flex;
flex-direction: column;
gap: 6px;
}
.agenda-expert {
padding: 8px 10px;
border-radius: 8px;
background: #e3ebf7;
border: 1px solid var(--tablet-border-strong);
}
.agenda-expert-name {
font-size: 13px;
font-weight: 700;
color: var(--tablet-text);
}
.agenda-expert-meta {
margin-top: 3px;
display: flex;
flex-wrap: wrap;
gap: 4px 10px;
color: var(--tablet-text-soft);
font-size: 11px;
line-height: 1.5;
}
.agenda-experts-empty {
color: var(--tablet-text-soft);
font-size: 12px;
line-height: 1.5;
}
.preview-iframe { .preview-iframe {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -2253,6 +2172,11 @@ loadCatalog()
padding-right: 6px; padding-right: 6px;
} }
:deep(.catalog-tree .el-tree-node__expand-icon) {
display: none;
pointer-events: none;
}
:deep(.el-tree-node__content:hover) { :deep(.el-tree-node__content:hover) {
background: var(--tablet-surface-muted); background: var(--tablet-surface-muted);
} }
@ -2350,6 +2274,13 @@ loadCatalog()
padding: 10px 8px; padding: 10px 8px;
} }
.tablet-shell.is-touch-tablet .agenda-entry-btn {
height: 34px;
padding: 0 16px;
font-size: 14px;
border-radius: 7px;
}
.tablet-shell.is-touch-tablet .center-header { .tablet-shell.is-touch-tablet .center-header {
padding: 12px 14px 10px; padding: 12px 14px 10px;
} }
@ -2358,6 +2289,10 @@ loadCatalog()
font-size: clamp(22px, 2.8vw, 34px); font-size: clamp(22px, 2.8vw, 34px);
} }
.tablet-shell.is-touch-tablet .center-title.is-file-title {
font-size: clamp(18px, 2.1vw, 24px);
}
.tablet-shell.is-touch-tablet .center-sub { .tablet-shell.is-touch-tablet .center-sub {
margin-top: 8px; margin-top: 8px;
font-size: clamp(14px, 1.8vw, 19px); font-size: clamp(14px, 1.8vw, 19px);
@ -2368,32 +2303,6 @@ loadCatalog()
gap: 6px; gap: 6px;
} }
.tablet-shell.is-touch-tablet .agenda-overview {
padding: 14px 14px calc(16px + env(safe-area-inset-bottom, 0px));
}
.tablet-shell.is-touch-tablet .agenda-content {
gap: 10px;
}
.tablet-shell.is-touch-tablet .agenda-card {
grid-template-columns: 42px minmax(0, 1fr);
gap: 8px;
}
.tablet-shell.is-touch-tablet .agenda-index {
height: 26px;
font-size: 13px;
}
.tablet-shell.is-touch-tablet .agenda-name {
font-size: 14px;
}
.tablet-shell.is-touch-tablet .agenda-meta {
font-size: 12px;
}
.tablet-shell.is-touch-tablet .side-tabbar { .tablet-shell.is-touch-tablet .side-tabbar {
padding: 8px; padding: 8px;
} }
@ -2443,15 +2352,6 @@ loadCatalog()
font-size: 16px; font-size: 16px;
} }
.tablet-shell.is-compact-tablet .agenda-content {
grid-template-columns: minmax(0, 1fr);
}
.tablet-shell.is-compact-tablet .agenda-projects-panel,
.tablet-shell.is-compact-tablet .agenda-experts-panel {
padding: 10px;
}
.tablet-shell.is-compact-tablet .ai-summary-wrap { .tablet-shell.is-compact-tablet .ai-summary-wrap {
max-height: 188px; max-height: 188px;
} }
@ -2460,6 +2360,10 @@ loadCatalog()
font-size: clamp(20px, 2.4vw, 28px); font-size: clamp(20px, 2.4vw, 28px);
} }
.tablet-shell.is-dense-tablet .center-title.is-file-title {
font-size: clamp(16px, 2vw, 21px);
}
.tablet-shell.is-dense-tablet .center-sub { .tablet-shell.is-dense-tablet .center-sub {
font-size: 14px; font-size: 14px;
} }
@ -2468,8 +2372,7 @@ loadCatalog()
font-size: 13px; font-size: 13px;
} }
.tablet-shell.is-dense-tablet .left-sub, .tablet-shell.is-dense-tablet .left-sub {
.tablet-shell.is-dense-tablet .agenda-sub {
font-size: 12px; font-size: 12px;
} }