feat(order): 实现订单总结内容的实时流式渲染
- 替换订单总结接口响应类型为object,支持事件流格式 - 采用EventSource实现订单总结的SSE连接,支持服务器推送数据 - 实现打字机效果,实时显示总结文本的追加内容 - 使用marked解析Markdown格式摘要内容,支持GFM和换行 - 使用DOMPurify清理生成HTML,保障内容安全,仅允许部分标签 - 增加实时生成中状态提示和闪烁的输入光标动画 - 添加超时机制和连接错误处理,保证用户提示友好 - 组件卸载时自动关闭SSE连接,防止资源泄漏 - 更新依赖,新增dompurify和marked及相关类型定义 - 调整ESLint和构建配置支持新增脚本模式
This commit is contained in:
@@ -11,7 +11,8 @@
|
|||||||
"Bash(pnpm eslint:*)",
|
"Bash(pnpm eslint:*)",
|
||||||
"Bash(mkdir:*)",
|
"Bash(mkdir:*)",
|
||||||
"Bash(pnpm build)",
|
"Bash(pnpm build)",
|
||||||
"Bash(pnpm eslint:fix)"
|
"Bash(pnpm eslint:fix)",
|
||||||
|
"Bash(pnpm eslint:fix:*)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
@@ -61,8 +61,10 @@
|
|||||||
"codemirror": "^6.0.2",
|
"codemirror": "^6.0.2",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
|
"dompurify": "^3.3.0",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"marked": "^17.0.1",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.3.1",
|
"pinia": "^2.3.1",
|
||||||
@@ -83,7 +85,9 @@
|
|||||||
"@openapi-codegen/cli": "^3.1.0",
|
"@openapi-codegen/cli": "^3.1.0",
|
||||||
"@openapi-codegen/typescript": "^11.0.1",
|
"@openapi-codegen/typescript": "^11.0.1",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
|
"@types/dompurify": "^3.2.0",
|
||||||
"@types/lodash": "^4.17.20",
|
"@types/lodash": "^4.17.20",
|
||||||
|
"@types/marked": "^6.0.0",
|
||||||
"@types/mockjs": "^1.0.10",
|
"@types/mockjs": "^1.0.10",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.14.1",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
|
|||||||
77
pnpm-lock.yaml
generated
77
pnpm-lock.yaml
generated
@@ -66,12 +66,18 @@ importers:
|
|||||||
dayjs:
|
dayjs:
|
||||||
specifier: ^1.11.19
|
specifier: ^1.11.19
|
||||||
version: 1.11.19
|
version: 1.11.19
|
||||||
|
dompurify:
|
||||||
|
specifier: ^3.3.0
|
||||||
|
version: 3.3.0
|
||||||
echarts:
|
echarts:
|
||||||
specifier: ^5.6.0
|
specifier: ^5.6.0
|
||||||
version: 5.6.0
|
version: 5.6.0
|
||||||
lodash:
|
lodash:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
|
marked:
|
||||||
|
specifier: ^17.0.1
|
||||||
|
version: 17.0.1
|
||||||
mitt:
|
mitt:
|
||||||
specifier: ^3.0.1
|
specifier: ^3.0.1
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
@@ -127,9 +133,15 @@ importers:
|
|||||||
'@types/crypto-js':
|
'@types/crypto-js':
|
||||||
specifier: ^4.2.2
|
specifier: ^4.2.2
|
||||||
version: 4.2.2
|
version: 4.2.2
|
||||||
|
'@types/dompurify':
|
||||||
|
specifier: ^3.2.0
|
||||||
|
version: 3.2.0
|
||||||
'@types/lodash':
|
'@types/lodash':
|
||||||
specifier: ^4.17.20
|
specifier: ^4.17.20
|
||||||
version: 4.17.20
|
version: 4.17.20
|
||||||
|
'@types/marked':
|
||||||
|
specifier: ^6.0.0
|
||||||
|
version: 6.0.0
|
||||||
'@types/mockjs':
|
'@types/mockjs':
|
||||||
specifier: ^1.0.10
|
specifier: ^1.0.10
|
||||||
version: 1.0.10
|
version: 1.0.10
|
||||||
@@ -156,7 +168,7 @@ importers:
|
|||||||
version: 4.1.2(vite@5.4.19(@types/node@22.15.18)(less@4.3.0))(vue@3.5.22(typescript@5.8.3))
|
version: 4.1.2(vite@5.4.19(@types/node@22.15.18)(less@4.3.0))(vue@3.5.22(typescript@5.8.3))
|
||||||
autoprefixer:
|
autoprefixer:
|
||||||
specifier: ^10.4.21
|
specifier: ^10.4.21
|
||||||
version: 10.4.21(postcss@8.5.3)
|
version: 10.4.21(postcss@8.5.6)
|
||||||
consola:
|
consola:
|
||||||
specifier: ^3.4.2
|
specifier: ^3.4.2
|
||||||
version: 3.4.2
|
version: 3.4.2
|
||||||
@@ -192,7 +204,7 @@ importers:
|
|||||||
version: 1.8.0
|
version: 1.8.0
|
||||||
postcss-less:
|
postcss-less:
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.0(postcss@8.5.3)
|
version: 6.0.0(postcss@8.5.6)
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.5.3
|
specifier: ^3.5.3
|
||||||
version: 3.5.3
|
version: 3.5.3
|
||||||
@@ -213,13 +225,13 @@ importers:
|
|||||||
version: 37.0.0(stylelint@16.19.1(typescript@5.8.3))
|
version: 37.0.0(stylelint@16.19.1(typescript@5.8.3))
|
||||||
stylelint-config-standard-less:
|
stylelint-config-standard-less:
|
||||||
specifier: ^3.0.1
|
specifier: ^3.0.1
|
||||||
version: 3.0.1(postcss@8.5.3)(stylelint@16.19.1(typescript@5.8.3))
|
version: 3.0.1(postcss@8.5.6)(stylelint@16.19.1(typescript@5.8.3))
|
||||||
stylelint-config-standard-vue:
|
stylelint-config-standard-vue:
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0(postcss-html@1.8.0)(stylelint@16.19.1(typescript@5.8.3))
|
version: 1.0.0(postcss-html@1.8.0)(stylelint@16.19.1(typescript@5.8.3))
|
||||||
stylelint-less:
|
stylelint-less:
|
||||||
specifier: ^3.0.1
|
specifier: ^3.0.1
|
||||||
version: 3.0.1(postcss@8.5.3)(stylelint@16.19.1(typescript@5.8.3))
|
version: 3.0.1(postcss@8.5.6)(stylelint@16.19.1(typescript@5.8.3))
|
||||||
stylelint-order:
|
stylelint-order:
|
||||||
specifier: ^6.0.4
|
specifier: ^6.0.4
|
||||||
version: 6.0.4(stylelint@16.19.1(typescript@5.8.3))
|
version: 6.0.4(stylelint@16.19.1(typescript@5.8.3))
|
||||||
@@ -1491,6 +1503,10 @@ packages:
|
|||||||
'@types/debug@4.1.12':
|
'@types/debug@4.1.12':
|
||||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||||
|
|
||||||
|
'@types/dompurify@3.2.0':
|
||||||
|
resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==}
|
||||||
|
deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.
|
||||||
|
|
||||||
'@types/estree@1.0.7':
|
'@types/estree@1.0.7':
|
||||||
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
||||||
|
|
||||||
@@ -1542,6 +1558,10 @@ packages:
|
|||||||
'@types/lodash@4.17.20':
|
'@types/lodash@4.17.20':
|
||||||
resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==}
|
resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==}
|
||||||
|
|
||||||
|
'@types/marked@6.0.0':
|
||||||
|
resolution: {integrity: sha512-jmjpa4BwUsmhxcfsgUit/7A9KbrC48Q0q8KvnY107ogcjGgTFDlIL3RpihNpx2Mu1hM4mdFQjoVc4O6JoGKHsA==}
|
||||||
|
deprecated: This is a stub types definition. marked provides its own type definitions, so you do not need this installed.
|
||||||
|
|
||||||
'@types/mdast@4.0.4':
|
'@types/mdast@4.0.4':
|
||||||
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
||||||
|
|
||||||
@@ -2428,8 +2448,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
dompurify@3.2.5:
|
dompurify@3.3.0:
|
||||||
resolution: {integrity: sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==}
|
resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==}
|
||||||
|
|
||||||
domutils@2.8.0:
|
domutils@2.8.0:
|
||||||
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
|
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
|
||||||
@@ -3772,6 +3792,11 @@ packages:
|
|||||||
markdown-table@3.0.4:
|
markdown-table@3.0.4:
|
||||||
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
||||||
|
|
||||||
|
marked@17.0.1:
|
||||||
|
resolution: {integrity: sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==}
|
||||||
|
engines: {node: '>= 20'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
math-intrinsics@1.1.0:
|
math-intrinsics@1.1.0:
|
||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -6684,7 +6709,7 @@ snapshots:
|
|||||||
'@types/lodash.debounce': 4.0.9
|
'@types/lodash.debounce': 4.0.9
|
||||||
'@types/lodash.throttle': 4.1.9
|
'@types/lodash.throttle': 4.1.9
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
dompurify: 3.2.5
|
dompurify: 3.3.0
|
||||||
lodash.debounce: 4.0.8
|
lodash.debounce: 4.0.8
|
||||||
lodash.throttle: 4.1.1
|
lodash.throttle: 4.1.1
|
||||||
nanoid: 5.1.5
|
nanoid: 5.1.5
|
||||||
@@ -7314,6 +7339,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/ms': 2.1.0
|
'@types/ms': 2.1.0
|
||||||
|
|
||||||
|
'@types/dompurify@3.2.0':
|
||||||
|
dependencies:
|
||||||
|
dompurify: 3.3.0
|
||||||
|
|
||||||
'@types/estree@1.0.7': {}
|
'@types/estree@1.0.7': {}
|
||||||
|
|
||||||
'@types/glob@7.2.0':
|
'@types/glob@7.2.0':
|
||||||
@@ -7374,6 +7403,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/lodash@4.17.20': {}
|
'@types/lodash@4.17.20': {}
|
||||||
|
|
||||||
|
'@types/marked@6.0.0':
|
||||||
|
dependencies:
|
||||||
|
marked: 17.0.1
|
||||||
|
|
||||||
'@types/mdast@4.0.4':
|
'@types/mdast@4.0.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
@@ -7758,14 +7791,14 @@ snapshots:
|
|||||||
|
|
||||||
asynckit@0.4.0: {}
|
asynckit@0.4.0: {}
|
||||||
|
|
||||||
autoprefixer@10.4.21(postcss@8.5.3):
|
autoprefixer@10.4.21(postcss@8.5.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.24.5
|
browserslist: 4.24.5
|
||||||
caniuse-lite: 1.0.30001718
|
caniuse-lite: 1.0.30001718
|
||||||
fraction.js: 4.3.7
|
fraction.js: 4.3.7
|
||||||
normalize-range: 0.1.2
|
normalize-range: 0.1.2
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
postcss: 8.5.3
|
postcss: 8.5.6
|
||||||
postcss-value-parser: 4.2.0
|
postcss-value-parser: 4.2.0
|
||||||
|
|
||||||
axios@1.12.2:
|
axios@1.12.2:
|
||||||
@@ -8404,7 +8437,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
domelementtype: 2.3.0
|
domelementtype: 2.3.0
|
||||||
|
|
||||||
dompurify@3.2.5:
|
dompurify@3.3.0:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/trusted-types': 2.0.7
|
'@types/trusted-types': 2.0.7
|
||||||
|
|
||||||
@@ -9872,6 +9905,8 @@ snapshots:
|
|||||||
|
|
||||||
markdown-table@3.0.4: {}
|
markdown-table@3.0.4: {}
|
||||||
|
|
||||||
|
marked@17.0.1: {}
|
||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
mathml-tag-names@2.1.3: {}
|
mathml-tag-names@2.1.3: {}
|
||||||
@@ -10678,9 +10713,9 @@ snapshots:
|
|||||||
postcss: 8.5.3
|
postcss: 8.5.3
|
||||||
postcss-safe-parser: 6.0.0(postcss@8.5.3)
|
postcss-safe-parser: 6.0.0(postcss@8.5.3)
|
||||||
|
|
||||||
postcss-less@6.0.0(postcss@8.5.3):
|
postcss-less@6.0.0(postcss@8.5.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
postcss: 8.5.3
|
postcss: 8.5.6
|
||||||
|
|
||||||
postcss-resolve-nested-selector@0.1.6: {}
|
postcss-resolve-nested-selector@0.1.6: {}
|
||||||
|
|
||||||
@@ -11389,14 +11424,14 @@ snapshots:
|
|||||||
stylelint: 16.19.1(typescript@5.8.3)
|
stylelint: 16.19.1(typescript@5.8.3)
|
||||||
stylelint-order: 6.0.4(stylelint@16.19.1(typescript@5.8.3))
|
stylelint-order: 6.0.4(stylelint@16.19.1(typescript@5.8.3))
|
||||||
|
|
||||||
stylelint-config-recommended-less@3.0.1(postcss@8.5.3)(stylelint@16.19.1(typescript@5.8.3)):
|
stylelint-config-recommended-less@3.0.1(postcss@8.5.6)(stylelint@16.19.1(typescript@5.8.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
postcss-less: 6.0.0(postcss@8.5.3)
|
postcss-less: 6.0.0(postcss@8.5.6)
|
||||||
stylelint: 16.19.1(typescript@5.8.3)
|
stylelint: 16.19.1(typescript@5.8.3)
|
||||||
stylelint-config-recommended: 14.0.1(stylelint@16.19.1(typescript@5.8.3))
|
stylelint-config-recommended: 14.0.1(stylelint@16.19.1(typescript@5.8.3))
|
||||||
stylelint-less: 3.0.1(postcss@8.5.3)(stylelint@16.19.1(typescript@5.8.3))
|
stylelint-less: 3.0.1(postcss@8.5.6)(stylelint@16.19.1(typescript@5.8.3))
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
postcss: 8.5.3
|
postcss: 8.5.6
|
||||||
|
|
||||||
stylelint-config-recommended-vue@1.6.0(postcss-html@1.8.0)(stylelint@16.19.1(typescript@5.8.3)):
|
stylelint-config-recommended-vue@1.6.0(postcss-html@1.8.0)(stylelint@16.19.1(typescript@5.8.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11418,13 +11453,13 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
stylelint: 16.19.1(typescript@5.8.3)
|
stylelint: 16.19.1(typescript@5.8.3)
|
||||||
|
|
||||||
stylelint-config-standard-less@3.0.1(postcss@8.5.3)(stylelint@16.19.1(typescript@5.8.3)):
|
stylelint-config-standard-less@3.0.1(postcss@8.5.6)(stylelint@16.19.1(typescript@5.8.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
stylelint: 16.19.1(typescript@5.8.3)
|
stylelint: 16.19.1(typescript@5.8.3)
|
||||||
stylelint-config-recommended-less: 3.0.1(postcss@8.5.3)(stylelint@16.19.1(typescript@5.8.3))
|
stylelint-config-recommended-less: 3.0.1(postcss@8.5.6)(stylelint@16.19.1(typescript@5.8.3))
|
||||||
stylelint-config-standard: 35.0.0(stylelint@16.19.1(typescript@5.8.3))
|
stylelint-config-standard: 35.0.0(stylelint@16.19.1(typescript@5.8.3))
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
postcss: 8.5.3
|
postcss: 8.5.6
|
||||||
|
|
||||||
stylelint-config-standard-vue@1.0.0(postcss-html@1.8.0)(stylelint@16.19.1(typescript@5.8.3)):
|
stylelint-config-standard-vue@1.0.0(postcss-html@1.8.0)(stylelint@16.19.1(typescript@5.8.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11444,9 +11479,9 @@ snapshots:
|
|||||||
stylelint: 16.19.1(typescript@5.8.3)
|
stylelint: 16.19.1(typescript@5.8.3)
|
||||||
stylelint-config-recommended: 15.0.0(stylelint@16.19.1(typescript@5.8.3))
|
stylelint-config-recommended: 15.0.0(stylelint@16.19.1(typescript@5.8.3))
|
||||||
|
|
||||||
stylelint-less@3.0.1(postcss@8.5.3)(stylelint@16.19.1(typescript@5.8.3)):
|
stylelint-less@3.0.1(postcss@8.5.6)(stylelint@16.19.1(typescript@5.8.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
postcss: 8.5.3
|
postcss: 8.5.6
|
||||||
postcss-resolve-nested-selector: 0.1.6
|
postcss-resolve-nested-selector: 0.1.6
|
||||||
postcss-value-parser: 4.2.0
|
postcss-value-parser: 4.2.0
|
||||||
stylelint: 16.19.1(typescript@5.8.3)
|
stylelint: 16.19.1(typescript@5.8.3)
|
||||||
|
|||||||
@@ -338,7 +338,6 @@ docs/KamiApiMerchantV1OrderQueryRecord.md
|
|||||||
docs/KamiApiMerchantV1OrderQueryReq.md
|
docs/KamiApiMerchantV1OrderQueryReq.md
|
||||||
docs/KamiApiMerchantV1OrderQueryRes.md
|
docs/KamiApiMerchantV1OrderQueryRes.md
|
||||||
docs/KamiApiMerchantV1OrderQuerySummaryReq.md
|
docs/KamiApiMerchantV1OrderQuerySummaryReq.md
|
||||||
docs/KamiApiMerchantV1OrderQuerySummaryRes.md
|
|
||||||
docs/KamiApiMerchantV1PlatformRateRecord.md
|
docs/KamiApiMerchantV1PlatformRateRecord.md
|
||||||
docs/KamiApiMerchantV1SampleAllListRes.md
|
docs/KamiApiMerchantV1SampleAllListRes.md
|
||||||
docs/KamiApiMerchantV1StealCreateReq.md
|
docs/KamiApiMerchantV1StealCreateReq.md
|
||||||
@@ -770,7 +769,6 @@ models/kami-api-merchant-v1-order-query-record.ts
|
|||||||
models/kami-api-merchant-v1-order-query-req.ts
|
models/kami-api-merchant-v1-order-query-req.ts
|
||||||
models/kami-api-merchant-v1-order-query-res.ts
|
models/kami-api-merchant-v1-order-query-res.ts
|
||||||
models/kami-api-merchant-v1-order-query-summary-req.ts
|
models/kami-api-merchant-v1-order-query-summary-req.ts
|
||||||
models/kami-api-merchant-v1-order-query-summary-res.ts
|
|
||||||
models/kami-api-merchant-v1-platform-rate-record.ts
|
models/kami-api-merchant-v1-platform-rate-record.ts
|
||||||
models/kami-api-merchant-v1-sample-all-list-res.ts
|
models/kami-api-merchant-v1-sample-all-list-res.ts
|
||||||
models/kami-api-merchant-v1-steal-create-req.ts
|
models/kami-api-merchant-v1-steal-create-req.ts
|
||||||
|
|||||||
@@ -307,8 +307,6 @@ import type { KamiApiMerchantV1MerchantSampleAllListRes } from '../models';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import type { KamiApiMerchantV1OrderQueryRes } from '../models';
|
import type { KamiApiMerchantV1OrderQueryRes } from '../models';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import type { KamiApiMerchantV1OrderQuerySummaryRes } from '../models';
|
|
||||||
// @ts-ignore
|
|
||||||
import type { KamiApiMerchantV1StealCreateReq } from '../models';
|
import type { KamiApiMerchantV1StealCreateReq } from '../models';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import type { KamiApiMerchantV1StealListRes } from '../models';
|
import type { KamiApiMerchantV1StealListRes } from '../models';
|
||||||
@@ -15860,10 +15858,7 @@ export const DefaultApiFp = function (configuration?: Configuration) {
|
|||||||
merchantOrderNo?: string,
|
merchantOrderNo?: string,
|
||||||
options?: RawAxiosRequestConfig
|
options?: RawAxiosRequestConfig
|
||||||
): Promise<
|
): Promise<
|
||||||
(
|
(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>
|
||||||
axios?: AxiosInstance,
|
|
||||||
basePath?: string
|
|
||||||
) => AxiosPromise<KamiApiMerchantV1OrderQuerySummaryRes>
|
|
||||||
> {
|
> {
|
||||||
const localVarAxiosArgs =
|
const localVarAxiosArgs =
|
||||||
await localVarAxiosParamCreator.apiMerchantOrderSummaryGet(
|
await localVarAxiosParamCreator.apiMerchantOrderSummaryGet(
|
||||||
@@ -20459,7 +20454,7 @@ export const DefaultApiFactory = function (
|
|||||||
apiMerchantOrderSummaryGet(
|
apiMerchantOrderSummaryGet(
|
||||||
requestParameters: DefaultApiApiMerchantOrderSummaryGetRequest = {},
|
requestParameters: DefaultApiApiMerchantOrderSummaryGetRequest = {},
|
||||||
options?: RawAxiosRequestConfig
|
options?: RawAxiosRequestConfig
|
||||||
): AxiosPromise<KamiApiMerchantV1OrderQuerySummaryRes> {
|
): AxiosPromise<object> {
|
||||||
return localVarFp
|
return localVarFp
|
||||||
.apiMerchantOrderSummaryGet(requestParameters.merchantOrderNo, options)
|
.apiMerchantOrderSummaryGet(requestParameters.merchantOrderNo, options)
|
||||||
.then(request => request(axios, basePath));
|
.then(request => request(axios, basePath));
|
||||||
@@ -23159,7 +23154,7 @@ export interface DefaultApiInterface {
|
|||||||
apiMerchantOrderSummaryGet(
|
apiMerchantOrderSummaryGet(
|
||||||
requestParameters?: DefaultApiApiMerchantOrderSummaryGetRequest,
|
requestParameters?: DefaultApiApiMerchantOrderSummaryGetRequest,
|
||||||
options?: RawAxiosRequestConfig
|
options?: RawAxiosRequestConfig
|
||||||
): AxiosPromise<KamiApiMerchantV1OrderQuerySummaryRes>;
|
): AxiosPromise<object>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -7150,7 +7150,7 @@ No authorization required
|
|||||||
|
|
||||||
# **apiMerchantOrderSummaryGet**
|
# **apiMerchantOrderSummaryGet**
|
||||||
|
|
||||||
> KamiApiMerchantV1OrderQuerySummaryRes apiMerchantOrderSummaryGet()
|
> object apiMerchantOrderSummaryGet()
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -7174,7 +7174,7 @@ const { status, data } =
|
|||||||
|
|
||||||
### Return type
|
### Return type
|
||||||
|
|
||||||
**KamiApiMerchantV1OrderQuerySummaryRes**
|
**object**
|
||||||
|
|
||||||
### Authorization
|
### Authorization
|
||||||
|
|
||||||
@@ -7183,7 +7183,7 @@ No authorization required
|
|||||||
### HTTP request headers
|
### HTTP request headers
|
||||||
|
|
||||||
- **Content-Type**: Not defined
|
- **Content-Type**: Not defined
|
||||||
- **Accept**: application/json
|
- **Accept**: text/event-stream
|
||||||
|
|
||||||
### HTTP response details
|
### HTTP response details
|
||||||
|
|
||||||
|
|||||||
@@ -317,7 +317,6 @@ export * from './kami-api-merchant-v1-order-query-record';
|
|||||||
export * from './kami-api-merchant-v1-order-query-req';
|
export * from './kami-api-merchant-v1-order-query-req';
|
||||||
export * from './kami-api-merchant-v1-order-query-res';
|
export * from './kami-api-merchant-v1-order-query-res';
|
||||||
export * from './kami-api-merchant-v1-order-query-summary-req';
|
export * from './kami-api-merchant-v1-order-query-summary-req';
|
||||||
export * from './kami-api-merchant-v1-order-query-summary-res';
|
|
||||||
export * from './kami-api-merchant-v1-platform-rate-record';
|
export * from './kami-api-merchant-v1-platform-rate-record';
|
||||||
export * from './kami-api-merchant-v1-sample-all-list-res';
|
export * from './kami-api-merchant-v1-sample-all-list-res';
|
||||||
export * from './kami-api-merchant-v1-steal-create-req';
|
export * from './kami-api-merchant-v1-steal-create-req';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a-typography-title style="text-align: center">
|
<a-typography-title style="text-align: center">
|
||||||
@@ -91,12 +92,23 @@
|
|||||||
:bordered="false"
|
:bordered="false"
|
||||||
style="background-color: var(--color-fill-2)"
|
style="background-color: var(--color-fill-2)"
|
||||||
>
|
>
|
||||||
<a-typography-paragraph
|
<!-- 实时生成状态提示 -->
|
||||||
style="margin: 0; line-height: 1.6"
|
<div
|
||||||
:deep="false"
|
v-if="isTyping"
|
||||||
|
style="text-align: right; margin-bottom: 8px"
|
||||||
>
|
>
|
||||||
{{ summaryData.message }}
|
<a-tag color="green" size="small">
|
||||||
</a-typography-paragraph>
|
<icon-sync />
|
||||||
|
实时生成中
|
||||||
|
</a-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="summary-content"
|
||||||
|
style="margin: 0; line-height: 1.6"
|
||||||
|
v-html="formattedSummaryHtml"
|
||||||
|
></div>
|
||||||
|
<span v-if="isTyping" class="typing-cursor">|</span>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,16 +150,20 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { apiClient } from '@/api';
|
import { apiClient } from '@/api';
|
||||||
import {
|
import { KamiApiMerchantV1OrderQueryRes } from '@/api/generated';
|
||||||
KamiApiMerchantV1OrderQueryRes,
|
|
||||||
KamiApiMerchantV1OrderQuerySummaryRes
|
// 定义总结数据的类型
|
||||||
} from '@/api/generated';
|
interface OrderSummaryRes {
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { reactive, ref, computed } from 'vue';
|
import DOMPurify from 'dompurify';
|
||||||
|
import { marked } from 'marked';
|
||||||
|
import { onUnmounted, reactive, ref, computed } from 'vue';
|
||||||
|
|
||||||
const renderData = ref<KamiApiMerchantV1OrderQueryRes>({});
|
const renderData = ref<KamiApiMerchantV1OrderQueryRes>({});
|
||||||
const summaryData = ref<KamiApiMerchantV1OrderQuerySummaryRes | null>(null);
|
const summaryData = ref<OrderSummaryRes | null>(null);
|
||||||
const { loading, setLoading } = useLoading(false);
|
const { loading, setLoading } = useLoading(false);
|
||||||
const { loading: summaryLoading, setLoading: setSummaryLoading } =
|
const { loading: summaryLoading, setLoading: setSummaryLoading } =
|
||||||
useLoading(false);
|
useLoading(false);
|
||||||
@@ -161,24 +177,164 @@ const isMobile = computed(() => {
|
|||||||
return window.innerWidth <= 768;
|
return window.innerWidth <= 768;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 存储 SSE 连接实例,用于清理
|
||||||
|
const currentEventSource = ref<EventSource | null>(null);
|
||||||
|
|
||||||
|
// 打字机状态管理
|
||||||
|
const isTyping = ref(false); // 是否正在流式接收数据
|
||||||
|
const hasStartedTyping = ref(false); // 是否已经开始打字机效果
|
||||||
|
|
||||||
|
// 配置 marked 选项
|
||||||
|
marked.setOptions({
|
||||||
|
breaks: true, // 支持换行
|
||||||
|
gfm: true // 启用 GitHub Flavored Markdown
|
||||||
|
});
|
||||||
|
|
||||||
|
// 同步版本的格式化函数(用于流式显示)
|
||||||
|
const formatSummaryTextSync = (text: string): string => {
|
||||||
|
if (!text) return '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用同步版本的 marked
|
||||||
|
const html = marked.parse(text) as string;
|
||||||
|
// 使用 DOMPurify 清理 HTML,只允许安全的标签
|
||||||
|
return DOMPurify.sanitize(html, {
|
||||||
|
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'b', 'i', 'ul', 'ol', 'li'],
|
||||||
|
ALLOWED_ATTR: []
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Markdown 解析错误:', error);
|
||||||
|
// 降级处理:返回纯文本,只处理基本格式并清理
|
||||||
|
const basicHtml = text
|
||||||
|
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') // 粗体
|
||||||
|
.replace(/\*(.*?)\*/g, '<em>$1</em>') // 斜体
|
||||||
|
.replace(/\n/g, '<br>'); // 换行
|
||||||
|
|
||||||
|
return DOMPurify.sanitize(basicHtml, {
|
||||||
|
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'b', 'i'],
|
||||||
|
ALLOWED_ATTR: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算属性:格式化后的总结内容
|
||||||
|
// 使用 DOMPurify 清理后的 HTML,确保安全
|
||||||
|
const formattedSummaryHtml = computed(() => {
|
||||||
|
return summaryData.value?.message
|
||||||
|
? formatSummaryTextSync(summaryData.value.message)
|
||||||
|
: '';
|
||||||
|
});
|
||||||
|
|
||||||
const fetchSummary = async (merchantOrderNo: string) => {
|
const fetchSummary = async (merchantOrderNo: string) => {
|
||||||
if (!merchantOrderNo) return;
|
if (!merchantOrderNo) return;
|
||||||
|
|
||||||
|
// 清理之前的连接
|
||||||
|
if (currentEventSource.value) {
|
||||||
|
currentEventSource.value.close();
|
||||||
|
currentEventSource.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setSummaryLoading(true);
|
setSummaryLoading(true);
|
||||||
summaryData.value = null;
|
summaryData.value = null;
|
||||||
const summaryResponse = await apiClient.apiMerchantOrderSummaryGet({
|
isTyping.value = false;
|
||||||
merchantOrderNo
|
hasStartedTyping.value = false;
|
||||||
});
|
|
||||||
summaryData.value = summaryResponse.data;
|
// 创建 SSE 连接
|
||||||
|
const baseUrl =
|
||||||
|
import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:12401';
|
||||||
|
const sseUrl = `${baseUrl}/api/merchant/order/summary?merchantOrderNo=${encodeURIComponent(merchantOrderNo)}`;
|
||||||
|
|
||||||
|
const eventSource = new EventSource(sseUrl);
|
||||||
|
currentEventSource.value = eventSource;
|
||||||
|
|
||||||
|
eventSource.onopen = () => {
|
||||||
|
console.log('SSE 连接已建立');
|
||||||
|
};
|
||||||
|
|
||||||
|
eventSource.onmessage = event => {
|
||||||
|
try {
|
||||||
|
console.log('收到 SSE 数据:', event.data);
|
||||||
|
|
||||||
|
// EventSource 会自动处理 "data: " 前缀,event.data 已经是解析后的内容
|
||||||
|
// 直接解析 JSON
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
|
||||||
|
if (data.content) {
|
||||||
|
// 第一次接收到数据,开始打字机效果
|
||||||
|
if (!hasStartedTyping.value) {
|
||||||
|
hasStartedTyping.value = true;
|
||||||
|
isTyping.value = true;
|
||||||
|
setSummaryLoading(false); // 结束 loading 状态
|
||||||
|
summaryData.value = { message: data.content };
|
||||||
|
} else {
|
||||||
|
// 累积流式数据
|
||||||
|
summaryData.value.message += data.content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否完成 - 根据新的格式可能需要调整
|
||||||
|
if (data.complete || data.finished || data.done) {
|
||||||
|
eventSource.close();
|
||||||
|
currentEventSource.value = null;
|
||||||
|
isTyping.value = false; // 停止打字机效果
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
console.error(
|
||||||
|
'解析 SSE 数据失败:',
|
||||||
|
parseError,
|
||||||
|
'原始数据:',
|
||||||
|
event.data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
eventSource.onerror = error => {
|
||||||
|
console.error('SSE 连接错误:', error);
|
||||||
|
eventSource.close();
|
||||||
|
currentEventSource.value = null;
|
||||||
|
setSummaryLoading(false);
|
||||||
|
isTyping.value = false;
|
||||||
|
hasStartedTyping.value = false;
|
||||||
|
|
||||||
|
// 如果没有收到任何数据,显示错误提示
|
||||||
|
if (!summaryData.value?.message) {
|
||||||
|
Message.warning('订单总结获取失败,请稍后重试');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置超时
|
||||||
|
setTimeout(() => {
|
||||||
|
if (eventSource.readyState !== EventSource.CLOSED) {
|
||||||
|
eventSource.close();
|
||||||
|
currentEventSource.value = null;
|
||||||
|
setSummaryLoading(false);
|
||||||
|
isTyping.value = false;
|
||||||
|
hasStartedTyping.value = false;
|
||||||
|
console.log('SSE 连接超时');
|
||||||
|
if (!summaryData.value?.message) {
|
||||||
|
Message.warning('订单总结获取超时,请稍后重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 30000); // 30秒超时
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取订单总结失败:', error);
|
console.error('建立 SSE 连接失败:', error);
|
||||||
Message.warning('订单总结获取失败,请稍后重试');
|
|
||||||
} finally {
|
|
||||||
setSummaryLoading(false);
|
setSummaryLoading(false);
|
||||||
|
currentEventSource.value = null;
|
||||||
|
isTyping.value = false;
|
||||||
|
hasStartedTyping.value = false;
|
||||||
|
Message.warning('订单总结获取失败,请稍后重试');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 组件卸载时清理 SSE 连接
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (currentEventSource.value) {
|
||||||
|
currentEventSource.value.close();
|
||||||
|
currentEventSource.value = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const search = async (value: string) => {
|
const search = async (value: string) => {
|
||||||
try {
|
try {
|
||||||
state.value = value;
|
state.value = value;
|
||||||
@@ -208,6 +364,18 @@ const search = async (value: string) => {
|
|||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.container {
|
.container {
|
||||||
|
@keyframes blink {
|
||||||
|
0%,
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
51%,
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
padding: 200px 20px;
|
padding: 200px 20px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@@ -357,6 +525,33 @@ const search = async (value: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 流式输入光标动画
|
||||||
|
.typing-cursor {
|
||||||
|
display: inline-block;
|
||||||
|
width: 2px;
|
||||||
|
height: 1.2em;
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
margin-left: 2px;
|
||||||
|
animation: blink 1s infinite;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化总结内容样式
|
||||||
|
.summary-content {
|
||||||
|
p {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.arco-input-wrapper) {
|
:deep(.arco-input-wrapper) {
|
||||||
background-color: var(--color-white);
|
background-color: var(--color-white);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user