fix: 新增7天浏览量

This commit is contained in:
若拙_233 2025-12-10 16:23:24 +08:00
parent 58f16be457
commit 6718b51fb9
2 changed files with 182 additions and 100 deletions

View File

@ -46,7 +46,8 @@ const activeDetailTab = ref('audit')
const reportLoading = ref(false) const reportLoading = ref(false)
const reportContent = ref('') const reportContent = ref('')
const pickFilledValue = (...values) => values.find((val) => val !== undefined && val !== null && val !== '') const pickFilledValue = (...values) =>
values.find((val) => val !== undefined && val !== null && val !== '')
const formatEnumField = (key, ...values) => formatEnumByKey(pickFilledValue(...values), key) const formatEnumField = (key, ...values) => formatEnumByKey(pickFilledValue(...values), key)
// //
@ -72,7 +73,7 @@ watch(activeDetailTab, async (newTab) => {
// //
const fetchReport = async () => { const fetchReport = async () => {
if (!props.detailData?.id) return if (!props.detailData?.id) return
reportLoading.value = true reportLoading.value = true
try { try {
const response = await api.getValuationReport({ valuation_id: props.detailData.id }) const response = await api.getValuationReport({ valuation_id: props.detailData.id })
@ -89,7 +90,7 @@ const fetchReport = async () => {
const detailSections = computed(() => { const detailSections = computed(() => {
const detail = props.detailData const detail = props.detailData
if (!detail) return [] if (!detail) return []
const sections = [ const sections = [
{ {
key: 'basic', key: 'basic',
@ -97,7 +98,11 @@ const detailSections = computed(() => {
fields: [ fields: [
{ label: '资产名称', type: 'text', value: detail.asset_name || '-' }, { label: '资产名称', type: 'text', value: detail.asset_name || '-' },
{ label: '所属机构/权利人', type: 'text', value: detail.institution || '-' }, { label: '所属机构/权利人', type: 'text', value: detail.institution || '-' },
{ label: '统一社会信用代码/身份证号', type: 'text', value: detail.credit_code_or_id || '-' }, {
label: '统一社会信用代码/身份证号',
type: 'text',
value: detail.credit_code_or_id || '-',
},
{ label: '所属行业', type: 'text', value: detail.industry || '-' }, { label: '所属行业', type: 'text', value: detail.industry || '-' },
{ label: '业务/传承介绍', type: 'text', value: detail.biz_intro || '-' }, { label: '业务/传承介绍', type: 'text', value: detail.biz_intro || '-' },
], ],
@ -106,17 +111,37 @@ const detailSections = computed(() => {
key: 'finance', key: 'finance',
title: '财务状况', title: '财务状况',
fields: [ fields: [
{ label: '近12个月机构营收/万元', type: 'text', value: formatNumberValue(detail.annual_revenue) }, {
{ label: '近12个月机构研发投入/万元', type: 'text', value: formatNumberValue(detail.rd_investment) }, label: '近12个月机构营收/万元',
{ label: '近三年机构收益/万元', type: 'list', value: formatThreeYearIncome(detail.three_year_income) }, type: 'text',
{ label: '资产受资助情况', type: 'text', value: formatEnumField('fundingStatus', detail.funding_status) }, 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', key: 'tech',
title: '非遗等级与技术', title: '非遗等级与技术',
fields: [ fields: [
{ label: '非遗传承人等级', type: 'text', value: formatEnumField('inheritorLevel', detail.inheritor_level) }, {
label: '非遗传承人等级',
type: 'text',
value: formatEnumField('inheritorLevel', detail.inheritor_level),
},
{ {
label: '非遗传承人年龄水平及数量', label: '非遗传承人年龄水平及数量',
type: 'list', type: 'list',
@ -126,10 +151,22 @@ const detailSections = computed(() => {
{ {
label: '非遗等级', label: '非遗等级',
type: 'text', type: 'text',
value: formatEnumField('heritageLevel', detail.heritage_level, detail.heritage_asset_level), 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: 'text', value: detail.patent_application_no || '-' },
{ label: '非遗资产历史证明证据及数量', type: 'list', value: formatHistoricalEvidence(detail.historical_evidence) },
{ {
label: '非遗资产所用专利/纹样图片', label: '非遗资产所用专利/纹样图片',
type: 'images', type: 'images',
@ -144,50 +181,86 @@ const detailSections = computed(() => {
{ {
label: '非遗资产应用成熟度', label: '非遗资产应用成熟度',
type: 'text', type: 'text',
value: formatEnumField('applicationMaturity', detail.application_maturity, detail.implementation_stage), value: formatEnumField(
'applicationMaturity',
detail.application_maturity,
detail.implementation_stage
),
}, },
{ {
label: '非遗资产应用覆盖范围', label: '非遗资产应用覆盖范围',
type: 'text', type: 'text',
value: formatEnumField('applicationCoverage', detail.application_coverage, detail.coverage_area), value: formatEnumField(
'applicationCoverage',
detail.application_coverage,
detail.coverage_area
),
}, },
{ {
label: '非遗资产跨界合作深度', label: '非遗资产跨界合作深度',
type: 'text', type: 'text',
value: formatEnumField('cooperationDepth', detail.cooperation_depth, detail.collaboration_type), value: formatEnumField(
'cooperationDepth',
detail.cooperation_depth,
detail.collaboration_type
),
}, },
{ {
label: '近12个月线下相关宣讲活动次数', label: '近12个月线下相关宣讲活动次数',
type: 'text', type: 'text',
value: formatNumberValue(detail.offline_activities ?? detail.offline_teaching_count), value: formatNumberValue(detail.offline_activities ?? detail.offline_teaching_count),
}, },
{ label: '线上相关宣传账号信息', type: 'list', value: formatPlatformAccounts(detail.platform_accounts) }, {
label: '线上相关宣传账号信息',
type: 'list',
value: formatPlatformAccounts(detail.platform_accounts),
},
], ],
}, },
{ {
key: 'products', key: 'products',
title: '非遗资产衍生商品信息', title: '非遗资产衍生商品信息',
fields: [ fields: [
{ label: '代表产品近12个月销售数量', type: 'text', value: formatNumberValue(detail.sales_volume) }, {
label: '代表产品近12个月销售数量',
type: 'text',
value: formatNumberValue(detail.sales_volume),
},
{ label: '商品链接浏览量', type: 'text', value: formatNumberValue(detail.link_views) }, { label: '商品链接浏览量', type: 'text', value: formatNumberValue(detail.link_views) },
{ label: '发行量', type: 'text', value: formatEnumField('circulation', detail.circulation, detail.scarcity_level) }, {
label: '发行量',
type: 'text',
value: formatEnumField('circulation', detail.circulation, detail.scarcity_level),
},
{ {
label: '最近一次市场活动时间', label: '最近一次市场活动时间',
type: 'text', type: 'text',
value: formatEnumField('marketActivity', detail.last_market_activity, detail.market_activity_time), value: formatEnumField(
'marketActivity',
detail.last_market_activity,
detail.market_activity_time
),
}, },
{ {
label: '月交易额水平', label: '月交易额水平',
type: 'text', type: 'text',
value: formatEnumField('monthlyTransaction', detail.monthly_transaction, detail.monthly_transaction_amount), value: formatEnumField(
'monthlyTransaction',
detail.monthly_transaction,
detail.monthly_transaction_amount
),
},
{
label: '近30天价格区间',
type: 'text',
value: formatPriceRange(detail.price_fluctuation),
}, },
{ label: '近30天价格区间', type: 'text', value: formatPriceRange(detail.price_fluctuation) },
], ],
}, },
] ]
// section NDataTable columns data // section NDataTable columns data
return sections.map(section => { return sections.map((section) => {
const columns = [ const columns = [
{ {
title: '字段名', title: '字段名',
@ -196,55 +269,65 @@ const detailSections = computed(() => {
align: 'center', align: 'center',
fixed: 'left', fixed: 'left',
}, },
...section.fields.map(field => ({ ...section.fields.map((field) => ({
title: field.label, title: field.label,
key: field.label, key: field.label,
width: 200, width: 200,
ellipsis: ['list', 'images'].includes(field.type) ? false : { ellipsis: ['list', 'images'].includes(field.type)
tooltip: { ? false
style: { maxWidth: '600px', maxHeight: '400px', overflow: 'auto' } : {
}, tooltip: {
}, style: { maxWidth: '600px', maxHeight: '400px', overflow: 'auto' },
},
},
render: (row) => { render: (row) => {
const fieldData = row[field.label] const fieldData = row[field.label]
if (!fieldData) return '-' if (!fieldData) return '-'
if (fieldData.type === 'list') { if (fieldData.type === 'list') {
if (fieldData.value && fieldData.value.length) { if (fieldData.value && fieldData.value.length) {
return h('div', { style: 'display: flex; flex-direction: column; gap: 4px;' }, return h(
fieldData.value.map(item => h('span', item)) 'div',
{ style: 'display: flex; flex-direction: column; gap: 4px;' },
fieldData.value.map((item) => h('span', item))
) )
} }
return '-' return '-'
} else if (fieldData.type === 'images') { } else if (fieldData.type === 'images') {
if (fieldData.value && fieldData.value.length) { if (fieldData.value && fieldData.value.length) {
const createImages = () => h(NImageGroup, {}, () => const createImages = () =>
fieldData.value.map(img => h(NImageGroup, {}, () =>
h(NImage, { fieldData.value.map((img) =>
src: img, h(NImage, {
width: 72, src: img,
height: 48, width: 72,
objectFit: 'cover', height: 48,
style: 'margin-right: 8px;' objectFit: 'cover',
}) style: 'margin-right: 8px;',
})
)
) )
)
return h(NPopover, { return h(
trigger: 'hover', NPopover,
displayDirective: 'show', {
keepAliveOnHover: true, trigger: 'hover',
style: { maxWidth: '600px', maxHeight: '400px', overflow: 'auto' } displayDirective: 'show',
}, { keepAliveOnHover: true,
trigger: () => h('div', { style: 'display: flex; overflow: hidden;' }, createImages()), style: { maxWidth: '600px', maxHeight: '400px', overflow: 'auto' },
default: () => createImages() },
}) {
trigger: () =>
h('div', { style: 'display: flex; overflow: hidden;' }, createImages()),
default: () => createImages(),
}
)
} }
return '-' return '-'
} else { } else {
return fieldData.value || '-' return fieldData.value || '-'
} }
} },
})), })),
] ]
@ -268,64 +351,64 @@ const detailSections = computed(() => {
const calcFlow = computed(() => props.detailData?.calculation_result?.flow || []) const calcFlow = computed(() => props.detailData?.calculation_result?.flow || [])
const renderedFlowHtml = computed(() => { const renderedFlowHtml = computed(() => {
return marked.parse(reportContent.value || mockReportMarkdown) return marked.parse(reportContent.value || mockReportMarkdown)
}) })
// //
const handleUploadCertificate = () => { const handleUploadCertificate = () => {
certificateModalMode.value = 'upload' certificateModalMode.value = 'upload'
certificateData.value = { certificateData.value = {
detailData: props.detailData detailData: props.detailData,
} }
certificateModalVisible.value = true certificateModalVisible.value = true
} }
const handleViewCertificate = () => { const handleViewCertificate = () => {
certificateModalMode.value = 'view' certificateModalMode.value = 'view'
const formatFiles = (urlData) => { const formatFiles = (urlData) => {
if (!urlData) return [] if (!urlData) return []
// Handle string (single or comma-separated) // Handle string (single or comma-separated)
const urls = typeof urlData === 'string' ? urlData.split(',') : (Array.isArray(urlData) ? urlData : []) const urls =
typeof urlData === 'string' ? urlData.split(',') : Array.isArray(urlData) ? urlData : []
return urls.filter(u => u).map((url, index) => ({
id: String(index), return urls
name: url.substring(url.lastIndexOf('/') + 1) || 'unknown', .filter((u) => u)
status: 'finished', .map((url, index) => ({
url: url id: String(index),
})) name: url.substring(url.lastIndexOf('/') + 1) || 'unknown',
status: 'finished',
url: url,
}))
} }
certificateData.value = { certificateData.value = {
reportFiles: formatFiles(props.detailData?.report_url), reportFiles: formatFiles(props.detailData?.report_url),
certificateFiles: formatFiles(props.detailData?.certificate_url), certificateFiles: formatFiles(props.detailData?.certificate_url),
detailData: props.detailData detailData: props.detailData,
} }
certificateModalVisible.value = true certificateModalVisible.value = true
} }
const handleCertificateConfirm = async (data) => { const handleCertificateConfirm = async (data) => {
console.log('证书数据:', data) console.log('证书数据:', data)
try { try {
const certificateUrl = data.certificateFiles?.map(f => f.url).filter(Boolean) || [] const certificateUrl = data.certificateFiles?.map((f) => f.url).filter(Boolean) || []
const reportUrl = data.reportFiles?.map(f => f.url).filter(Boolean) || [] const reportUrl = data.reportFiles?.map((f) => f.url).filter(Boolean) || []
// 1 // 1
const payload = { const payload = {
...props.detailData, ...props.detailData,
certificate_url: certificateUrl?.[0], certificate_url: certificateUrl?.[0],
report_url: reportUrl?.[0], report_url: reportUrl?.[0],
status: 'success' status: 'success',
} }
console.log("🔥🔥🔥🔥🔥🔥🔥 ~ handleCertificateConfirm ~ payload:", payload); console.log('🔥🔥🔥🔥🔥🔥🔥 ~ handleCertificateConfirm ~ payload:', payload)
await api.updateValuation(payload) await api.updateValuation(payload)
$message.success('上传并通知成功') $message.success('上传并通知成功')
certificateModalVisible.value = false certificateModalVisible.value = false
emit('back') // emit('refresh') emit('back') // emit('refresh')
@ -359,12 +442,7 @@ const handleCertificateConfirm = async (data) => {
</div> </div>
</div> </div>
<NTabs <NTabs v-model:value="activeDetailTab" type="line" size="large" class="audit-tabs">
v-model:value="activeDetailTab"
type="line"
size="large"
class="audit-tabs"
>
<NTabPane name="audit" tab="审核信息"> <NTabPane name="audit" tab="审核信息">
<NSpin :show="loading"> <NSpin :show="loading">
<div v-for="section in detailSections" :key="section.key" class="detail-section"> <div v-for="section in detailSections" :key="section.key" class="detail-section">
@ -395,19 +473,11 @@ const handleCertificateConfirm = async (data) => {
<!-- 证书按钮 --> <!-- 证书按钮 -->
<div class="certificate-actions"> <div class="certificate-actions">
<NButton <NButton v-if="mode === 'approve'" type="primary" @click="handleUploadCertificate">
v-if="mode === 'approve'"
type="primary"
@click="handleUploadCertificate"
>
<TheIcon icon="mdi:upload" :size="16" class="mr-4" /> <TheIcon icon="mdi:upload" :size="16" class="mr-4" />
上传证书 上传证书
</NButton> </NButton>
<NButton <NButton v-else type="info" @click="handleViewCertificate">
v-else
type="info"
@click="handleViewCertificate"
>
<TheIcon icon="mdi:eye" :size="16" class="mr-4" /> <TheIcon icon="mdi:eye" :size="16" class="mr-4" />
查看证书 查看证书
</NButton> </NButton>
@ -441,7 +511,7 @@ const handleCertificateConfirm = async (data) => {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: 16px;
margin: -16px 0px 10px ; margin: -16px 0px 10px;
} }
.back-btn { .back-btn {
@ -556,8 +626,8 @@ const handleCertificateConfirm = async (data) => {
line-height: 1.6; line-height: 1.6;
color: #333; color: #333;
} }
.markdown-body :deep(h1), .markdown-body :deep(h1),
.markdown-body :deep(h2), .markdown-body :deep(h2),
.markdown-body :deep(h3), .markdown-body :deep(h3),
.markdown-body :deep(h4), .markdown-body :deep(h4),
.markdown-body :deep(h5), .markdown-body :deep(h5),
@ -567,10 +637,23 @@ const handleCertificateConfirm = async (data) => {
font-weight: 600; font-weight: 600;
line-height: 1.25; line-height: 1.25;
} }
.markdown-body :deep(h1) { font-size: 2em; border-bottom: 1px solid #eaecef; padding-bottom: .3em; } .markdown-body :deep(h1) {
.markdown-body :deep(h2) { font-size: 1.5em; border-bottom: 1px solid #eaecef; padding-bottom: .3em; } font-size: 2em;
.markdown-body :deep(h3) { font-size: 1.25em; } border-bottom: 1px solid #eaecef;
.markdown-body :deep(p) { margin-top: 0; margin-bottom: 16px; } padding-bottom: 0.3em;
}
.markdown-body :deep(h2) {
font-size: 1.5em;
border-bottom: 1px solid #eaecef;
padding-bottom: 0.3em;
}
.markdown-body :deep(h3) {
font-size: 1.25em;
}
.markdown-body :deep(p) {
margin-top: 0;
margin-bottom: 16px;
}
.markdown-body :deep(blockquote) { .markdown-body :deep(blockquote) {
margin: 0 0 16px; margin: 0 0 16px;
padding: 0 1em; padding: 0 1em;
@ -592,7 +675,8 @@ const handleCertificateConfirm = async (data) => {
.markdown-body :deep(table tr:nth-child(2n)) { .markdown-body :deep(table tr:nth-child(2n)) {
background-color: #f6f8fa; background-color: #f6f8fa;
} }
.markdown-body :deep(table th), .markdown-body :deep(table td) { .markdown-body :deep(table th),
.markdown-body :deep(table td) {
padding: 6px 13px; padding: 6px 13px;
border: 1px solid #dfe2e5; border: 1px solid #dfe2e5;
} }
@ -603,7 +687,7 @@ const handleCertificateConfirm = async (data) => {
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
margin: 0; margin: 0;
font-size: 85%; font-size: 85%;
background-color: rgba(27,31,35,0.05); background-color: rgba(27, 31, 35, 0.05);
border-radius: 3px; border-radius: 3px;
} }
.markdown-body :deep(pre) { .markdown-body :deep(pre) {
@ -767,6 +851,4 @@ const handleCertificateConfirm = async (data) => {
color: #999; color: #999;
padding: 40px 0; padding: 40px 0;
} }
</style> </style>

View File

@ -148,7 +148,7 @@ export const formatPlatformAccounts = (accounts = {}) => {
if (!info) return `${label}-` if (!info) return `${label}-`
return `${label}${info.account || '-'}(赞${formatNumberValue(info.likes)} / 评${formatNumberValue( return `${label}${info.account || '-'}(赞${formatNumberValue(info.likes)} / 评${formatNumberValue(
info.comments info.comments
)} / 转${formatNumberValue(info.shares)}` )} / 转${formatNumberValue(info.shares)}/ 七日浏览量${formatNumberValue(info.views)}`
}) })
return list.length ? list : ['暂无账号信息'] return list.length ? list : ['暂无账号信息']
} }