Эх сурвалжийг харах

强生demo2:新增复制雷达图到word功能

liyanbo 4 долоо хоног өмнө
parent
commit
5f237fd343

+ 170 - 4
src/views/qsfl/components/AiQAChat2.vue

@@ -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
 }