|
|
@@ -493,6 +493,166 @@ const svgToPngDataUrl = async (sourceSvg) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+const getScoreCellValue = (value = '', label) => {
|
|
|
+ const text = String(value).trim()
|
|
|
+ const prefix = new RegExp(`^${label}[::]\\s*`)
|
|
|
+ return text.replace(prefix, '') || '未评分'
|
|
|
+}
|
|
|
+
|
|
|
+const createScoreTableCell = (tagName, text, style) => {
|
|
|
+ const cell = document.createElement(tagName)
|
|
|
+ cell.textContent = text
|
|
|
+ Object.assign(cell.style, style)
|
|
|
+ return cell
|
|
|
+}
|
|
|
+
|
|
|
+const createWordScoreTable = (scoreList) => {
|
|
|
+ const table = document.createElement('table')
|
|
|
+ table.className = 'markdown-score-detail-table'
|
|
|
+ table.setAttribute('border', '1')
|
|
|
+ table.setAttribute('cellspacing', '0')
|
|
|
+ table.setAttribute('cellpadding', '0')
|
|
|
+ table.setAttribute('width', '440')
|
|
|
+ Object.assign(table.style, {
|
|
|
+ width: '440px',
|
|
|
+ borderCollapse: 'collapse',
|
|
|
+ border: '1px solid #A9C1D8',
|
|
|
+ tableLayout: 'fixed',
|
|
|
+ margin: '8px auto 14px',
|
|
|
+ color: '#1F2937',
|
|
|
+ fontFamily: 'Microsoft YaHei, SimSun, sans-serif',
|
|
|
+ fontSize: '10.5pt',
|
|
|
+ lineHeight: '1.45',
|
|
|
+ msoTableLspace: '0pt',
|
|
|
+ msoTableRspace: '0pt'
|
|
|
+ })
|
|
|
+
|
|
|
+ const headerRow = document.createElement('tr')
|
|
|
+ const headerStyle = {
|
|
|
+ padding: '6px 8px',
|
|
|
+ border: '1px solid #A9C1D8',
|
|
|
+ backgroundColor: '#DCEAF7',
|
|
|
+ color: '#1F4E78',
|
|
|
+ fontWeight: 'bold',
|
|
|
+ textAlign: 'center',
|
|
|
+ verticalAlign: 'middle'
|
|
|
+ }
|
|
|
+ headerRow.append(
|
|
|
+ createScoreTableCell('th', '评分维度', { ...headerStyle, width: '56%' }),
|
|
|
+ createScoreTableCell('th', 'AI评分', { ...headerStyle, width: '22%' }),
|
|
|
+ createScoreTableCell('th', '专家评分', { ...headerStyle, width: '22%' })
|
|
|
+ )
|
|
|
+
|
|
|
+ const thead = document.createElement('thead')
|
|
|
+ thead.appendChild(headerRow)
|
|
|
+ const tbody = document.createElement('tbody')
|
|
|
+ const rows = [...scoreList.children]
|
|
|
+ rows.forEach((row, index) => {
|
|
|
+ const dimension = row.querySelector('span')?.textContent?.trim() || '未命名维度'
|
|
|
+ const scores = [...row.querySelectorAll('b')].map((node) => node.textContent || '')
|
|
|
+ const aiScore = getScoreCellValue(scores.find((score) => /^AI[::]/.test(score.trim())) || '', 'AI')
|
|
|
+ const expertScore = getScoreCellValue(scores.find((score) => /^专家[::]/.test(score.trim())) || '', '专家')
|
|
|
+ const tr = document.createElement('tr')
|
|
|
+ const cellStyle = {
|
|
|
+ padding: '6px 8px',
|
|
|
+ border: '1px solid #A9C1D8',
|
|
|
+ backgroundColor: index % 2 === 0 ? '#FFFFFF' : '#F6FAFE',
|
|
|
+ verticalAlign: 'middle'
|
|
|
+ }
|
|
|
+ tr.append(
|
|
|
+ createScoreTableCell('td', dimension, { ...cellStyle, textAlign: 'left' }),
|
|
|
+ createScoreTableCell('td', aiScore, { ...cellStyle, textAlign: 'center' }),
|
|
|
+ createScoreTableCell('td', expertScore, { ...cellStyle, textAlign: 'center' })
|
|
|
+ )
|
|
|
+ tbody.appendChild(tr)
|
|
|
+ })
|
|
|
+ table.append(thead, tbody)
|
|
|
+ return table
|
|
|
+}
|
|
|
+
|
|
|
+const prepareRadarForWord = (copyNode) => {
|
|
|
+ copyNode.querySelectorAll('.markdown-score-radar').forEach((radar) => {
|
|
|
+ Object.assign(radar.style, {
|
|
|
+ width: '460px',
|
|
|
+ maxWidth: '100%',
|
|
|
+ margin: '14px auto',
|
|
|
+ padding: '0',
|
|
|
+ border: '0',
|
|
|
+ background: '#FFFFFF',
|
|
|
+ pageBreakInside: 'avoid',
|
|
|
+ textAlign: 'center'
|
|
|
+ })
|
|
|
+
|
|
|
+ const header = radar.querySelector('.markdown-score-radar__header')
|
|
|
+ if (header) {
|
|
|
+ Object.assign(header.style, {
|
|
|
+ display: 'block',
|
|
|
+ margin: '0 0 4px',
|
|
|
+ padding: '0',
|
|
|
+ border: '0',
|
|
|
+ color: '#1F4E78',
|
|
|
+ fontFamily: 'Microsoft YaHei, SimSun, sans-serif',
|
|
|
+ textAlign: 'left'
|
|
|
+ })
|
|
|
+ const title = header.querySelector('strong')
|
|
|
+ if (title) Object.assign(title.style, { display: 'block', fontSize: '12pt', fontWeight: 'bold', lineHeight: '1.5' })
|
|
|
+ const subtitle = header.querySelector('small')
|
|
|
+ if (subtitle) Object.assign(subtitle.style, { display: 'block', color: '#64748B', fontSize: '9pt', lineHeight: '1.45' })
|
|
|
+ }
|
|
|
+
|
|
|
+ // The table header already explains the two series, so omit the web-only legend.
|
|
|
+ radar.querySelector('.markdown-score-radar__legend')?.remove()
|
|
|
+ const scoreList = radar.querySelector('.markdown-score-radar__score-list')
|
|
|
+ if (scoreList) scoreList.replaceWith(createWordScoreTable(scoreList))
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const prepareOrderedListsForWord = (copyNode) => {
|
|
|
+ copyNode.querySelectorAll('ol').forEach((list) => {
|
|
|
+ const start = Number.parseInt(list.getAttribute('start') || '1', 10) || 1
|
|
|
+ const items = [...list.children].filter((item) => item.tagName === 'LI')
|
|
|
+ const fragment = document.createDocumentFragment()
|
|
|
+
|
|
|
+ items.forEach((item, index) => {
|
|
|
+ const lines = (item.textContent || '')
|
|
|
+ .split(/\r?\n/)
|
|
|
+ .map((line) => line.trim())
|
|
|
+ .filter(Boolean)
|
|
|
+ if (!lines.length) return
|
|
|
+
|
|
|
+ const title = document.createElement('p')
|
|
|
+ const isReportSection = /^(分项打分明细表格|综合点评|AI评分数据)/.test(lines[0])
|
|
|
+ title.textContent = `${start + index}. ${lines.shift()}`
|
|
|
+ Object.assign(title.style, {
|
|
|
+ margin: isReportSection ? '12px 0 5px' : '6px 0 3px',
|
|
|
+ color: isReportSection ? '#1F4E78' : '#1F2937',
|
|
|
+ fontFamily: 'Microsoft YaHei, SimSun, sans-serif',
|
|
|
+ fontSize: isReportSection ? '11pt' : '10.5pt',
|
|
|
+ fontWeight: isReportSection ? 'bold' : 'normal',
|
|
|
+ lineHeight: '1.55',
|
|
|
+ textAlign: 'left'
|
|
|
+ })
|
|
|
+ fragment.appendChild(title)
|
|
|
+
|
|
|
+ if (lines.length) {
|
|
|
+ const description = document.createElement('p')
|
|
|
+ description.textContent = lines.join(' ')
|
|
|
+ Object.assign(description.style, {
|
|
|
+ margin: '0 0 8px',
|
|
|
+ color: '#1F2937',
|
|
|
+ fontFamily: 'Microsoft YaHei, SimSun, sans-serif',
|
|
|
+ fontSize: '10.5pt',
|
|
|
+ lineHeight: '1.65',
|
|
|
+ textAlign: 'left'
|
|
|
+ })
|
|
|
+ fragment.appendChild(description)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ list.replaceWith(fragment)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
const getRichCopyHtml = async (trigger) => {
|
|
|
const markdownNode = trigger?.closest('.message-card')?.querySelector('.markdown-view')
|
|
|
if (!markdownNode) return null
|
|
|
@@ -505,12 +665,18 @@ const getRichCopyHtml = async (trigger) => {
|
|
|
const image = document.createElement('img')
|
|
|
image.src = dataUrl
|
|
|
image.alt = '评分雷达图'
|
|
|
- Object.assign(image.style, { display: 'block', width: '560px', maxWidth: '100%', height: 'auto', margin: '8px auto' })
|
|
|
+ image.setAttribute('width', '360')
|
|
|
+ image.setAttribute('height', '300')
|
|
|
+ Object.assign(image.style, { display: 'block', width: '360px', height: '300px', margin: '6px auto 8px', border: '0' })
|
|
|
copiedCharts[index]?.replaceWith(image)
|
|
|
}
|
|
|
- copyNode.querySelectorAll('table').forEach((table) => Object.assign(table.style, { width: '100%', borderCollapse: 'collapse', border: '1px solid #B7C3D4' }))
|
|
|
- copyNode.querySelectorAll('th').forEach((cell) => Object.assign(cell.style, { padding: '8px 10px', border: '1px solid #B7C3D4', backgroundColor: '#E8F0FC', fontWeight: 'bold', textAlign: 'left' }))
|
|
|
- copyNode.querySelectorAll('td').forEach((cell) => Object.assign(cell.style, { padding: '8px 10px', border: '1px solid #B7C3D4', textAlign: 'left', verticalAlign: 'top' }))
|
|
|
+ prepareOrderedListsForWord(copyNode)
|
|
|
+ prepareRadarForWord(copyNode)
|
|
|
+ copyNode.querySelectorAll('table:not(.markdown-score-detail-table)').forEach((table) => {
|
|
|
+ Object.assign(table.style, { width: '100%', borderCollapse: 'collapse', border: '1px solid #B7C3D4' })
|
|
|
+ table.querySelectorAll('th').forEach((cell) => Object.assign(cell.style, { padding: '8px 10px', border: '1px solid #B7C3D4', backgroundColor: '#E8F0FC', fontWeight: 'bold', textAlign: 'left' }))
|
|
|
+ table.querySelectorAll('td').forEach((cell) => Object.assign(cell.style, { padding: '8px 10px', border: '1px solid #B7C3D4', textAlign: 'left', verticalAlign: 'top' }))
|
|
|
+ })
|
|
|
return copyNode.innerHTML
|
|
|
}
|
|
|
|