feat(review-tablet): 优化目录交互与文件识别展示
parent
06cd96067d
commit
2566d1e311
|
|
@ -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'
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -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">当前评审会议编目:{{ projectCount }}篇</div>
|
<div class="left-sub-row">
|
||||||
|
<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
|
||||||
await loadProjectFiles(data)
|
const isExpanded = !!node?.expanded
|
||||||
|
if (!isExpanded) {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue