feat: 添加评估报告下载功能,包括下载页面、生成工具和模板,并更新了路由和权限守卫。
This commit is contained in:
parent
4b945339d0
commit
5332324b10
@ -19,11 +19,15 @@
|
||||
"@zclzone/eslint-config": "^0.0.4",
|
||||
"axios": "^1.4.0",
|
||||
"dayjs": "^1.11.9",
|
||||
"docxtemplater": "^3.67.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"eslint": "^8.46.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"jszip": "^3.10.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"naive-ui": "^2.34.4",
|
||||
"pinia": "^2.1.6",
|
||||
"pizzip": "^3.2.0",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"sass": "^1.65.1",
|
||||
"typescript": "^5.1.6",
|
||||
|
||||
110
web/pnpm-lock.yaml
generated
110
web/pnpm-lock.yaml
generated
@ -29,12 +29,21 @@ importers:
|
||||
dayjs:
|
||||
specifier: ^1.11.9
|
||||
version: 1.11.12
|
||||
docxtemplater:
|
||||
specifier: ^3.67.5
|
||||
version: 3.67.5
|
||||
dotenv:
|
||||
specifier: ^16.3.1
|
||||
version: 16.4.5
|
||||
eslint:
|
||||
specifier: ^8.46.0
|
||||
version: 8.57.0
|
||||
file-saver:
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5
|
||||
jszip:
|
||||
specifier: ^3.10.1
|
||||
version: 3.10.1
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
@ -44,6 +53,9 @@ importers:
|
||||
pinia:
|
||||
specifier: ^2.1.6
|
||||
version: 2.2.0(typescript@5.5.4)(vue@3.4.34(typescript@5.5.4))
|
||||
pizzip:
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.0
|
||||
rollup-plugin-visualizer:
|
||||
specifier: ^5.9.2
|
||||
version: 5.12.0(rollup@3.29.4)
|
||||
@ -581,6 +593,10 @@ packages:
|
||||
'@vueuse/shared@10.11.0':
|
||||
resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==}
|
||||
|
||||
'@xmldom/xmldom@0.9.8':
|
||||
resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==}
|
||||
engines: {node: '>=14.6'}
|
||||
|
||||
'@zclzone/eslint-config@0.0.4':
|
||||
resolution: {integrity: sha512-dDDHsLc0qEt/tczC1nRU5d+2LCOPwwKohw5Wlq4A1mTFgTQaFoSDmP/j9XnAbjCYfxbGUeEat0221WwwVbPhuw==}
|
||||
|
||||
@ -810,6 +826,9 @@ packages:
|
||||
resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
core-util-is@1.0.3:
|
||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||
|
||||
cors@2.8.5:
|
||||
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
||||
engines: {node: '>= 0.10'}
|
||||
@ -941,6 +960,10 @@ packages:
|
||||
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
docxtemplater@3.67.5:
|
||||
resolution: {integrity: sha512-Jnh9rdMf5sDmrfONs3nVDhZwVFxLNdP3RVHkqLQYA6eZLkWA+kx5aYy0cVmEqJcVIaFYf5JJEFdClD7fn6ZUng==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
dom-serializer@0.2.2:
|
||||
resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==}
|
||||
|
||||
@ -1159,6 +1182,9 @@ packages:
|
||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
|
||||
file-saver@2.0.5:
|
||||
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
||||
|
||||
filelist@1.0.4:
|
||||
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
|
||||
|
||||
@ -1363,6 +1389,9 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
hasBin: true
|
||||
|
||||
immediate@3.0.6:
|
||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||
|
||||
immutable@4.3.7:
|
||||
resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
|
||||
|
||||
@ -1575,6 +1604,9 @@ packages:
|
||||
jsonfile@6.1.0:
|
||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||
|
||||
jszip@3.10.1:
|
||||
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
||||
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
@ -1601,6 +1633,9 @@ packages:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
lie@3.3.0:
|
||||
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
||||
|
||||
loader-utils@1.4.2:
|
||||
resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
@ -1803,6 +1838,12 @@ packages:
|
||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
pako@1.0.11:
|
||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||
|
||||
pako@2.1.0:
|
||||
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
|
||||
|
||||
param-case@3.0.4:
|
||||
resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
|
||||
|
||||
@ -1864,6 +1905,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
pizzip@3.2.0:
|
||||
resolution: {integrity: sha512-X4NPNICxCfIK8VYhF6wbksn81vTiziyLbvKuORVAmolvnUzl1A1xmz9DAWKxPRq9lZg84pJOOAMq3OE61bD8IQ==}
|
||||
|
||||
pkg-types@1.1.3:
|
||||
resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==}
|
||||
|
||||
@ -1922,6 +1966,9 @@ packages:
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
process-nextick-args@2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
@ -1936,6 +1983,9 @@ packages:
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
readable-stream@2.3.8:
|
||||
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
|
||||
|
||||
readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
@ -2018,6 +2068,9 @@ packages:
|
||||
resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
|
||||
engines: {node: '>=0.4'}
|
||||
|
||||
safe-buffer@5.1.2:
|
||||
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
|
||||
|
||||
safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
|
||||
@ -2056,6 +2109,9 @@ packages:
|
||||
resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
setimmediate@1.0.5:
|
||||
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
@ -2149,6 +2205,9 @@ packages:
|
||||
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
string_decoder@1.1.1:
|
||||
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
|
||||
|
||||
string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
|
||||
@ -3067,6 +3126,8 @@ snapshots:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@xmldom/xmldom@0.9.8': {}
|
||||
|
||||
'@zclzone/eslint-config@0.0.4':
|
||||
dependencies:
|
||||
eslint: 8.57.0
|
||||
@ -3315,6 +3376,8 @@ snapshots:
|
||||
|
||||
copy-descriptor@0.1.1: {}
|
||||
|
||||
core-util-is@1.0.3: {}
|
||||
|
||||
cors@2.8.5:
|
||||
dependencies:
|
||||
object-assign: 4.1.1
|
||||
@ -3442,6 +3505,10 @@ snapshots:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
docxtemplater@3.67.5:
|
||||
dependencies:
|
||||
'@xmldom/xmldom': 0.9.8
|
||||
|
||||
dom-serializer@0.2.2:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
@ -3779,6 +3846,8 @@ snapshots:
|
||||
dependencies:
|
||||
flat-cache: 3.2.0
|
||||
|
||||
file-saver@2.0.5: {}
|
||||
|
||||
filelist@1.0.4:
|
||||
dependencies:
|
||||
minimatch: 5.1.6
|
||||
@ -3988,6 +4057,8 @@ snapshots:
|
||||
|
||||
image-size@0.5.5: {}
|
||||
|
||||
immediate@3.0.6: {}
|
||||
|
||||
immutable@4.3.7: {}
|
||||
|
||||
import-fresh@3.3.0:
|
||||
@ -4176,6 +4247,13 @@ snapshots:
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
jszip@3.10.1:
|
||||
dependencies:
|
||||
lie: 3.3.0
|
||||
pako: 1.0.11
|
||||
readable-stream: 2.3.8
|
||||
setimmediate: 1.0.5
|
||||
|
||||
keyv@4.5.4:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
@ -4199,6 +4277,10 @@ snapshots:
|
||||
prelude-ls: 1.2.1
|
||||
type-check: 0.4.0
|
||||
|
||||
lie@3.3.0:
|
||||
dependencies:
|
||||
immediate: 3.0.6
|
||||
|
||||
loader-utils@1.4.2:
|
||||
dependencies:
|
||||
big.js: 5.2.2
|
||||
@ -4444,6 +4526,10 @@ snapshots:
|
||||
dependencies:
|
||||
p-limit: 3.1.0
|
||||
|
||||
pako@1.0.11: {}
|
||||
|
||||
pako@2.1.0: {}
|
||||
|
||||
param-case@3.0.4:
|
||||
dependencies:
|
||||
dot-case: 3.0.4
|
||||
@ -4488,6 +4574,10 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.5.4
|
||||
|
||||
pizzip@3.2.0:
|
||||
dependencies:
|
||||
pako: 2.1.0
|
||||
|
||||
pkg-types@1.1.3:
|
||||
dependencies:
|
||||
confbox: 0.1.7
|
||||
@ -4551,6 +4641,8 @@ snapshots:
|
||||
|
||||
prettier@2.8.8: {}
|
||||
|
||||
process-nextick-args@2.0.1: {}
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
punycode@2.3.1: {}
|
||||
@ -4562,6 +4654,16 @@ snapshots:
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
readable-stream@2.3.8:
|
||||
dependencies:
|
||||
core-util-is: 1.0.3
|
||||
inherits: 2.0.4
|
||||
isarray: 1.0.0
|
||||
process-nextick-args: 2.0.1
|
||||
safe-buffer: 5.1.2
|
||||
string_decoder: 1.1.1
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
readable-stream@3.6.2:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
@ -4636,6 +4738,8 @@ snapshots:
|
||||
has-symbols: 1.0.3
|
||||
isarray: 2.0.5
|
||||
|
||||
safe-buffer@5.1.2: {}
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
safe-regex-test@1.0.3:
|
||||
@ -4683,6 +4787,8 @@ snapshots:
|
||||
is-plain-object: 2.0.4
|
||||
split-string: 3.1.0
|
||||
|
||||
setimmediate@1.0.5: {}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
dependencies:
|
||||
shebang-regex: 3.0.0
|
||||
@ -4790,6 +4896,10 @@ snapshots:
|
||||
define-properties: 1.2.1
|
||||
es-object-atoms: 1.0.0
|
||||
|
||||
string_decoder@1.1.1:
|
||||
dependencies:
|
||||
safe-buffer: 5.1.2
|
||||
|
||||
string_decoder@1.3.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
BIN
web/public/report_template.docx
Normal file
BIN
web/public/report_template.docx
Normal file
Binary file not shown.
@ -7,7 +7,7 @@ export function createAuthGuard(router) {
|
||||
|
||||
/** 没有token的情况 */
|
||||
if (isNullOrWhitespace(token)) {
|
||||
if (WHITE_LIST.includes(to.path)) return true
|
||||
if (WHITE_LIST.includes(to.path) || to.path.startsWith('/report/download/')) return true
|
||||
return { path: 'login', query: { ...to.query, redirect: to.path } }
|
||||
}
|
||||
|
||||
|
||||
@ -107,6 +107,15 @@ export const basicRoutes = [
|
||||
component: () => import('@/views/error-page/404.vue'),
|
||||
isHidden: true,
|
||||
},
|
||||
{
|
||||
name: 'ReportDownload',
|
||||
path: '/report/download/:id',
|
||||
component: () => import('@/views/report/download/index.vue'),
|
||||
isHidden: true,
|
||||
meta: {
|
||||
title: '下载报告',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Login',
|
||||
path: '/login',
|
||||
|
||||
131
web/src/utils/report.js
Normal file
131
web/src/utils/report.js
Normal file
@ -0,0 +1,131 @@
|
||||
import PizZip from 'pizzip'
|
||||
import Docxtemplater from 'docxtemplater'
|
||||
import { saveAs } from 'file-saver'
|
||||
import {
|
||||
formatNumberValue,
|
||||
formatThreeYearIncome,
|
||||
formatAgeDistribution,
|
||||
formatHistoricalEvidence,
|
||||
formatPlatformAccounts,
|
||||
formatPriceRange
|
||||
} from '@/views/valuation/audit/utils'
|
||||
|
||||
export const generateReport = async (detailData) => {
|
||||
try {
|
||||
// Load the template
|
||||
const response = await fetch('/report_template.docx')
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load report template')
|
||||
}
|
||||
const content = await response.arrayBuffer()
|
||||
|
||||
const zip = new PizZip(content)
|
||||
const doc = new Docxtemplater(zip, {
|
||||
paragraphLoop: true,
|
||||
linebreaks: true,
|
||||
delimiters: { start: '[', end: ']' },
|
||||
})
|
||||
|
||||
// Helper to get safe number
|
||||
const getNum = (val) => (val || val === 0 ? Number(val) : '-')
|
||||
const getWan = (val) => (val || val === 0 ? formatNumberValue(val) : '-')
|
||||
|
||||
// Prepare data
|
||||
const data = {
|
||||
...detailData,
|
||||
// Summary
|
||||
B: getWan(detailData.model_value_b),
|
||||
B1: getWan(detailData.economic_value_b1),
|
||||
B2: getWan(detailData.cultural_value_b2),
|
||||
B3: getNum(detailData.risk_coefficient_b3),
|
||||
C: getWan(detailData.market_value_c),
|
||||
DPR: getNum(detailData.pledge_rate),
|
||||
|
||||
// Basic Info
|
||||
asset_name: detailData.asset_name || '-',
|
||||
institution: detailData.institution || '-',
|
||||
credit_code: detailData.credit_code || detailData.credit_code_or_id || '-',
|
||||
industry: detailData.industry || '-',
|
||||
business_heritage_intro: detailData.business_heritage_intro || detailData.biz_intro || '-',
|
||||
|
||||
// Finance
|
||||
annual_revenue: getWan(detailData.annual_revenue),
|
||||
rd_investment: getWan(detailData.rd_investment),
|
||||
three_year_income: Array.isArray(detailData.three_year_income) ? detailData.three_year_income.join(',') : '-',
|
||||
funding_status: detailData.funding_status || '-',
|
||||
|
||||
// Tech / Non-heritage attributes
|
||||
heritage_level: detailData.heritage_level || detailData.heritage_asset_level || '-',
|
||||
inheritor_age_count_50: detailData.inheritor_age_count?.[0] || 0,
|
||||
inheritor_age_count_50_70: detailData.inheritor_age_count?.[1] || 0,
|
||||
inheritor_age_count_70: detailData.inheritor_age_count?.[2] || 0,
|
||||
|
||||
historical_evidence_artifacts: detailData.historical_evidence?.artifacts || 0,
|
||||
historical_evidence_literature: detailData.historical_evidence?.ancient_literature || 0,
|
||||
historical_evidence_testimony: detailData.historical_evidence?.inheritor_testimony || 0,
|
||||
historical_evidence_research: detailData.historical_evidence?.modern_research || 0,
|
||||
|
||||
offline_activities: getNum(detailData.offline_activities ?? detailData.offline_teaching_count),
|
||||
online_clicks: getWan(detailData.online_clicks), // Assuming this field exists or needs mapping
|
||||
|
||||
// Market
|
||||
platform_accounts_bilibili: detailData.platform_accounts?.bilibili?.account || '-',
|
||||
platform_accounts_douyin: detailData.platform_accounts?.douyin?.account || '-',
|
||||
price_fluctuation_min: detailData.price_fluctuation?.[0] || '-',
|
||||
price_fluctuation_max: detailData.price_fluctuation?.[1] || '-',
|
||||
monthly_transaction: detailData.monthly_transaction || detailData.monthly_transaction_amount || '-',
|
||||
circulation: detailData.circulation || detailData.scarcity_level || '-',
|
||||
|
||||
// Detailed Parameters (Assuming these keys exist in detailData or calculation_result)
|
||||
// Economic Value B1
|
||||
B11: getWan(detailData.basic_value_b11),
|
||||
F: getWan(detailData.financial_value_p),
|
||||
L: getNum(detailData.legal_strength_l),
|
||||
D: getNum(detailData.development_potential_d),
|
||||
|
||||
B12: getNum(detailData.traffic_factor_b12),
|
||||
search_index_ratio: getNum(detailData.search_index_ratio),
|
||||
S3: getNum(detailData.social_media_spread_s3),
|
||||
|
||||
B13: getNum(detailData.policy_multiplier_b13),
|
||||
policy_fit_score: getNum(detailData.policy_fit_score),
|
||||
|
||||
// Cultural Value B2
|
||||
B21: getNum(detailData.living_inheritance_b21),
|
||||
inheritor_level_score: getNum(detailData.inheritor_level_score),
|
||||
teaching_frequency_score: getNum(detailData.teaching_frequency_score),
|
||||
cooperation_depth_score: getNum(detailData.cooperation_depth_score),
|
||||
|
||||
B22: getNum(detailData.pattern_entropy_b22),
|
||||
SC: getNum(detailData.structure_complexity_sc),
|
||||
H: getNum(detailData.info_entropy_h),
|
||||
HI: getNum(detailData.history_inheritance_hi),
|
||||
|
||||
// Risk
|
||||
risk_market: getNum(detailData.risk_market),
|
||||
risk_legal: getNum(detailData.risk_legal),
|
||||
risk_inheritance: getNum(detailData.risk_inheritance),
|
||||
|
||||
// Market Verification
|
||||
C1: getWan(detailData.market_bidding_c1),
|
||||
C2: getNum(detailData.heat_coefficient_c2),
|
||||
C3: getNum(detailData.scarcity_multiplier_c3),
|
||||
C4: getNum(detailData.time_decay_c4),
|
||||
|
||||
// Current Date
|
||||
current_date: new Date().toLocaleDateString('zh-CN'),
|
||||
}
|
||||
|
||||
doc.render(data)
|
||||
|
||||
const out = doc.getZip().generate({
|
||||
type: 'blob',
|
||||
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
})
|
||||
|
||||
saveAs(out, `${detailData.asset_name || '评估报告'}.docx`)
|
||||
} catch (error) {
|
||||
console.error('Report generation failed:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
178
web/src/views/report/download/index.vue
Normal file
178
web/src/views/report/download/index.vue
Normal file
@ -0,0 +1,178 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { NCard, NButton, NResult, NSpin, useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
import { generateReport } from '@/utils/report'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const message = useMessage()
|
||||
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
const detailData = ref(null)
|
||||
const downloaded = ref(false)
|
||||
|
||||
const fetchDetail = async () => {
|
||||
const id = route.params.id || route.query.id
|
||||
if (!id) {
|
||||
error.value = '参数错误:缺少ID'
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const { data } = await api.getValuationById({ valuation_id: Number(id) })
|
||||
detailData.value = data
|
||||
} catch (err) {
|
||||
error.value = err.message || '获取数据失败'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDownload = async () => {
|
||||
if (!detailData.value) return
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
await generateReport(detailData.value)
|
||||
message.success('下载成功')
|
||||
downloaded.value = true
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
message.error('下载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="download-page">
|
||||
<div class="content-wrapper">
|
||||
<NSpin :show="loading">
|
||||
<NCard v-if="!error && detailData" class="download-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<TheIcon icon="mdi:file-document-outline" :size="32" color="#18a058" />
|
||||
<span>评估报告下载</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="info-section">
|
||||
<div class="info-item">
|
||||
<span class="label">资产名称:</span>
|
||||
<span class="value">{{ detailData.asset_name }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">评估结果:</span>
|
||||
<span class="value">¥{{ detailData.final_value_ab?.toLocaleString() || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">生成时间:</span>
|
||||
<span class="value">{{ new Date().toLocaleDateString() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-section">
|
||||
<NButton
|
||||
type="primary"
|
||||
size="large"
|
||||
:loading="loading"
|
||||
@click="handleDownload"
|
||||
class="download-btn"
|
||||
>
|
||||
<template #icon>
|
||||
<TheIcon icon="mdi:download" />
|
||||
</template>
|
||||
下载 Word 报告
|
||||
</NButton>
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
<NResult
|
||||
v-else-if="error"
|
||||
status="error"
|
||||
title="无法下载"
|
||||
:description="error"
|
||||
>
|
||||
<template #footer>
|
||||
<NButton @click="fetchDetail">重试</NButton>
|
||||
</template>
|
||||
</NResult>
|
||||
</NSpin>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.download-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f0f2f5;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
.download-card {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
margin: 24px 0;
|
||||
padding: 16px;
|
||||
background-color: #f9fafb;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-weight: 500;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.action-section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@ -327,7 +327,8 @@ const handleViewCertificate = () => {
|
||||
|
||||
certificateData.value = {
|
||||
reportFiles: formatFiles(props.detailData?.report_url),
|
||||
certificateFiles: formatFiles(props.detailData?.certificate_url)
|
||||
certificateFiles: formatFiles(props.detailData?.certificate_url),
|
||||
detailData: props.detailData
|
||||
}
|
||||
certificateModalVisible.value = true
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
// import { DownloadIcon } from '@vicons/tabler'
|
||||
|
||||
import { getToken } from '@/utils/auth/token'
|
||||
import { generateReport } from '@/utils/report'
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
@ -162,10 +163,15 @@ const isUploadMode = computed(() => props.mode === 'upload')
|
||||
|
||||
|
||||
// 下载报告
|
||||
const handleDownloadReport = () => {
|
||||
// 这里实现下载原版报告的逻辑
|
||||
console.log('下载原版报告')
|
||||
// TODO: 实现实际的下载功能
|
||||
const handleDownloadReport = async () => {
|
||||
try {
|
||||
message.loading('正在生成报告...')
|
||||
await generateReport(props.certificateData.detailData)
|
||||
message.success('报告生成并下载成功')
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
message.error('报告生成失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 文件预览
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user