diff --git a/web/package.json b/web/package.json index 9888866..78a8bb4 100644 --- a/web/package.json +++ b/web/package.json @@ -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", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 51d615f..47273ed 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -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 diff --git a/web/public/report_template.docx b/web/public/report_template.docx new file mode 100644 index 0000000..82a661a Binary files /dev/null and b/web/public/report_template.docx differ diff --git a/web/src/router/guard/auth-guard.js b/web/src/router/guard/auth-guard.js index 230e75a..5d6cb1f 100644 --- a/web/src/router/guard/auth-guard.js +++ b/web/src/router/guard/auth-guard.js @@ -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 } } } diff --git a/web/src/router/routes/index.js b/web/src/router/routes/index.js index cd07ee3..0d96f2e 100644 --- a/web/src/router/routes/index.js +++ b/web/src/router/routes/index.js @@ -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', diff --git a/web/src/utils/report.js b/web/src/utils/report.js new file mode 100644 index 0000000..7f7b4f4 --- /dev/null +++ b/web/src/utils/report.js @@ -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 + } +} diff --git a/web/src/views/report/download/index.vue b/web/src/views/report/download/index.vue new file mode 100644 index 0000000..1008dad --- /dev/null +++ b/web/src/views/report/download/index.vue @@ -0,0 +1,178 @@ + + + + + diff --git a/web/src/views/valuation/audit/components/AuditDetail.vue b/web/src/views/valuation/audit/components/AuditDetail.vue index 9d931c4..64c0ad6 100644 --- a/web/src/views/valuation/audit/components/AuditDetail.vue +++ b/web/src/views/valuation/audit/components/AuditDetail.vue @@ -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 } diff --git a/web/src/views/valuation/audit/components/CertificateModal.vue b/web/src/views/valuation/audit/components/CertificateModal.vue index aeac525..0cce982 100644 --- a/web/src/views/valuation/audit/components/CertificateModal.vue +++ b/web/src/views/valuation/audit/components/CertificateModal.vue @@ -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('报告生成失败') + } } // 文件预览