Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

113 linhas
3.4 KiB

  1. /**
  2. * AI 取名结果解析(从 ai-name 页面抽出,供创作页与详情弹窗共用)
  3. *
  4. * 后端返回的 content 可能是:
  5. * 1) JSON({ summary, names: [] })
  6. * 2) JSON Lines(每行一个 {type:'summary'|'name'})
  7. * 3) 【名字】+ 拼音/评分/出处… 的标记文本
  8. */
  9. export function parseNameResult(content) {
  10. if (!content) return { summary: '', names: [] }
  11. let text = String(content).trim()
  12. .replace(/^```json\s*/i, '')
  13. .replace(/^```\s*/i, '')
  14. .replace(/```$/i, '')
  15. .trim()
  16. const line = parseJsonLines(text)
  17. if (line.names.length) return line
  18. const marked = parseMarkedText(text)
  19. if (marked.names.length) return marked
  20. const s = text.indexOf('{')
  21. const e = text.lastIndexOf('}')
  22. if (s >= 0 && e > s) text = text.substring(s, e + 1)
  23. try {
  24. const d = JSON.parse(text)
  25. return { summary: d.summary || '', names: Array.isArray(d.names) ? d.names : [] }
  26. } catch {
  27. return { summary: text, names: [] }
  28. }
  29. }
  30. function parseJsonLines(text) {
  31. const d = { summary: '', names: [] }
  32. String(text || '').split(/\r?\n/).forEach((l) => {
  33. l = l.trim()
  34. if (!l) return
  35. try {
  36. const i = JSON.parse(l)
  37. if (i.type === 'summary') d.summary = i.content || ''
  38. if (i.type === 'name' && i.data) d.names.push(i.data)
  39. } catch { /* 忽略非 JSON 行 */ }
  40. })
  41. return d
  42. }
  43. function parseMarkedText(text) {
  44. const d = { summary: '', names: [] }
  45. const v = String(text || '')
  46. const sm = v.match(/整体建议[::]([\s\S]*?)(?=\n\s*【|$)/)
  47. if (sm) d.summary = sm[1].trim()
  48. const reg = /【([^】]+)】([\s\S]*?)(?=\n\s*【|$)/g
  49. let m
  50. while ((m = reg.exec(v))) {
  51. const body = m[2] || ''
  52. const name = resolveNameBlockTitle(m[1], body)
  53. d.names.push({
  54. name,
  55. pinyin: matchField(body, '拼音'),
  56. score: parseInt(matchField(body, '评分')) || 90,
  57. source: matchField(body, '出处'),
  58. wuxing: matchField(body, '五行'),
  59. meaning: matchField(body, '寓意'),
  60. tone: matchField(body, '音律'),
  61. reason: matchField(body, '推荐'),
  62. })
  63. }
  64. d.names = d.names.filter(i => i.name).slice(0, 5)
  65. return d
  66. }
  67. function resolveNameBlockTitle(title, body) {
  68. const n = String(title || '').trim()
  69. if (!['姓名', '名字', '候选名'].includes(n)) return n
  70. const fr = /^(拼音|评分|出处|五行|寓意|音律|推荐)[::]/
  71. const line = String(body || '').split(/\r?\n/).map(i => i.trim()).find(i => i && !fr.test(i))
  72. return line ? line.replace(/^姓名[::]/, '').trim() : n
  73. }
  74. function matchField(body, label) {
  75. const r = new RegExp(`${label}[::]([^\\n\\r]*)`)
  76. const m = String(body || '').match(r)
  77. return m ? m[1].trim() : ''
  78. }
  79. /** 名字详情展示字段 */
  80. export const NAME_FIELDS = [
  81. { key: 'source', label: '出处' },
  82. { key: 'wuxing', label: '五行' },
  83. { key: 'meaning', label: '寓意' },
  84. { key: 'tone', label: '音律' },
  85. { key: 'reason', label: '推荐' },
  86. ]
  87. /** 把名字结果拼成可复制文本 */
  88. export function buildNameCopyText(result) {
  89. const names = result?.names || []
  90. if (!names.length) return ''
  91. const lines = []
  92. if (result.summary) lines.push(result.summary)
  93. names.forEach((item) => {
  94. lines.push(`${item.name}${item.score ? `(${item.score}分)` : ''}`)
  95. NAME_FIELDS.forEach((f) => {
  96. if (item[f.key]) lines.push(`${f.label}:${item[f.key]}`)
  97. })
  98. if (item.pinyin) lines.push(`拼音:${item.pinyin}`)
  99. lines.push('')
  100. })
  101. return lines.join('\n')
  102. }