762 lines
19 KiB
Vue
762 lines
19 KiB
Vue
<script setup>
|
||
import { computed, ref, watch, h } from 'vue'
|
||
import {
|
||
NButton,
|
||
NTag,
|
||
NTabs,
|
||
NTabPane,
|
||
NSpin,
|
||
NImage,
|
||
NImageGroup,
|
||
NDataTable,
|
||
} from 'naive-ui'
|
||
import { marked } from 'marked'
|
||
|
||
import { formatDate } from '@/utils'
|
||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||
import CertificateModal from './CertificateModal.vue'
|
||
import api from '@/api'
|
||
import { useMessage } from 'naive-ui'
|
||
|
||
import { getStatusConfig } from '../constants'
|
||
import {
|
||
formatAgeDistribution,
|
||
formatAmount,
|
||
formatHistoricalEvidence,
|
||
formatPercent,
|
||
formatPlatformAccounts,
|
||
formatPriceRange,
|
||
formatThreeYearIncome,
|
||
formatNumberValue,
|
||
formatEnumByKey,
|
||
} from '../utils'
|
||
import { mockReportMarkdown } from './mockData.js'
|
||
|
||
const props = defineProps({
|
||
loading: { type: Boolean, default: false },
|
||
detailData: { type: Object, default: null },
|
||
mode: { type: String, default: 'view' },
|
||
})
|
||
|
||
const emit = defineEmits(['back', 'approve', 'reject'])
|
||
const $message = useMessage()
|
||
|
||
const activeDetailTab = ref('audit')
|
||
const reportLoading = ref(false)
|
||
const reportContent = ref('')
|
||
|
||
const pickFilledValue = (...values) => values.find((val) => val !== undefined && val !== null && val !== '')
|
||
const formatEnumField = (key, ...values) => formatEnumByKey(pickFilledValue(...values), key)
|
||
|
||
// 证书弹窗相关状态
|
||
const certificateModalVisible = ref(false)
|
||
const certificateModalMode = ref('upload') // 'upload' 或 'view'
|
||
const certificateData = ref({})
|
||
|
||
watch(
|
||
() => props.detailData?.id,
|
||
() => {
|
||
activeDetailTab.value = 'audit'
|
||
reportContent.value = ''
|
||
}
|
||
)
|
||
|
||
// 监听 tab 切换,当切换到计算流程时加载报告
|
||
watch(activeDetailTab, async (newTab) => {
|
||
if (newTab === 'flow' && props.detailData?.id && !reportContent.value) {
|
||
await fetchReport()
|
||
}
|
||
})
|
||
|
||
// 获取报告内容
|
||
const fetchReport = async () => {
|
||
if (!props.detailData?.id) return
|
||
|
||
reportLoading.value = true
|
||
try {
|
||
const response = await api.getValuationReport({ valuation_id: props.detailData.id })
|
||
reportContent.value = response.data || response || ''
|
||
} catch (error) {
|
||
console.error('获取报告失败:', error)
|
||
$message.error('获取报告失败')
|
||
reportContent.value = '# 获取报告失败\n\n请稍后重试'
|
||
} finally {
|
||
reportLoading.value = false
|
||
}
|
||
}
|
||
|
||
const detailSections = computed(() => {
|
||
const detail = props.detailData
|
||
if (!detail) return []
|
||
|
||
const sections = [
|
||
{
|
||
key: 'basic',
|
||
title: '基础信息',
|
||
fields: [
|
||
{ label: '资产名称', type: 'text', value: detail.asset_name || '-' },
|
||
{ label: '所属机构/权利人', type: 'text', value: detail.institution || '-' },
|
||
{ label: '统一社会信用代码/身份证号', type: 'text', value: detail.credit_code_or_id || '-' },
|
||
{ label: '所属行业', type: 'text', value: detail.industry || '-' },
|
||
{ label: '业务/传承介绍', type: 'text', value: detail.biz_intro || '-' },
|
||
],
|
||
},
|
||
{
|
||
key: 'finance',
|
||
title: '财务状况',
|
||
fields: [
|
||
{ label: '近12个月机构营收/万元', type: 'text', value: formatNumberValue(detail.annual_revenue) },
|
||
{ label: '近12个月机构研发投入/万元', type: 'text', value: formatNumberValue(detail.rd_investment) },
|
||
{ label: '近三年机构收益/万元', type: 'list', value: formatThreeYearIncome(detail.three_year_income) },
|
||
{ label: '资产受资助情况', type: 'text', value: formatEnumField('fundingStatus', detail.funding_status) },
|
||
],
|
||
},
|
||
{
|
||
key: 'tech',
|
||
title: '非遗等级与技术',
|
||
fields: [
|
||
{ label: '非遗传承人等级', type: 'text', value: formatEnumField('inheritorLevel', detail.inheritor_level) },
|
||
{
|
||
label: '非遗传承人年龄水平及数量',
|
||
type: 'list',
|
||
value: formatAgeDistribution(detail.inheritor_age_count || detail.inheritor_ages),
|
||
},
|
||
{ label: '非遗传承人等级证书', type: 'images', value: detail.inheritor_certificates || [] },
|
||
{
|
||
label: '非遗等级',
|
||
type: 'text',
|
||
value: formatEnumField('heritageLevel', detail.heritage_level, detail.heritage_asset_level),
|
||
},
|
||
{ label: '非遗资产所用专利的申请号', type: 'text', value: detail.patent_application_no || '-' },
|
||
{ label: '非遗资产历史证明证据及数量', type: 'list', value: formatHistoricalEvidence(detail.historical_evidence) },
|
||
{
|
||
label: '非遗资产所用专利/纹样图片',
|
||
type: 'images',
|
||
value: [...(detail.patent_certificates || []), ...(detail.pattern_images || [])],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
key: 'promotion',
|
||
title: '非遗应用与推广',
|
||
fields: [
|
||
{
|
||
label: '非遗资产应用成熟度',
|
||
type: 'text',
|
||
value: formatEnumField('applicationMaturity', detail.application_maturity, detail.implementation_stage),
|
||
},
|
||
{
|
||
label: '非遗资产应用覆盖范围',
|
||
type: 'text',
|
||
value: formatEnumField('applicationCoverage', detail.application_coverage, detail.coverage_area),
|
||
},
|
||
{
|
||
label: '非遗资产跨界合作深度',
|
||
type: 'text',
|
||
value: formatEnumField('cooperationDepth', detail.cooperation_depth, detail.collaboration_type),
|
||
},
|
||
{
|
||
label: '近12个月线下相关宣讲活动次数',
|
||
type: 'text',
|
||
value: formatNumberValue(detail.offline_activities ?? detail.offline_teaching_count),
|
||
},
|
||
{ label: '线上相关宣传账号信息', type: 'list', value: formatPlatformAccounts(detail.platform_accounts) },
|
||
],
|
||
},
|
||
{
|
||
key: 'products',
|
||
title: '非遗资产衍生商品信息',
|
||
fields: [
|
||
{ label: '代表产品近12个月销售数量', type: 'text', value: formatNumberValue(detail.sales_volume) },
|
||
{ label: '商品链接浏览量', type: 'text', value: formatNumberValue(detail.link_views) },
|
||
{ label: '发行量', type: 'text', value: formatEnumField('circulation', detail.circulation, detail.scarcity_level) },
|
||
{
|
||
label: '最近一次市场活动时间',
|
||
type: 'text',
|
||
value: formatEnumField('marketActivity', detail.last_market_activity, detail.market_activity_time),
|
||
},
|
||
{
|
||
label: '月交易额水平',
|
||
type: 'text',
|
||
value: formatEnumField('monthlyTransaction', detail.monthly_transaction, detail.monthly_transaction_amount),
|
||
},
|
||
{ label: '近30天价格区间', type: 'text', value: formatPriceRange(detail.price_fluctuation) },
|
||
],
|
||
},
|
||
]
|
||
|
||
// 为每个 section 生成 NDataTable 需要的 columns 和 data
|
||
return sections.map(section => {
|
||
const columns = [
|
||
{
|
||
title: '字段名',
|
||
key: 'fieldName',
|
||
width: 120,
|
||
align: 'center',
|
||
fixed: 'left',
|
||
},
|
||
...section.fields.map(field => ({
|
||
title: field.label,
|
||
key: field.label,
|
||
width: 200,
|
||
ellipsis: field.type === 'list' ? false : {
|
||
tooltip: {
|
||
style: { maxWidth: '600px', maxHeight: '400px', overflow: 'auto' }
|
||
},
|
||
},
|
||
render: (row) => {
|
||
const fieldData = row[field.label]
|
||
if (!fieldData) return '-'
|
||
|
||
if (fieldData.type === 'list') {
|
||
if (fieldData.value && fieldData.value.length) {
|
||
return h('div', { style: 'display: flex; flex-direction: column; gap: 4px;' },
|
||
fieldData.value.map(item => h('span', item))
|
||
)
|
||
}
|
||
return '-'
|
||
} else if (fieldData.type === 'images') {
|
||
if (fieldData.value && fieldData.value.length) {
|
||
return h(NImageGroup, {}, () =>
|
||
fieldData.value.map(img =>
|
||
h(NImage, {
|
||
src: img,
|
||
width: 72,
|
||
height: 48,
|
||
objectFit: 'cover',
|
||
style: 'margin-right: 8px;'
|
||
})
|
||
)
|
||
)
|
||
}
|
||
return '-'
|
||
} else {
|
||
return fieldData.value || '-'
|
||
}
|
||
}
|
||
})),
|
||
]
|
||
|
||
const data = [
|
||
{
|
||
fieldName: '用户输入',
|
||
...section.fields.reduce((acc, field) => {
|
||
acc[field.label] = field
|
||
return acc
|
||
}, {}),
|
||
},
|
||
]
|
||
|
||
return {
|
||
...section,
|
||
columns,
|
||
data,
|
||
}
|
||
})
|
||
})
|
||
|
||
const calcFlow = computed(() => props.detailData?.calculation_result?.flow || [])
|
||
|
||
|
||
|
||
const renderedFlowHtml = computed(() => {
|
||
return marked.parse(reportContent.value || mockReportMarkdown)
|
||
})
|
||
|
||
|
||
// 证书相关功能
|
||
const handleUploadCertificate = () => {
|
||
certificateModalMode.value = 'upload'
|
||
certificateData.value = {
|
||
detailData: props.detailData
|
||
}
|
||
certificateModalVisible.value = true
|
||
}
|
||
|
||
const handleViewCertificate = () => {
|
||
certificateModalMode.value = 'view'
|
||
|
||
const formatFiles = (urlData) => {
|
||
if (!urlData) return []
|
||
// Handle string (single or comma-separated)
|
||
const urls = typeof urlData === 'string' ? urlData.split(',') : (Array.isArray(urlData) ? urlData : [])
|
||
|
||
return urls.filter(u => u).map((url, index) => ({
|
||
id: String(index),
|
||
name: url.substring(url.lastIndexOf('/') + 1) || 'unknown',
|
||
status: 'finished',
|
||
url: url
|
||
}))
|
||
}
|
||
|
||
certificateData.value = {
|
||
reportFiles: formatFiles(props.detailData?.report_url),
|
||
certificateFiles: formatFiles(props.detailData?.certificate_url),
|
||
detailData: props.detailData
|
||
}
|
||
certificateModalVisible.value = true
|
||
}
|
||
|
||
const handleCertificateConfirm = async (data) => {
|
||
console.log('证书数据:', data)
|
||
|
||
try {
|
||
const certificateUrl = data.certificateFiles?.map(f => f.url).filter(Boolean) || []
|
||
const reportUrl = data.reportFiles?.map(f => f.url).filter(Boolean) || []
|
||
|
||
// 现在改为只能上传 1 张
|
||
const payload = {
|
||
...props.detailData,
|
||
certificate_url: certificateUrl?.[0],
|
||
report_url: reportUrl?.[0],
|
||
status: 'success'
|
||
}
|
||
console.log("🔥🔥🔥🔥🔥🔥🔥 ~ handleCertificateConfirm ~ payload:", payload);
|
||
|
||
await api.updateValuation(payload)
|
||
|
||
$message.success('上传并通知成功')
|
||
certificateModalVisible.value = false
|
||
emit('back') // 或者 emit('refresh') 取决于需求,这里假设返回列表或刷新
|
||
} catch (error) {
|
||
console.error('更新失败:', error)
|
||
$message.error('操作失败')
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="audit-detail">
|
||
<div class="detail-header">
|
||
<div>
|
||
<button type="button" class="back-btn" @click="emit('back')">
|
||
<TheIcon icon="mdi:arrow-left" :size="16" class="mr-4" />
|
||
返回审核列表
|
||
</button>
|
||
<!-- <div class="detail-title">
|
||
<h2>{{ detailData?.asset_name || '审核详情' }}</h2>
|
||
<NTag size="small" :type="getStatusConfig(detailData?.status).type">
|
||
{{ getStatusConfig(detailData?.status).text }}
|
||
</NTag>
|
||
</div>
|
||
<p class="detail-meta">
|
||
<span>手机号:{{ detailData?.phone || '-' }}</span>
|
||
<span>微信号:{{ detailData?.wechat || '-' }}</span>
|
||
<span>提交时间:{{ formatDate(detailData?.created_at) }}</span>
|
||
<span>审核时间:{{ detailData?.reviewed_at ? formatDate(detailData?.reviewed_at) : '-' }}</span>
|
||
</p> -->
|
||
</div>
|
||
</div>
|
||
|
||
<NTabs
|
||
v-model:value="activeDetailTab"
|
||
type="line"
|
||
size="large"
|
||
class="audit-tabs"
|
||
>
|
||
<NTabPane name="audit" tab="审核信息">
|
||
<NSpin :show="loading">
|
||
<div v-for="section in detailSections" :key="section.key" class="detail-section">
|
||
<div class="section-title">
|
||
<span class="dot" />
|
||
<span>{{ section.title }}</span>
|
||
</div>
|
||
<NDataTable
|
||
:columns="section.columns"
|
||
:data="section.data"
|
||
:bordered="true"
|
||
:single-line="false"
|
||
:scroll-x="section.fields.length * 200 + 120"
|
||
>
|
||
<template #empty>
|
||
<span>暂无数据</span>
|
||
</template>
|
||
</NDataTable>
|
||
</div>
|
||
</NSpin>
|
||
</NTabPane>
|
||
<NTabPane name="flow" tab="计算流程">
|
||
<NSpin :show="reportLoading">
|
||
<div class="markdown-body" v-html="renderedFlowHtml"></div>
|
||
</NSpin>
|
||
</NTabPane>
|
||
</NTabs>
|
||
|
||
<!-- 证书按钮 -->
|
||
<div class="certificate-actions">
|
||
<NButton
|
||
v-if="mode === 'approve'"
|
||
type="primary"
|
||
@click="handleUploadCertificate"
|
||
>
|
||
<TheIcon icon="mdi:upload" :size="16" class="mr-4" />
|
||
上传证书
|
||
</NButton>
|
||
<NButton
|
||
v-else
|
||
type="info"
|
||
@click="handleViewCertificate"
|
||
>
|
||
<TheIcon icon="mdi:eye" :size="16" class="mr-4" />
|
||
查看证书
|
||
</NButton>
|
||
</div>
|
||
|
||
<!-- 证书弹窗 -->
|
||
<CertificateModal
|
||
v-model:visible="certificateModalVisible"
|
||
:mode="certificateModalMode"
|
||
:certificate-data="certificateData"
|
||
@confirm="handleCertificateConfirm"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.audit-detail {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
.certificate-actions {
|
||
margin-top: 20px;
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
z-index: 100;
|
||
}
|
||
}
|
||
|
||
.detail-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
margin: -16px 0px 10px ;
|
||
}
|
||
|
||
.back-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 0;
|
||
border: none;
|
||
background: transparent;
|
||
color: #409eff;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.detail-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin: 8px 0;
|
||
}
|
||
|
||
.detail-title h2 {
|
||
margin: 0;
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.detail-meta {
|
||
margin: 0;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16px;
|
||
color: #666;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.detail-actions {
|
||
display: flex;
|
||
gap: 12px;
|
||
align-items: center;
|
||
}
|
||
|
||
.detail-section {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.audit-tabs :deep(.n-tabs-nav) {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.audit-tabs :deep(.n-tabs-tab) {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #1d2129;
|
||
padding: 0 24px;
|
||
}
|
||
|
||
.audit-tabs :deep(.n-tabs-tab.n-tabs-tab--active) {
|
||
color: #ff6f3b;
|
||
}
|
||
|
||
.audit-tabs :deep(.n-tabs-nav__line) {
|
||
background-color: #ff6f3b;
|
||
height: 3px;
|
||
border-radius: 999px;
|
||
}
|
||
|
||
.section-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
font-size: 18px;
|
||
color: #1d2129;
|
||
}
|
||
|
||
.section-title .dot {
|
||
width: 10px;
|
||
height: 18px;
|
||
border-radius: 4px;
|
||
background: #3b82f6;
|
||
}
|
||
|
||
.detail-section :deep(.n-data-table) {
|
||
background: #f9fafe;
|
||
}
|
||
|
||
.detail-section :deep(.n-data-table-th) {
|
||
background: #f1f2f5;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.detail-section :deep(.n-data-table-th:first-child) {
|
||
background: #f1f2f5;
|
||
text-align: center;
|
||
}
|
||
|
||
.detail-section :deep(.n-data-table-td:first-child) {
|
||
background: #f1f2f5;
|
||
text-align: center;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.cell-multi {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
/* Markdown Styles */
|
||
.markdown-body {
|
||
line-height: 1.6;
|
||
color: #333;
|
||
}
|
||
.markdown-body :deep(h1),
|
||
.markdown-body :deep(h2),
|
||
.markdown-body :deep(h3),
|
||
.markdown-body :deep(h4),
|
||
.markdown-body :deep(h5),
|
||
.markdown-body :deep(h6) {
|
||
margin-top: 24px;
|
||
margin-bottom: 16px;
|
||
font-weight: 600;
|
||
line-height: 1.25;
|
||
}
|
||
.markdown-body :deep(h1) { font-size: 2em; border-bottom: 1px solid #eaecef; padding-bottom: .3em; }
|
||
.markdown-body :deep(h2) { font-size: 1.5em; border-bottom: 1px solid #eaecef; padding-bottom: .3em; }
|
||
.markdown-body :deep(h3) { font-size: 1.25em; }
|
||
.markdown-body :deep(p) { margin-top: 0; margin-bottom: 16px; }
|
||
.markdown-body :deep(blockquote) {
|
||
margin: 0 0 16px;
|
||
padding: 0 1em;
|
||
color: #6a737d;
|
||
border-left: 0.25em solid #dfe2e5;
|
||
}
|
||
.markdown-body :deep(table) {
|
||
display: block;
|
||
width: 100%;
|
||
overflow: auto;
|
||
margin-bottom: 16px;
|
||
border-spacing: 0;
|
||
border-collapse: collapse;
|
||
}
|
||
.markdown-body :deep(table tr) {
|
||
background-color: #fff;
|
||
border-top: 1px solid #c6cbd1;
|
||
}
|
||
.markdown-body :deep(table tr:nth-child(2n)) {
|
||
background-color: #f6f8fa;
|
||
}
|
||
.markdown-body :deep(table th), .markdown-body :deep(table td) {
|
||
padding: 6px 13px;
|
||
border: 1px solid #dfe2e5;
|
||
}
|
||
.markdown-body :deep(table th) {
|
||
font-weight: 600;
|
||
}
|
||
.markdown-body :deep(code) {
|
||
padding: 0.2em 0.4em;
|
||
margin: 0;
|
||
font-size: 85%;
|
||
background-color: rgba(27,31,35,0.05);
|
||
border-radius: 3px;
|
||
}
|
||
.markdown-body :deep(pre) {
|
||
padding: 16px;
|
||
overflow: auto;
|
||
font-size: 85%;
|
||
line-height: 1.45;
|
||
background-color: #f6f8fa;
|
||
border-radius: 3px;
|
||
}
|
||
.markdown-body :deep(pre code) {
|
||
display: inline;
|
||
padding: 0;
|
||
margin: 0;
|
||
overflow: visible;
|
||
line-height: inherit;
|
||
word-wrap: normal;
|
||
background-color: initial;
|
||
border: 0;
|
||
}
|
||
|
||
/* 计算流程容器 */
|
||
:deep(.calc-flow-container) {
|
||
display: flex;
|
||
gap: 24px;
|
||
min-height: 600px;
|
||
}
|
||
|
||
/* 左侧详细流程 */
|
||
:deep(.calc-flow-left) {
|
||
flex: 1;
|
||
background: #f8f9fa;
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
overflow-y: auto;
|
||
max-height: 800px;
|
||
}
|
||
|
||
:deep(.calc-formula-header) {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1d2129;
|
||
margin-bottom: 24px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 2px solid #e5e7eb;
|
||
}
|
||
|
||
:deep(.calc-section) {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
:deep(.calc-section-title) {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #1d2129;
|
||
margin-bottom: 16px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
:deep(.calc-subsection) {
|
||
margin-left: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
:deep(.calc-subsection-title) {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: #374151;
|
||
margin-bottom: 12px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
:deep(.calc-item) {
|
||
margin-left: 20px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
:deep(.calc-item-label) {
|
||
font-size: 14px;
|
||
color: #4b5563;
|
||
margin-bottom: 8px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
:deep(.calc-detail) {
|
||
margin-left: 20px;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
:deep(.calc-detail-item) {
|
||
font-size: 13px;
|
||
color: #6b7280;
|
||
margin-bottom: 4px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* 右侧大纲 */
|
||
:deep(.calc-flow-right) {
|
||
width: 320px;
|
||
background: #fff;
|
||
border: 1px solid #e5e7eb;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
overflow-y: auto;
|
||
max-height: 800px;
|
||
}
|
||
|
||
:deep(.calc-outline-title) {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #1d2129;
|
||
margin-bottom: 16px;
|
||
padding-bottom: 12px;
|
||
border-bottom: 2px solid #e5e7eb;
|
||
}
|
||
|
||
:deep(.calc-outline) {
|
||
font-size: 13px;
|
||
}
|
||
|
||
:deep(.outline-section) {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
:deep(.outline-title) {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #1d2129;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
:deep(.outline-subsection) {
|
||
margin-left: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
:deep(.outline-subtitle) {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #374151;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
:deep(.outline-item) {
|
||
font-size: 12px;
|
||
color: #4b5563;
|
||
margin-bottom: 4px;
|
||
margin-left: 12px;
|
||
}
|
||
|
||
:deep(.outline-detail) {
|
||
font-size: 11px;
|
||
color: #6b7280;
|
||
margin-left: 24px;
|
||
margin-bottom: 2px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.calc-empty {
|
||
text-align: center;
|
||
color: #999;
|
||
padding: 40px 0;
|
||
}
|
||
|
||
|
||
</style>
|