Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
sdream-ai-fe
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
侯明涛
sdream-ai-fe
Commits
e879c818
Commit
e879c818
authored
May 15, 2026
by
Liu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: 按钮切换&&问答逻辑
parent
66d1d2fd
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1511 additions
and
267 deletions
+1511
-267
.npmrc
+5
-0
pnpm-lock.yaml
+820
-4
src/api/home.ts
+1
-1
src/components/ChatEditor/index.tsx
+122
-127
src/layouts/HistoryBar/components/HistoryBarList/index.tsx
+6
-4
src/layouts/Navbar/Navbar.tsx
+18
-15
src/lib/toolRoleRecordTypes.ts
+151
-0
src/lib/workflowToolParams.ts
+119
-0
src/pages/Chat/Chat.tsx
+175
-60
src/pages/Chat/components/ChatItem/ChatAnswerRecommend.tsx
+6
-9
src/pages/Chat/components/ChatWelcome/index.tsx
+10
-18
src/pages/Chat/helper.ts
+33
-1
src/pages/Home/HomeNew.tsx
+23
-28
src/types/toolList.ts
+22
-0
No files found.
.npmrc
View file @
e879c818
public-hoist-pattern[]=*@heroui/*
# On Apple Silicon, optional native deps (e.g. @rspack/binding-*) follow the arch
# of the Node that ran `pnpm install`. If you later run Node as x64 (Rosetta),
# rsbuild needs @rspack/binding-darwin-x64 as well — install both CPU variants.
supportedArchitectures.cpu=x64,arm64
pnpm-lock.yaml
View file @
e879c818
...
...
@@ -38,6 +38,9 @@ importers:
github-markdown-css
:
specifier
:
^5.8.0
version
:
5.8.1
mermaid
:
specifier
:
^11.13.0
version
:
11.15.0
pdfjs-dist
:
specifier
:
^5.4.149
version
:
5.4.149
...
...
@@ -132,6 +135,9 @@ importers:
'
@types/react-dom'
:
specifier
:
^18.3.0
version
:
18.3.7(@types/react@18.3.24)
baseline-browser-mapping
:
specifier
:
^2.8.31
version
:
2.10.29
eslint
:
specifier
:
^8.57.0
version
:
8.57.1
...
...
@@ -212,6 +218,9 @@ packages:
'
@antfu/install-pkg@0.4.1'
:
resolution
:
{
integrity
:
sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==
}
'
@antfu/install-pkg@1.1.0'
:
resolution
:
{
integrity
:
sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==
}
'
@antfu/utils@0.7.10'
:
resolution
:
{
integrity
:
sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==
}
...
...
@@ -294,6 +303,12 @@ packages:
resolution
:
{
integrity
:
sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==
}
engines
:
{
node
:
'
>=6.9.0'
}
'
@braintree/sanitize-url@7.1.2'
:
resolution
:
{
integrity
:
sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==
}
'
@chevrotain/types@11.1.2'
:
resolution
:
{
integrity
:
sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==
}
'
@clack/core@0.3.5'
:
resolution
:
{
integrity
:
sha512-5cfhQNH+1VQ2xLQlmzXMqUoiaH0lRBq9/CLW9lTyMbuKLC3+xEK01tHVvyut++mLOn5urSHmkm6I0Lg9MaJSTQ==
}
...
...
@@ -959,6 +974,12 @@ packages:
resolution
:
{
integrity
:
sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
}
deprecated
:
Use @eslint/object-schema instead
'
@iconify/types@2.0.0'
:
resolution
:
{
integrity
:
sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==
}
'
@iconify/utils@3.1.3'
:
resolution
:
{
integrity
:
sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==
}
'
@internationalized/date@3.6.0'
:
resolution
:
{
integrity
:
sha512-+z6ti+CcJnRlLHok/emGEsWQhe7kfSmEW+/6qCzvKY67YPh7YOBfvc7+/+NXq+zJlbArg30tYpqLjNgcAYv2YQ==
}
...
...
@@ -1005,6 +1026,9 @@ packages:
'
@jridgewell/trace-mapping@0.3.31'
:
resolution
:
{
integrity
:
sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
}
'
@mermaid-js/parser@1.1.1'
:
resolution
:
{
integrity
:
sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==
}
'
@module-federation/runtime-tools@0.5.1'
:
resolution
:
{
integrity
:
sha512-nfBedkoZ3/SWyO0hnmaxuz0R0iGPSikHZOAZ0N/dVSQaIzlffUo35B5nlC2wgWIc0JdMZfkwkjZRrnuuDIJbzg==
}
...
...
@@ -2055,6 +2079,99 @@ packages:
'
@types/cors@2.8.19'
:
resolution
:
{
integrity
:
sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==
}
'
@types/d3-array@3.2.2'
:
resolution
:
{
integrity
:
sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==
}
'
@types/d3-axis@3.0.6'
:
resolution
:
{
integrity
:
sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==
}
'
@types/d3-brush@3.0.6'
:
resolution
:
{
integrity
:
sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==
}
'
@types/d3-chord@3.0.6'
:
resolution
:
{
integrity
:
sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==
}
'
@types/d3-color@3.1.3'
:
resolution
:
{
integrity
:
sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==
}
'
@types/d3-contour@3.0.6'
:
resolution
:
{
integrity
:
sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==
}
'
@types/d3-delaunay@6.0.4'
:
resolution
:
{
integrity
:
sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==
}
'
@types/d3-dispatch@3.0.7'
:
resolution
:
{
integrity
:
sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==
}
'
@types/d3-drag@3.0.7'
:
resolution
:
{
integrity
:
sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==
}
'
@types/d3-dsv@3.0.7'
:
resolution
:
{
integrity
:
sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==
}
'
@types/d3-ease@3.0.2'
:
resolution
:
{
integrity
:
sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==
}
'
@types/d3-fetch@3.0.7'
:
resolution
:
{
integrity
:
sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==
}
'
@types/d3-force@3.0.10'
:
resolution
:
{
integrity
:
sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==
}
'
@types/d3-format@3.0.4'
:
resolution
:
{
integrity
:
sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==
}
'
@types/d3-geo@3.1.0'
:
resolution
:
{
integrity
:
sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==
}
'
@types/d3-hierarchy@3.1.7'
:
resolution
:
{
integrity
:
sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==
}
'
@types/d3-interpolate@3.0.4'
:
resolution
:
{
integrity
:
sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==
}
'
@types/d3-path@3.1.1'
:
resolution
:
{
integrity
:
sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==
}
'
@types/d3-polygon@3.0.2'
:
resolution
:
{
integrity
:
sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==
}
'
@types/d3-quadtree@3.0.6'
:
resolution
:
{
integrity
:
sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==
}
'
@types/d3-random@3.0.3'
:
resolution
:
{
integrity
:
sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==
}
'
@types/d3-scale-chromatic@3.1.0'
:
resolution
:
{
integrity
:
sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==
}
'
@types/d3-scale@4.0.9'
:
resolution
:
{
integrity
:
sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==
}
'
@types/d3-selection@3.0.11'
:
resolution
:
{
integrity
:
sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==
}
'
@types/d3-shape@3.1.8'
:
resolution
:
{
integrity
:
sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==
}
'
@types/d3-time-format@4.0.3'
:
resolution
:
{
integrity
:
sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==
}
'
@types/d3-time@3.0.4'
:
resolution
:
{
integrity
:
sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==
}
'
@types/d3-timer@3.0.2'
:
resolution
:
{
integrity
:
sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==
}
'
@types/d3-transition@3.0.9'
:
resolution
:
{
integrity
:
sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==
}
'
@types/d3-zoom@3.0.8'
:
resolution
:
{
integrity
:
sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==
}
'
@types/d3@7.4.3'
:
resolution
:
{
integrity
:
sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==
}
'
@types/debug@4.1.12'
:
resolution
:
{
integrity
:
sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==
}
...
...
@@ -2073,6 +2190,9 @@ packages:
'
@types/estree@1.0.8'
:
resolution
:
{
integrity
:
sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
}
'
@types/geojson@7946.0.16'
:
resolution
:
{
integrity
:
sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==
}
'
@types/hast@3.0.4'
:
resolution
:
{
integrity
:
sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==
}
...
...
@@ -2117,6 +2237,9 @@ packages:
'
@types/tapable@2.2.7'
:
resolution
:
{
integrity
:
sha512-D6QzACV9vNX3r8HQQNTOnpG+Bv1rko+yEA82wKs3O9CQ5+XW7HI7TED17/UE7+5dIxyxZIWTxKbsBeF6uKFCwA==
}
'
@types/trusted-types@2.0.7'
:
resolution
:
{
integrity
:
sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
}
'
@types/unist@2.0.11'
:
resolution
:
{
integrity
:
sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==
}
...
...
@@ -2309,6 +2432,9 @@ packages:
cpu
:
[
x64
]
os
:
[
win32
]
'
@upsetjs/venn.js@2.0.0'
:
resolution
:
{
integrity
:
sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==
}
'
@vitest/eslint-plugin@1.3.12'
:
resolution
:
{
integrity
:
sha512-cSEyUYGj8j8SLqKrzN7BlfsJ3wG67eRT25819PXuyoSBogLXiyagdKx4MHWHV1zv+EEuyMXsEKkBEKzXpxyBrg==
}
peerDependencies
:
...
...
@@ -2508,8 +2634,9 @@ packages:
resolution
:
{
integrity
:
sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==
}
engines
:
{
node
:
^4.5.0 || >= 5.9
}
baseline-browser-mapping@2.8.6
:
resolution
:
{
integrity
:
sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==
}
baseline-browser-mapping@2.10.29
:
resolution
:
{
integrity
:
sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==
}
engines
:
{
node
:
'
>=6.0.0'
}
hasBin
:
true
big.js@5.2.2
:
...
...
@@ -2706,6 +2833,10 @@ packages:
resolution
:
{
integrity
:
sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
}
engines
:
{
node
:
'
>=
10'
}
commander@8.3.0
:
resolution
:
{
integrity
:
sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
}
engines
:
{
node
:
'
>=
12'
}
comment-parser@1.4.1
:
resolution
:
{
integrity
:
sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==
}
engines
:
{
node
:
'
>=
12.0.0'
}
...
...
@@ -2750,6 +2881,12 @@ packages:
resolution
:
{
integrity
:
sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
}
engines
:
{
node
:
'
>=
0.10'
}
cose-base@1.0.3
:
resolution
:
{
integrity
:
sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==
}
cose-base@2.2.0
:
resolution
:
{
integrity
:
sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==
}
cosmiconfig@8.3.6
:
resolution
:
{
integrity
:
sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==
}
engines
:
{
node
:
'
>=14'
}
...
...
@@ -2795,6 +2932,162 @@ packages:
csstype@3.1.3
:
resolution
:
{
integrity
:
sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
}
cytoscape-cose-bilkent@4.1.0
:
resolution
:
{
integrity
:
sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==
}
peerDependencies
:
cytoscape
:
^3.2.0
cytoscape-fcose@2.2.0
:
resolution
:
{
integrity
:
sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==
}
peerDependencies
:
cytoscape
:
^3.2.0
cytoscape@3.33.3
:
resolution
:
{
integrity
:
sha512-Gej7U+OKR+LZ8kvX7rb2HhCYJ0IhvEFsnkud4SB1PR+BUY/TsSO0dmOW59WEVLu51b1Rm+gQRKoz4bLYxGSZ2g==
}
engines
:
{
node
:
'
>=0.10'
}
d3-array@2.12.1
:
resolution
:
{
integrity
:
sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==
}
d3-array@3.2.4
:
resolution
:
{
integrity
:
sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
}
engines
:
{
node
:
'
>=12'
}
d3-axis@3.0.0
:
resolution
:
{
integrity
:
sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==
}
engines
:
{
node
:
'
>=12'
}
d3-brush@3.0.0
:
resolution
:
{
integrity
:
sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==
}
engines
:
{
node
:
'
>=12'
}
d3-chord@3.0.1
:
resolution
:
{
integrity
:
sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==
}
engines
:
{
node
:
'
>=12'
}
d3-color@3.1.0
:
resolution
:
{
integrity
:
sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
}
engines
:
{
node
:
'
>=12'
}
d3-contour@4.0.2
:
resolution
:
{
integrity
:
sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==
}
engines
:
{
node
:
'
>=12'
}
d3-delaunay@6.0.4
:
resolution
:
{
integrity
:
sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==
}
engines
:
{
node
:
'
>=12'
}
d3-dispatch@3.0.1
:
resolution
:
{
integrity
:
sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
}
engines
:
{
node
:
'
>=12'
}
d3-drag@3.0.0
:
resolution
:
{
integrity
:
sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
}
engines
:
{
node
:
'
>=12'
}
d3-dsv@3.0.1
:
resolution
:
{
integrity
:
sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==
}
engines
:
{
node
:
'
>=12'
}
hasBin
:
true
d3-ease@3.0.1
:
resolution
:
{
integrity
:
sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
}
engines
:
{
node
:
'
>=12'
}
d3-fetch@3.0.1
:
resolution
:
{
integrity
:
sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==
}
engines
:
{
node
:
'
>=12'
}
d3-force@3.0.0
:
resolution
:
{
integrity
:
sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==
}
engines
:
{
node
:
'
>=12'
}
d3-format@3.1.2
:
resolution
:
{
integrity
:
sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==
}
engines
:
{
node
:
'
>=12'
}
d3-geo@3.1.1
:
resolution
:
{
integrity
:
sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==
}
engines
:
{
node
:
'
>=12'
}
d3-hierarchy@3.1.2
:
resolution
:
{
integrity
:
sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==
}
engines
:
{
node
:
'
>=12'
}
d3-interpolate@3.0.1
:
resolution
:
{
integrity
:
sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
}
engines
:
{
node
:
'
>=12'
}
d3-path@1.0.9
:
resolution
:
{
integrity
:
sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==
}
d3-path@3.1.0
:
resolution
:
{
integrity
:
sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==
}
engines
:
{
node
:
'
>=12'
}
d3-polygon@3.0.1
:
resolution
:
{
integrity
:
sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==
}
engines
:
{
node
:
'
>=12'
}
d3-quadtree@3.0.1
:
resolution
:
{
integrity
:
sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==
}
engines
:
{
node
:
'
>=12'
}
d3-random@3.0.1
:
resolution
:
{
integrity
:
sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==
}
engines
:
{
node
:
'
>=12'
}
d3-sankey@0.12.3
:
resolution
:
{
integrity
:
sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==
}
d3-scale-chromatic@3.1.0
:
resolution
:
{
integrity
:
sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==
}
engines
:
{
node
:
'
>=12'
}
d3-scale@4.0.2
:
resolution
:
{
integrity
:
sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
}
engines
:
{
node
:
'
>=12'
}
d3-selection@3.0.0
:
resolution
:
{
integrity
:
sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
}
engines
:
{
node
:
'
>=12'
}
d3-shape@1.3.7
:
resolution
:
{
integrity
:
sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==
}
d3-shape@3.2.0
:
resolution
:
{
integrity
:
sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==
}
engines
:
{
node
:
'
>=12'
}
d3-time-format@4.1.0
:
resolution
:
{
integrity
:
sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
}
engines
:
{
node
:
'
>=12'
}
d3-time@3.1.0
:
resolution
:
{
integrity
:
sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
}
engines
:
{
node
:
'
>=12'
}
d3-timer@3.0.1
:
resolution
:
{
integrity
:
sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
}
engines
:
{
node
:
'
>=12'
}
d3-transition@3.0.1
:
resolution
:
{
integrity
:
sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
}
engines
:
{
node
:
'
>=12'
}
peerDependencies
:
d3-selection
:
2 - 3
d3-zoom@3.0.0
:
resolution
:
{
integrity
:
sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
}
engines
:
{
node
:
'
>=12'
}
d3@7.9.0
:
resolution
:
{
integrity
:
sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==
}
engines
:
{
node
:
'
>=12'
}
dagre-d3-es@7.0.14
:
resolution
:
{
integrity
:
sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==
}
date-fns@3.6.0
:
resolution
:
{
integrity
:
sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
}
...
...
@@ -2804,6 +3097,9 @@ packages:
dayjs@1.11.18
:
resolution
:
{
integrity
:
sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==
}
dayjs@1.11.20
:
resolution
:
{
integrity
:
sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==
}
debounce@1.2.1
:
resolution
:
{
integrity
:
sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
}
...
...
@@ -2860,6 +3156,9 @@ packages:
defu@6.1.4
:
resolution
:
{
integrity
:
sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==
}
delaunator@5.1.0
:
resolution
:
{
integrity
:
sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==
}
delayed-stream@1.0.0
:
resolution
:
{
integrity
:
sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
}
engines
:
{
node
:
'
>=0.4.0'
}
...
...
@@ -2902,6 +3201,9 @@ packages:
resolution
:
{
integrity
:
sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
}
engines
:
{
node
:
'
>=
4'
}
dompurify@3.4.3
:
resolution
:
{
integrity
:
sha512-VVwJidIJcp1hpg2OMXML3ZVRPYSZiq4aX7qBh83BSIpOaRDqI+qxhXjjIWnpzkOXhmp0L81lnoME1mnCc9H48A==
}
domutils@3.2.2
:
resolution
:
{
integrity
:
sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==
}
...
...
@@ -2999,6 +3301,9 @@ packages:
resolution
:
{
integrity
:
sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
}
engines
:
{
node
:
'
>=
0.4'
}
es-toolkit@1.46.1
:
resolution
:
{
integrity
:
sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==
}
escalade@3.2.0
:
resolution
:
{
integrity
:
sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
}
engines
:
{
node
:
'
>=6'
}
...
...
@@ -3546,6 +3851,9 @@ packages:
resolution
:
{
integrity
:
sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==
}
engines
:
{
node
:
'
>=10'
}
hachure-fill@0.5.2
:
resolution
:
{
integrity
:
sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==
}
has-flag@3.0.0
:
resolution
:
{
integrity
:
sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
}
engines
:
{
node
:
'
>=4'
}
...
...
@@ -3617,6 +3925,10 @@ packages:
resolution
:
{
integrity
:
sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
}
engines
:
{
node
:
'
>=0.10.0'
}
iconv-lite@0.6.3
:
resolution
:
{
integrity
:
sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
}
engines
:
{
node
:
'
>=0.10.0'
}
ignore@5.3.2
:
resolution
:
{
integrity
:
sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
}
engines
:
{
node
:
'
>=
4'
}
...
...
@@ -3635,6 +3947,9 @@ packages:
resolution
:
{
integrity
:
sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==
}
engines
:
{
node
:
'
>=6'
}
import-meta-resolve@4.2.0
:
resolution
:
{
integrity
:
sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==
}
imurmurhash@0.1.4
:
resolution
:
{
integrity
:
sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
}
engines
:
{
node
:
'
>=0.8.19'
}
...
...
@@ -3659,6 +3974,13 @@ packages:
react
:
^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
react-dom
:
^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
internmap@1.0.1
:
resolution
:
{
integrity
:
sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==
}
internmap@2.0.3
:
resolution
:
{
integrity
:
sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
}
engines
:
{
node
:
'
>=12'
}
intersection-observer@0.12.2
:
resolution
:
{
integrity
:
sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==
}
...
...
@@ -3846,9 +4168,22 @@ packages:
jszip@3.10.1
:
resolution
:
{
integrity
:
sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
}
katex@0.16.46
:
resolution
:
{
integrity
:
sha512-WHy4Coo+bGZyH7NwJKHkS04YFsFcarWbAEOAC3EMndzdN6VSZqklLLIgfxzyaW9jDoeGYJX9SWbJPKpecox0Uw==
}
hasBin
:
true
keyv@4.5.4
:
resolution
:
{
integrity
:
sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
}
khroma@2.1.0
:
resolution
:
{
integrity
:
sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==
}
layout-base@1.0.2
:
resolution
:
{
integrity
:
sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==
}
layout-base@2.0.1
:
resolution
:
{
integrity
:
sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==
}
levn@0.4.1
:
resolution
:
{
integrity
:
sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
}
engines
:
{
node
:
'
>=
0.8.0'
}
...
...
@@ -3896,6 +4231,9 @@ packages:
resolution
:
{
integrity
:
sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
}
engines
:
{
node
:
'
>=10'
}
lodash-es@4.18.1
:
resolution
:
{
integrity
:
sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==
}
lodash.merge@4.6.2
:
resolution
:
{
integrity
:
sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
}
...
...
@@ -3934,6 +4272,11 @@ packages:
markdown-table@3.0.4
:
resolution
:
{
integrity
:
sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==
}
marked@16.4.2
:
resolution
:
{
integrity
:
sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==
}
engines
:
{
node
:
'
>=
20'
}
hasBin
:
true
math-intrinsics@1.1.0
:
resolution
:
{
integrity
:
sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
}
engines
:
{
node
:
'
>=
0.4'
}
...
...
@@ -4014,6 +4357,9 @@ packages:
resolution
:
{
integrity
:
sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
}
engines
:
{
node
:
'
>=
8'
}
mermaid@11.15.0
:
resolution
:
{
integrity
:
sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==
}
micromark-core-commonmark@2.0.3
:
resolution
:
{
integrity
:
sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==
}
...
...
@@ -4279,6 +4625,9 @@ packages:
package-manager-detector@0.2.11
:
resolution
:
{
integrity
:
sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==
}
package-manager-detector@1.6.0
:
resolution
:
{
integrity
:
sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==
}
pako@1.0.11
:
resolution
:
{
integrity
:
sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
}
...
...
@@ -4316,6 +4665,9 @@ packages:
path-browserify@1.0.1
:
resolution
:
{
integrity
:
sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
}
path-data-parser@0.1.0
:
resolution
:
{
integrity
:
sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==
}
path-exists@4.0.0
:
resolution
:
{
integrity
:
sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
}
engines
:
{
node
:
'
>=8'
}
...
...
@@ -4388,6 +4740,12 @@ packages:
resolution
:
{
integrity
:
sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
}
engines
:
{
node
:
'
>=4'
}
points-on-curve@0.2.0
:
resolution
:
{
integrity
:
sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==
}
points-on-path@0.2.1
:
resolution
:
{
integrity
:
sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==
}
popper.js@1.16.1
:
resolution
:
{
integrity
:
sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
}
deprecated
:
You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1
...
...
@@ -4710,12 +5068,21 @@ packages:
deprecated
:
Rimraf versions prior to v4 are no longer supported
hasBin
:
true
robust-predicates@3.0.3
:
resolution
:
{
integrity
:
sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==
}
roughjs@4.6.6
:
resolution
:
{
integrity
:
sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==
}
rslog@1.2.11
:
resolution
:
{
integrity
:
sha512-YgMMzQf6lL9q4rD9WS/lpPWxVNJ1ttY9+dOXJ0+7vJrKCAOT4GH0EiRnBi9mKOitcHiOwjqJPV1n/HRqqgZmOQ==
}
run-parallel@1.2.0
:
resolution
:
{
integrity
:
sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
}
rw@1.3.3
:
resolution
:
{
integrity
:
sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==
}
safe-buffer@5.1.2
:
resolution
:
{
integrity
:
sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
}
...
...
@@ -4946,6 +5313,9 @@ packages:
style-to-object@1.0.9
:
resolution
:
{
integrity
:
sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==
}
stylis@4.4.0
:
resolution
:
{
integrity
:
sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==
}
sucrase@3.35.0
:
resolution
:
{
integrity
:
sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==
}
engines
:
{
node
:
'
>=16
||
14
>=14.17'
}
...
...
@@ -5054,6 +5424,10 @@ packages:
tinyexec@0.3.2
:
resolution
:
{
integrity
:
sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==
}
tinyexec@1.1.2
:
resolution
:
{
integrity
:
sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==
}
engines
:
{
node
:
'
>=18'
}
to-regex-range@5.0.1
:
resolution
:
{
integrity
:
sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
}
engines
:
{
node
:
'
>=8.0'
}
...
...
@@ -5093,6 +5467,10 @@ packages:
peerDependencies
:
typescript
:
'
>=4.0.0'
ts-dedent@2.2.0
:
resolution
:
{
integrity
:
sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==
}
engines
:
{
node
:
'
>=6.10'
}
ts-interface-checker@0.1.13
:
resolution
:
{
integrity
:
sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
}
...
...
@@ -5224,6 +5602,10 @@ packages:
resolution
:
{
integrity
:
sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
}
engines
:
{
node
:
'
>=
0.4.0'
}
uuid@14.0.0
:
resolution
:
{
integrity
:
sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==
}
hasBin
:
true
validate-npm-package-license@3.0.4
:
resolution
:
{
integrity
:
sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
}
...
...
@@ -5438,6 +5820,11 @@ snapshots:
package-manager-detector
:
0.2.11
tinyexec
:
0.3.2
'
@antfu/install-pkg@1.1.0'
:
dependencies
:
package-manager-detector
:
1.6.0
tinyexec
:
1.1.2
'
@antfu/utils@0.7.10'
:
{}
'
@babel/code-frame@7.24.7'
:
...
...
@@ -5554,6 +5941,10 @@ snapshots:
'
@babel/helper-string-parser'
:
7.27.1
'
@babel/helper-validator-identifier'
:
7.27.1
'
@braintree/sanitize-url@7.1.2'
:
{}
'
@chevrotain/types@11.1.2'
:
{}
'
@clack/core@0.3.5'
:
dependencies
:
picocolors
:
1.1.1
...
...
@@ -6800,6 +7191,14 @@ snapshots:
'
@humanwhocodes/object-schema@2.0.3'
:
{}
'
@iconify/types@2.0.0'
:
{}
'
@iconify/utils@3.1.3'
:
dependencies
:
'
@antfu/install-pkg'
:
1.1.0
'
@iconify/types'
:
2.0.0
import-meta-resolve
:
4.2.0
'
@internationalized/date@3.6.0'
:
dependencies
:
'
@swc/helpers'
:
0.5.17
...
...
@@ -6860,6 +7259,10 @@ snapshots:
'
@jridgewell/resolve-uri'
:
3.1.2
'
@jridgewell/sourcemap-codec'
:
1.5.5
'
@mermaid-js/parser@1.1.1'
:
dependencies
:
'
@chevrotain/types'
:
11.1.2
'
@module-federation/runtime-tools@0.5.1'
:
dependencies
:
'
@module-federation/runtime'
:
0.5.1
...
...
@@ -8402,6 +8805,123 @@ snapshots:
dependencies
:
'
@types/node'
:
22.18.6
'
@types/d3-array@3.2.2'
:
{}
'
@types/d3-axis@3.0.6'
:
dependencies
:
'
@types/d3-selection'
:
3.0.11
'
@types/d3-brush@3.0.6'
:
dependencies
:
'
@types/d3-selection'
:
3.0.11
'
@types/d3-chord@3.0.6'
:
{}
'
@types/d3-color@3.1.3'
:
{}
'
@types/d3-contour@3.0.6'
:
dependencies
:
'
@types/d3-array'
:
3.2.2
'
@types/geojson'
:
7946.0.16
'
@types/d3-delaunay@6.0.4'
:
{}
'
@types/d3-dispatch@3.0.7'
:
{}
'
@types/d3-drag@3.0.7'
:
dependencies
:
'
@types/d3-selection'
:
3.0.11
'
@types/d3-dsv@3.0.7'
:
{}
'
@types/d3-ease@3.0.2'
:
{}
'
@types/d3-fetch@3.0.7'
:
dependencies
:
'
@types/d3-dsv'
:
3.0.7
'
@types/d3-force@3.0.10'
:
{}
'
@types/d3-format@3.0.4'
:
{}
'
@types/d3-geo@3.1.0'
:
dependencies
:
'
@types/geojson'
:
7946.0.16
'
@types/d3-hierarchy@3.1.7'
:
{}
'
@types/d3-interpolate@3.0.4'
:
dependencies
:
'
@types/d3-color'
:
3.1.3
'
@types/d3-path@3.1.1'
:
{}
'
@types/d3-polygon@3.0.2'
:
{}
'
@types/d3-quadtree@3.0.6'
:
{}
'
@types/d3-random@3.0.3'
:
{}
'
@types/d3-scale-chromatic@3.1.0'
:
{}
'
@types/d3-scale@4.0.9'
:
dependencies
:
'
@types/d3-time'
:
3.0.4
'
@types/d3-selection@3.0.11'
:
{}
'
@types/d3-shape@3.1.8'
:
dependencies
:
'
@types/d3-path'
:
3.1.1
'
@types/d3-time-format@4.0.3'
:
{}
'
@types/d3-time@3.0.4'
:
{}
'
@types/d3-timer@3.0.2'
:
{}
'
@types/d3-transition@3.0.9'
:
dependencies
:
'
@types/d3-selection'
:
3.0.11
'
@types/d3-zoom@3.0.8'
:
dependencies
:
'
@types/d3-interpolate'
:
3.0.4
'
@types/d3-selection'
:
3.0.11
'
@types/d3@7.4.3'
:
dependencies
:
'
@types/d3-array'
:
3.2.2
'
@types/d3-axis'
:
3.0.6
'
@types/d3-brush'
:
3.0.6
'
@types/d3-chord'
:
3.0.6
'
@types/d3-color'
:
3.1.3
'
@types/d3-contour'
:
3.0.6
'
@types/d3-delaunay'
:
6.0.4
'
@types/d3-dispatch'
:
3.0.7
'
@types/d3-drag'
:
3.0.7
'
@types/d3-dsv'
:
3.0.7
'
@types/d3-ease'
:
3.0.2
'
@types/d3-fetch'
:
3.0.7
'
@types/d3-force'
:
3.0.10
'
@types/d3-format'
:
3.0.4
'
@types/d3-geo'
:
3.1.0
'
@types/d3-hierarchy'
:
3.1.7
'
@types/d3-interpolate'
:
3.0.4
'
@types/d3-path'
:
3.1.1
'
@types/d3-polygon'
:
3.0.2
'
@types/d3-quadtree'
:
3.0.6
'
@types/d3-random'
:
3.0.3
'
@types/d3-scale'
:
4.0.9
'
@types/d3-scale-chromatic'
:
3.1.0
'
@types/d3-selection'
:
3.0.11
'
@types/d3-shape'
:
3.1.8
'
@types/d3-time'
:
3.0.4
'
@types/d3-time-format'
:
4.0.3
'
@types/d3-timer'
:
3.0.2
'
@types/d3-transition'
:
3.0.9
'
@types/d3-zoom'
:
3.0.8
'
@types/debug@4.1.12'
:
dependencies
:
'
@types/ms'
:
2.1.0
...
...
@@ -8424,6 +8944,8 @@ snapshots:
'
@types/estree@1.0.8'
:
{}
'
@types/geojson@7946.0.16'
:
{}
'
@types/hast@3.0.4'
:
dependencies
:
'
@types/unist'
:
3.0.3
...
...
@@ -8469,6 +8991,9 @@ snapshots:
dependencies
:
tapable
:
2.2.3
'
@types/trusted-types@2.0.7'
:
optional
:
true
'
@types/unist@2.0.11'
:
{}
'
@types/unist@3.0.3'
:
{}
...
...
@@ -8665,6 +9190,11 @@ snapshots:
'
@unrs/resolver-binding-win32-x64-msvc@1.11.1'
:
optional
:
true
'
@upsetjs/venn.js@2.0.0'
:
optionalDependencies
:
d3-selection
:
3.0.0
d3-transition
:
3.0.1(d3-selection@3.0.0)
'
@vitest/eslint-plugin@1.3.12(eslint@8.57.1)(typescript@5.9.2)'
:
dependencies
:
'
@typescript-eslint/scope-manager'
:
8.44.0
...
...
@@ -8901,7 +9431,7 @@ snapshots:
base64id@2.0.0
:
{}
baseline-browser-mapping@2.
8.6
:
{}
baseline-browser-mapping@2.
10.29
:
{}
big.js@5.2.2
:
{}
...
...
@@ -8943,7 +9473,7 @@ snapshots:
browserslist@4.26.2
:
dependencies
:
baseline-browser-mapping
:
2.
8.6
baseline-browser-mapping
:
2.
10.29
caniuse-lite
:
1.0.30001743
electron-to-chromium
:
1.5.222
node-releases
:
2.0.21
...
...
@@ -9088,6 +9618,8 @@ snapshots:
commander@7.2.0
:
{}
commander@8.3.0
:
{}
comment-parser@1.4.1
:
{}
compare-versions@6.1.1
:
{}
...
...
@@ -9126,6 +9658,14 @@ snapshots:
object-assign
:
4.1.1
vary
:
1.1.2
cose-base@1.0.3
:
dependencies
:
layout-base
:
1.0.2
cose-base@2.2.0
:
dependencies
:
layout-base
:
2.0.1
cosmiconfig@8.3.6(typescript@5.9.2)
:
dependencies
:
import-fresh
:
3.3.1
...
...
@@ -9171,12 +9711,198 @@ snapshots:
csstype@3.1.3
:
{}
cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.3)
:
dependencies
:
cose-base
:
1.0.3
cytoscape
:
3.33.3
cytoscape-fcose@2.2.0(cytoscape@3.33.3)
:
dependencies
:
cose-base
:
2.2.0
cytoscape
:
3.33.3
cytoscape@3.33.3
:
{}
d3-array@2.12.1
:
dependencies
:
internmap
:
1.0.1
d3-array@3.2.4
:
dependencies
:
internmap
:
2.0.3
d3-axis@3.0.0
:
{}
d3-brush@3.0.0
:
dependencies
:
d3-dispatch
:
3.0.1
d3-drag
:
3.0.0
d3-interpolate
:
3.0.1
d3-selection
:
3.0.0
d3-transition
:
3.0.1(d3-selection@3.0.0)
d3-chord@3.0.1
:
dependencies
:
d3-path
:
3.1.0
d3-color@3.1.0
:
{}
d3-contour@4.0.2
:
dependencies
:
d3-array
:
3.2.4
d3-delaunay@6.0.4
:
dependencies
:
delaunator
:
5.1.0
d3-dispatch@3.0.1
:
{}
d3-drag@3.0.0
:
dependencies
:
d3-dispatch
:
3.0.1
d3-selection
:
3.0.0
d3-dsv@3.0.1
:
dependencies
:
commander
:
7.2.0
iconv-lite
:
0.6.3
rw
:
1.3.3
d3-ease@3.0.1
:
{}
d3-fetch@3.0.1
:
dependencies
:
d3-dsv
:
3.0.1
d3-force@3.0.0
:
dependencies
:
d3-dispatch
:
3.0.1
d3-quadtree
:
3.0.1
d3-timer
:
3.0.1
d3-format@3.1.2
:
{}
d3-geo@3.1.1
:
dependencies
:
d3-array
:
3.2.4
d3-hierarchy@3.1.2
:
{}
d3-interpolate@3.0.1
:
dependencies
:
d3-color
:
3.1.0
d3-path@1.0.9
:
{}
d3-path@3.1.0
:
{}
d3-polygon@3.0.1
:
{}
d3-quadtree@3.0.1
:
{}
d3-random@3.0.1
:
{}
d3-sankey@0.12.3
:
dependencies
:
d3-array
:
2.12.1
d3-shape
:
1.3.7
d3-scale-chromatic@3.1.0
:
dependencies
:
d3-color
:
3.1.0
d3-interpolate
:
3.0.1
d3-scale@4.0.2
:
dependencies
:
d3-array
:
3.2.4
d3-format
:
3.1.2
d3-interpolate
:
3.0.1
d3-time
:
3.1.0
d3-time-format
:
4.1.0
d3-selection@3.0.0
:
{}
d3-shape@1.3.7
:
dependencies
:
d3-path
:
1.0.9
d3-shape@3.2.0
:
dependencies
:
d3-path
:
3.1.0
d3-time-format@4.1.0
:
dependencies
:
d3-time
:
3.1.0
d3-time@3.1.0
:
dependencies
:
d3-array
:
3.2.4
d3-timer@3.0.1
:
{}
d3-transition@3.0.1(d3-selection@3.0.0)
:
dependencies
:
d3-color
:
3.1.0
d3-dispatch
:
3.0.1
d3-ease
:
3.0.1
d3-interpolate
:
3.0.1
d3-selection
:
3.0.0
d3-timer
:
3.0.1
d3-zoom@3.0.0
:
dependencies
:
d3-dispatch
:
3.0.1
d3-drag
:
3.0.0
d3-interpolate
:
3.0.1
d3-selection
:
3.0.0
d3-transition
:
3.0.1(d3-selection@3.0.0)
d3@7.9.0
:
dependencies
:
d3-array
:
3.2.4
d3-axis
:
3.0.0
d3-brush
:
3.0.0
d3-chord
:
3.0.1
d3-color
:
3.1.0
d3-contour
:
4.0.2
d3-delaunay
:
6.0.4
d3-dispatch
:
3.0.1
d3-drag
:
3.0.0
d3-dsv
:
3.0.1
d3-ease
:
3.0.1
d3-fetch
:
3.0.1
d3-force
:
3.0.0
d3-format
:
3.1.2
d3-geo
:
3.1.1
d3-hierarchy
:
3.1.2
d3-interpolate
:
3.0.1
d3-path
:
3.1.0
d3-polygon
:
3.0.1
d3-quadtree
:
3.0.1
d3-random
:
3.0.1
d3-scale
:
4.0.2
d3-scale-chromatic
:
3.1.0
d3-selection
:
3.0.0
d3-shape
:
3.2.0
d3-time
:
3.1.0
d3-time-format
:
4.1.0
d3-timer
:
3.0.1
d3-transition
:
3.0.1(d3-selection@3.0.0)
d3-zoom
:
3.0.0
dagre-d3-es@7.0.14
:
dependencies
:
d3
:
7.9.0
lodash-es
:
4.18.1
date-fns@3.6.0
:
{}
dayjs@1.11.12
:
{}
dayjs@1.11.18
:
{}
dayjs@1.11.20
:
{}
debounce@1.2.1
:
{}
debug@2.6.9
:
...
...
@@ -9211,6 +9937,10 @@ snapshots:
defu@6.1.4
:
{}
delaunator@5.1.0
:
dependencies
:
robust-predicates
:
3.0.3
delayed-stream@1.0.0
:
{}
depd@2.0.0
:
{}
...
...
@@ -9247,6 +9977,10 @@ snapshots:
dependencies
:
domelementtype
:
2.3.0
dompurify@3.4.3
:
optionalDependencies
:
'
@types/trusted-types'
:
2.0.7
domutils@3.2.2
:
dependencies
:
dom-serializer
:
2.0.0
...
...
@@ -9344,6 +10078,8 @@ snapshots:
has-tostringtag
:
1.0.2
hasown
:
2.0.2
es-toolkit@1.46.1
:
{}
escalade@3.2.0
:
{}
escape-html@1.0.3
:
{}
...
...
@@ -10022,6 +10758,8 @@ snapshots:
dependencies
:
duplexer
:
0.1.2
hachure-fill@0.5.2
:
{}
has-flag@3.0.0
:
{}
has-flag@4.0.0
:
{}
...
...
@@ -10139,6 +10877,10 @@ snapshots:
dependencies
:
safer-buffer
:
2.1.2
iconv-lite@0.6.3
:
dependencies
:
safer-buffer
:
2.1.2
ignore@5.3.2
:
{}
ignore@7.0.5
:
{}
...
...
@@ -10152,6 +10894,8 @@ snapshots:
parent-module
:
1.0.1
resolve-from
:
4.0.0
import-meta-resolve@4.2.0
:
{}
imurmurhash@0.1.4
:
{}
indent-string@4.0.0
:
{}
...
...
@@ -10170,6 +10914,10 @@ snapshots:
react
:
18.3.1
react-dom
:
18.3.1(react@18.3.1)
internmap@1.0.1
:
{}
internmap@2.0.3
:
{}
intersection-observer@0.12.2
:
{}
intl-messageformat@10.7.16
:
...
...
@@ -10329,10 +11077,20 @@ snapshots:
readable-stream
:
2.3.8
setimmediate
:
1.0.5
katex@0.16.46
:
dependencies
:
commander
:
8.3.0
keyv@4.5.4
:
dependencies
:
json-buffer
:
3.0.1
khroma@2.1.0
:
{}
layout-base@1.0.2
:
{}
layout-base@2.0.1
:
{}
levn@0.4.1
:
dependencies
:
prelude-ls
:
1.2.1
...
...
@@ -10393,6 +11151,8 @@ snapshots:
dependencies
:
p-locate
:
5.0.0
lodash-es@4.18.1
:
{}
lodash.merge@4.6.2
:
{}
lodash@4.17.21
:
{}
...
...
@@ -10431,6 +11191,8 @@ snapshots:
markdown-table@3.0.4
:
{}
marked@16.4.2
:
{}
math-intrinsics@1.1.0
:
{}
mdast-util-find-and-replace@3.0.2
:
...
...
@@ -10612,6 +11374,30 @@ snapshots:
merge2@1.4.1
:
{}
mermaid@11.15.0
:
dependencies
:
'
@braintree/sanitize-url'
:
7.1.2
'
@iconify/utils'
:
3.1.3
'
@mermaid-js/parser'
:
1.1.1
'
@types/d3'
:
7.4.3
'
@upsetjs/venn.js'
:
2.0.0
cytoscape
:
3.33.3
cytoscape-cose-bilkent
:
4.1.0(cytoscape@3.33.3)
cytoscape-fcose
:
2.2.0(cytoscape@3.33.3)
d3
:
7.9.0
d3-sankey
:
0.12.3
dagre-d3-es
:
7.0.14
dayjs
:
1.11.20
dompurify
:
3.4.3
es-toolkit
:
1.46.1
katex
:
0.16.46
khroma
:
2.1.0
marked
:
16.4.2
roughjs
:
4.6.6
stylis
:
4.4.0
ts-dedent
:
2.2.0
uuid
:
14.0.0
micromark-core-commonmark@2.0.3
:
dependencies
:
decode-named-character-reference
:
1.2.0
...
...
@@ -10971,6 +11757,8 @@ snapshots:
dependencies
:
quansync
:
0.2.11
package-manager-detector@1.6.0
:
{}
pako@1.0.11
:
{}
parent-module@1.0.1
:
...
...
@@ -11019,6 +11807,8 @@ snapshots:
path-browserify@1.0.1
:
{}
path-data-parser@0.1.0
:
{}
path-exists@4.0.0
:
{}
path-is-absolute@1.0.1
:
{}
...
...
@@ -11068,6 +11858,13 @@ snapshots:
pluralize@8.0.0
:
{}
points-on-curve@0.2.0
:
{}
points-on-path@0.2.1
:
dependencies
:
path-data-parser
:
0.1.0
points-on-curve
:
0.2.0
popper.js@1.16.1
:
{}
postcss-import@15.1.0(postcss@8.5.6)
:
...
...
@@ -11427,12 +12224,23 @@ snapshots:
dependencies
:
glob
:
7.2.3
robust-predicates@3.0.3
:
{}
roughjs@4.6.6
:
dependencies
:
hachure-fill
:
0.5.2
path-data-parser
:
0.1.0
points-on-curve
:
0.2.0
points-on-path
:
0.2.1
rslog@1.2.11
:
{}
run-parallel@1.2.0
:
dependencies
:
queue-microtask
:
1.2.3
rw@1.3.3
:
{}
safe-buffer@5.1.2
:
{}
safe-buffer@5.2.1
:
{}
...
...
@@ -11701,6 +12509,8 @@ snapshots:
dependencies
:
inline-style-parser
:
0.2.4
stylis@4.4.0
:
{}
sucrase@3.35.0
:
dependencies
:
'
@jridgewell/gen-mapping'
:
0.3.13
...
...
@@ -11825,6 +12635,8 @@ snapshots:
tinyexec@0.3.2
:
{}
tinyexec@1.1.2
:
{}
to-regex-range@5.0.1
:
dependencies
:
is-number
:
7.0.0
...
...
@@ -11860,6 +12672,8 @@ snapshots:
picomatch
:
4.0.3
typescript
:
5.9.2
ts-dedent@2.2.0
:
{}
ts-interface-checker@0.1.13
:
{}
ts-pattern@5.8.0
:
{}
...
...
@@ -12000,6 +12814,8 @@ snapshots:
utils-merge@1.0.1
:
{}
uuid@14.0.0
:
{}
validate-npm-package-license@3.0.4
:
dependencies
:
spdx-correct
:
3.2.0
...
...
src/api/home.ts
View file @
e879c818
...
...
@@ -24,7 +24,7 @@ export function fetchToolList() {
* @params
* conversationId: 会话id
* busiType: 业务类型,固定值 '01'
* recordType: 记录类型,
固定值 'A14'
* recordType: 记录类型,
由当前工具 get_tool_list 项的 userRole 第二段决定
*/
export
function
fetchEfficiencyQuestionList
(
data
:
any
)
{
return
http
.
post
(
'/conversation/api/conversation/mobile/v1/query_strategy_recommend_question'
,
data
)
...
...
src/components/ChatEditor/index.tsx
View file @
e879c818
...
...
@@ -8,17 +8,20 @@ import type { RootState } from '@/store'
import
SendIcon
from
'@/assets/svg/send.svg?react'
import
{
type
WithAuthProps
,
withAuth
}
from
'@/auth/withAuth'
import
{
useAppDispatch
,
useAppSelector
}
from
'@/store/hook'
import
{
clearCurrentToolId
,
setCurrentToolId
}
from
'@/store/conversationSlice'
import
{
clear
BusinessInsightWorkflow
,
clear
CurrentToolId
,
setCurrentToolId
}
from
'@/store/conversationSlice'
import
{
setIsAsking
}
from
'@/store/chatSlice'
import
{
fetchToolList
}
from
'@/api/home'
import
{
safeSessionStorageGetItem
,
safeSessionStorageRemoveItem
,
safeSessionStorageSetItem
}
from
'@/lib/utils'
import
{
clearWorkflowToolCaches
,
getWorkflowToolIdAndRecordTypeForRequest
,
setWorkflowRequestSeedPair
}
from
'@/lib/workflowToolParams'
import
type
{
ToolListRow
}
from
'@/types/toolList'
import
{
getToolQuestionPairFromRow
,
isGeneralInstitutionalToolRow
,
normalizeToolId
,
readToolListFromSessionStorage
}
from
'@/lib/toolRoleRecordTypes'
import
{
fetchSessionConversationId
,
fetchUserQaRecordPage
}
from
'@/api/conversation'
interface
ChatEditorProps
{
onChange
?:
(
value
:
string
)
=>
void
onFocus
?:
()
=>
void
onSubmit
?:
(
value
:
string
,
toolId
?:
string
)
=>
void
onToolClick
?:
(
isToolBtn
:
boolean
,
toolId
?:
string
,
toolName
?:
string
,
shouldChangeStyle
?:
boolean
,
conversationId
?:
string
,
isLoading
?:
boolean
)
=>
void
onToolClick
?:
(
isToolBtn
:
boolean
,
toolId
?:
string
,
toolName
?:
string
,
shouldChangeStyle
?:
boolean
,
conversationId
?:
string
,
isLoading
?:
boolean
,
toolMeta
?:
Pick
<
ToolListRow
,
'commonQuestionApi'
|
'businessInsightConfigTypes'
|
'showStructuredQueryPlaceholder'
>
)
=>
void
placeholders
:
string
[]
showContentTips
?:
boolean
initialValue
?:
string
...
...
@@ -34,7 +37,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
const
intervalRef
=
useRef
<
NodeJS
.
Timeout
|
null
>
(
null
)
const
[
isOpenLoginModal
,
isOpenLoginModalActions
]
=
useToggle
()
const
isAsking
=
useAppSelector
((
state
:
RootState
)
=>
state
.
chat
.
isAsking
)
const
[
toolList
,
setToolList
]
=
useState
<
any
[]
>
([])
const
[
toolList
,
setToolList
]
=
useState
<
ToolListRow
[]
>
([])
const
[
selectedToolId
,
setSelectedToolId
]
=
useState
<
string
|
null
>
(
null
)
const
[
isToolBtnActive
,
setIsToolBtnActive
]
=
useState
<
boolean
>
(
true
)
const
currentToolId
=
useAppSelector
((
state
:
RootState
)
=>
state
.
conversation
.
currentToolId
)
...
...
@@ -136,7 +139,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
const
cached
=
sessionStorage
.
getItem
(
cacheKey
)
if
(
cached
)
{
try
{
const
{
toolList
,
timestamp
}
=
JSON
.
parse
(
cached
)
const
{
toolList
,
timestamp
}
=
JSON
.
parse
(
cached
)
as
{
toolList
:
ToolListRow
[],
timestamp
:
number
}
if
(
Date
.
now
()
-
timestamp
<
5
*
60
*
1000
)
{
setToolList
(
toolList
)
return
...
...
@@ -149,10 +152,10 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
// 调用真实 API 获取工具列表
const
res
=
await
fetchToolList
()
if
(
res
?.
data
&&
Array
.
isArray
(
res
.
data
)
&&
res
.
data
.
length
>
0
)
{
setToolList
(
res
.
data
)
// 缓存工具列表(包含时间戳)
const
rows
=
res
.
data
as
ToolListRow
[]
setToolList
(
rows
)
sessionStorage
.
setItem
(
cacheKey
,
JSON
.
stringify
({
toolList
:
r
es
.
data
,
toolList
:
r
ows
,
timestamp
:
Date
.
now
(),
}))
}
...
...
@@ -277,8 +280,11 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
const
handleSubmit
=
()
=>
{
if
(
isAsking
)
return
// 以 DOM 为准,避免 content 状态与 contentEditable 不同步(如 IME、快速连点)导致误判为空
const
textFromDom
=
(
editorRef
.
current
?.
textContent
??
''
).
trim
()
const
text
=
textFromDom
||
content
.
trim
()
if
(
checkAuth
())
{
if
(
content
.
trim
()
)
{
if
(
text
)
{
// 只在提质增效模式下传递 toolId,制度活化不传
let
toolId
:
string
|
undefined
...
...
@@ -290,7 +296,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
// 制度活化:不传递 toolId
toolId
=
undefined
}
onSubmit
?.(
content
.
trim
()
,
toolId
)
onSubmit
?.(
text
,
toolId
)
setContent
(
''
)
if
(
editorRef
.
current
)
{
editorRef
.
current
.
textContent
=
''
...
...
@@ -317,90 +323,58 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
document
.
execCommand
(
'insertText'
,
false
,
text
)
}
// 处理制度活化按钮点击:先创建新会话
const
handleGeneralClick
=
async
()
=>
{
const
pickToolMeta
=
(
t
:
ToolListRow
)
=>
({
commonQuestionApi
:
t
.
commonQuestionApi
??
undefined
,
businessInsightConfigTypes
:
t
.
businessInsightConfigTypes
??
undefined
,
showStructuredQueryPlaceholder
:
t
.
showStructuredQueryPlaceholder
??
undefined
,
})
/** 底部工具条:统一「清缓存 → 建会话 → 换路由 → 拉 QA → 通知上层」;仅会话参数与后续是否自动 workflow 随是否通用行变化 */
const
handleWorkflowToolButtonPress
=
async
(
tool
:
ToolListRow
)
=>
{
if
(
!
checkAuth
())
return
// 通知上层开始 loading
onToolClick
?.(
true
,
undefined
,
'制度活化'
,
false
,
undefined
,
true
)
clearWorkflowToolCaches
()
dispatch
(
clearBusinessInsightWorkflow
())
const
isGeneral
=
isGeneralInstitutionalToolRow
(
tool
)
const
normalizedToolId
=
normalizeToolId
(
tool
.
toolId
)
const
busiIdForSession
=
isGeneral
?
''
:
normalizedToolId
const
displayName
=
tool
.
toolName
?.
trim
()
||
''
const
parentIsToolBtn
=
isGeneral
const
eventToolId
=
isGeneral
?
undefined
:
normalizedToolId
const
shouldChangeStyleForParent
=
!
isGeneral
onToolClick
?.(
parentIsToolBtn
,
eventToolId
,
displayName
,
shouldChangeStyleForParent
,
undefined
,
true
,
pickToolMeta
(
tool
),
)
try
{
// 重置提交按钮状态
dispatch
(
setIsAsking
(
false
))
// 先更新 Redux,确保状态同步
if
(
isGeneral
)
{
dispatch
(
clearCurrentToolId
())
// 立即更新本地状态,让 UI 立即响应
setIsToolBtnActive
(
true
)
setSelectedToolId
(
null
)
safeSessionStorageRemoveItem
(
'showToolQuestion'
)
safeSessionStorageRemoveItem
(
'currentToolId'
)
setSessionToolId
(
null
)
setShowToolQuestion
(
false
)
// 先通知上层更新欢迎语(即便后续接口异常也能生效)
onToolClick
?.(
true
,
undefined
,
'制度活化'
,
false
)
// 清空路由中的 toolId 参数
onToolClick
?.(
true
,
undefined
,
displayName
,
false
,
undefined
,
undefined
,
pickToolMeta
(
tool
))
if
(
toolIdFromUrl
)
{
const
newSearchParams
=
new
URLSearchParams
(
searchParams
)
newSearchParams
.
delete
(
'toolId'
)
setSearchParams
(
newSearchParams
,
{
replace
:
true
})
}
// 先调用 fetchSessionConversationId 获取会话ID
// 等待 token 就绪后再调用接口
await
waitForToken
()
const
requestData
=
{
busiId
:
''
,
}
const
res
=
await
fetchSessionConversationId
(
requestData
)
if
(
res
?.
data
?.
conversationId
)
{
const
conversationId
=
res
.
data
.
conversationId
// 获取到会话ID后,通知上层更新欢迎语并传递 conversationId
onToolClick
?.(
true
,
undefined
,
'制度活化'
,
false
,
conversationId
)
// 在 navigate 之前设置标记,避免 Chat 组件的 useEffect 重复调用接口
sessionStorage
.
setItem
(
'toolHistoryLoading'
,
conversationId
)
// 更新路由到新的会话ID
navigate
(
`/chat/
${
conversationId
}
`
,
{
replace
:
true
,
state
:
{
toolId
:
null
,
skipHistoryLoad
:
true
,
// 标记跳过历史记录加载,因为 ChatEditor 会处理
},
})
// 使用获取到的会话ID调用历史会话
await
waitForToken
()
const
qaRes
=
await
fetchUserQaRecordPage
(
conversationId
,
''
)
console
.
log
(
'qaRes chatEditor11111'
,
qaRes
)
// 通过自定义事件将历史记录数据传递给父组件进行渲染
window
.
dispatchEvent
(
new
CustomEvent
(
'toolHistoryLoaded'
,
{
detail
:
{
conversationId
,
toolId
:
''
,
toolName
:
'制度活化'
,
qaRecords
:
qaRes
?.
data
||
[],
},
}))
// 清除标记,避免影响后续路由切换
sessionStorage
.
removeItem
(
'toolHistoryLoading'
)
}
}
catch
(
error
)
{
console
.
error
(
'获取会话ID或历史记录失败:'
,
error
)
// 出错时也要清除标记
sessionStorage
.
removeItem
(
'toolHistoryLoading'
)
}
finally
{
// 通知上层结束 loading
onToolClick
?.(
true
,
undefined
,
'制度活化'
,
false
,
undefined
,
false
)
}
}
// 处理工具按钮点击:先创建新会话,再切换工具
const
handleToolClick
=
async
(
tool
:
any
)
=>
{
// 通知上层开始 loading
onToolClick
?.(
false
,
tool
.
toolId
,
tool
.
toolName
,
true
,
undefined
,
true
)
try
{
// 重置提交按钮状态
dispatch
(
setIsAsking
(
false
))
if
(
tool
.
toolName
===
'数据助手'
)
{
else
{
if
(
tool
.
showStructuredQueryPlaceholder
===
true
)
{
safeSessionStorageSetItem
(
'showToolQuestion'
,
'true'
)
setShowToolQuestion
(
true
)
}
...
...
@@ -408,80 +382,106 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
safeSessionStorageRemoveItem
(
'showToolQuestion'
)
setShowToolQuestion
(
false
)
}
dispatch
(
setCurrentToolId
(
tool
.
t
oolId
))
setSelectedToolId
(
tool
.
t
oolId
)
dispatch
(
setCurrentToolId
(
normalizedT
oolId
))
setSelectedToolId
(
normalizedT
oolId
)
setIsToolBtnActive
(
false
)
safeSessionStorageSetItem
(
'currentToolId'
,
tool
.
toolId
)
setSessionToolId
(
tool
.
toolId
)
safeSessionStorageSetItem
(
'currentToolId'
,
normalizedToolId
)
setSessionToolId
(
normalizedToolId
)
}
// 先调用 fetchSessionConversationId 获取会话ID
// 等待 token 就绪后再调用接口
await
waitForToken
()
const
requestData
=
{
busiId
:
tool
.
toolId
,
}
const
res
=
await
fetchSessionConversationId
(
requestData
)
const
res
=
await
fetchSessionConversationId
({
busiId
:
busiIdForSession
})
if
(
res
?.
data
?.
conversationId
)
{
const
conversationId
=
res
.
data
.
conversationId
// 获取到会话ID后,通知上层更新欢迎语并传递 conversationId
onToolClick
?.(
false
,
tool
.
toolId
,
tool
.
toolName
,
true
,
conversationId
)
// 在 navigate 之前设置标记,避免 Chat 组件的 useEffect 重复调用接口
// 使用 sessionStorage 作为标记,因为 location.state 可能有时序问题
onToolClick
?.(
parentIsToolBtn
,
eventToolId
,
displayName
,
shouldChangeStyleForParent
,
conversationId
,
undefined
,
pickToolMeta
(
tool
),
)
sessionStorage
.
setItem
(
'toolHistoryLoading'
,
conversationId
)
// 更新路由到新的会话ID,并携带 toolId 参数
navigate
(
`/chat/
${
conversationId
}
?toolId=
${
tool
.
toolId
}
`
,
{
if
(
isGeneral
)
{
navigate
(
`/chat/
${
conversationId
}
`
,
{
replace
:
true
,
state
:
{
toolId
:
null
,
skipHistoryLoad
:
true
,
},
})
}
else
{
navigate
(
`/chat/
${
conversationId
}
?toolId=
${
normalizedToolId
}
`
,
{
replace
:
true
,
state
:
{
toolId
:
tool
.
t
oolId
,
skipHistoryLoad
:
true
,
// 标记跳过历史记录加载,因为 ChatEditor 会处理
toolId
:
normalizedT
oolId
,
skipHistoryLoad
:
true
,
},
})
// 使用获取到的会话ID调用历史会话
}
await
waitForToken
()
const
qaRes
=
await
fetchUserQaRecordPage
(
conversationId
,
tool
.
toolId
)
const
qaToolIdArg
=
isGeneral
?
''
:
normalizedToolId
const
qaRes
=
await
fetchUserQaRecordPage
(
conversationId
,
qaToolIdArg
)
console
.
log
(
'qaRes chatEditor11111'
,
qaRes
)
// 通过自定义事件将历史记录数据传递给父组件进行渲染
window
.
dispatchEvent
(
new
CustomEvent
(
'toolHistoryLoaded'
,
{
detail
:
{
conversationId
,
toolId
:
tool
.
t
oolId
,
toolName
:
tool
.
tool
Name
,
toolId
:
isGeneral
?
''
:
normalizedT
oolId
,
toolName
:
display
Name
,
qaRecords
:
qaRes
?.
data
||
[],
},
}))
// 提质增效模式下,延迟触发自动提问接口
// 使用双重 requestAnimationFrame 确保 Chat 组件的 currentIdRef 已经更新
if
(
tool
.
toolId
===
'6712395743241'
)
{
console
.
log
(
'[ChatEditor] 提质增效模式 - 准备延迟触发 autoSubmitQuestion'
,
{
conversationId
,
toolId
:
tool
.
toolId
})
if
(
!
isGeneral
)
{
const
listForPair
=
toolList
?.
length
?
toolList
:
readToolListFromSessionStorage
()
const
clickedPair
=
getToolQuestionPairFromRow
(
tool
)
if
(
clickedPair
.
toolId
&&
clickedPair
.
recordType
)
setWorkflowRequestSeedPair
(
clickedPair
.
toolId
,
clickedPair
.
recordType
)
const
{
toolId
:
wfToolId
,
recordType
:
wfRecordType
}
=
getWorkflowToolIdAndRecordTypeForRequest
(
listForPair
)
if
(
wfToolId
&&
wfRecordType
)
{
console
.
log
(
'[ChatEditor] 工具会话 - 准备延迟触发 autoSubmitQuestion'
,
{
conversationId
,
toolId
:
wfToolId
,
recordType
:
wfRecordType
})
requestAnimationFrame
(()
=>
{
requestAnimationFrame
(()
=>
{
console
.
log
(
'[ChatEditor] 提质增效模式 - 触发 autoSubmitQuestion 事件'
,
{
conversationId
,
toolId
:
tool
.
toolId
})
console
.
log
(
'[ChatEditor] 工具会话 - 触发 autoSubmitQuestion 事件'
,
{
conversationId
,
toolId
:
wfToolId
,
recordType
:
wfRecordType
})
window
.
dispatchEvent
(
new
CustomEvent
(
'autoSubmitQuestion'
,
{
detail
:
{
conversationId
,
question
:
''
,
toolId
:
tool
.
t
oolId
,
recordType
:
'B03'
,
toolId
:
wfT
oolId
,
recordType
:
wfRecordType
,
busiType
:
'01'
,
},
}))
})
})
}
// 清除标记,避免影响后续路由切换
}
sessionStorage
.
removeItem
(
'toolHistoryLoading'
)
}
}
catch
(
error
)
{
console
.
error
(
'获取会话ID或历史记录失败:'
,
error
)
// 出错时也要通知上层更新欢迎语(不传递 conversationId)
onToolClick
?.(
false
,
tool
.
toolId
,
tool
.
toolName
,
true
)
// 出错时也要清除标记
sessionStorage
.
removeItem
(
'toolHistoryLoading'
)
if
(
!
isGeneral
)
{
onToolClick
?.(
false
,
eventToolId
,
displayName
,
shouldChangeStyleForParent
,
undefined
,
undefined
,
pickToolMeta
(
tool
))
}
}
finally
{
// 通知上层结束 loading
onToolClick
?.(
false
,
tool
.
toolId
,
tool
.
toolName
,
true
,
undefined
,
false
)
onToolClick
?.(
parentIsToolBtn
,
eventToolId
,
displayName
,
shouldChangeStyleForParent
,
undefined
,
false
,
pickToolMeta
(
tool
),
)
}
}
...
...
@@ -500,12 +500,11 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
}
},
[
content
])
//
组件加载时和路由参数变化时获取工具列表
//
仅进入带工具条的页面时拉取一次(含 5 分钟 session 缓存);会话/路由切换不重复请求 get_tool_list
useEffect
(()
=>
{
if
(
!
hideTools
)
{
getToolList
()
}
},
[
location
.
pathname
,
location
.
search
,
hideTools
])
if
(
!
hideTools
)
void
getToolList
()
},
[
hideTools
])
// 监听 sessionStorage 中的 showToolQuestion
useEffect
(()
=>
{
...
...
@@ -605,14 +604,13 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
</
div
>
{
!
hideTools
&&
toolList
&&
toolList
.
length
>
0
&&
(
<
div
className=
"absolute left-4 bottom-2 flex items-center gap-3 pointer-events-auto pl-[16px]"
>
{
toolList
.
map
((
tool
:
any
,
index
:
number
)
=>
{
{
toolList
.
map
((
tool
:
ToolListRow
,
index
:
number
)
=>
{
// 根据 selectedToolId 或路由中的 toolId 进行匹配,注意数据类型统一转换为字符串比较
// 优先使用 selectedToolId(用户点击后的状态),其次使用 sessionStorage 中的 currentToolId(刷新时使用),再次使用路由中的 toolId(初始化时使用)
const
toolIdStr
=
String
(
tool
.
toolId
)
const
isSelectedByState
=
selectedToolId
&&
toolIdStr
===
String
(
selectedToolId
)
const
toolIdStr
=
normalizeToolId
(
tool
.
toolId
)
const
isSelectedByState
=
Boolean
(
selectedToolId
)
&&
toolIdStr
===
String
(
selectedToolId
)
const
isSelectedBySession
=
!
selectedToolId
&&
sessionToolId
&&
toolIdStr
===
String
(
sessionToolId
)
// 制度活化高亮:路由内没有 toolId 或 toolId 为空时默认高亮,点击后也要高亮
const
isGeneralMode
=
tool
.
toolName
===
'制度活化'
&&
isToolBtnActive
&&
!
selectedToolId
&&
!
sessionToolId
&&
!
toolIdFromUrl
const
isGeneralMode
=
isGeneralInstitutionalToolRow
(
tool
)
&&
isToolBtnActive
&&
!
selectedToolId
&&
!
sessionToolId
&&
!
toolIdFromUrl
const
isSelected
=
isSelectedByState
||
isSelectedBySession
||
isGeneralMode
const
baseBtnClass
...
...
@@ -627,10 +625,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
// 高亮状态直接返回,避免重复触发
if
(
isSelected
)
return
if
(
tool
.
toolName
===
'制度活化'
)
await
handleGeneralClick
()
else
await
handleToolClick
(
tool
)
await
handleWorkflowToolButtonPress
(
tool
)
}
// 处理后端返回的 base64 图标
...
...
@@ -647,7 +642,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
return
(
<
Button
key=
{
tool
.
toolId
||
`tool-${index}`
}
key=
{
tool
IdStr
||
`tool-${index}`
}
className=
{
`${baseBtnClass}${selectedClass}`
}
radius=
"full"
variant=
{
selectedVariant
}
...
...
src/layouts/HistoryBar/components/HistoryBarList/index.tsx
View file @
e879c818
...
...
@@ -10,6 +10,7 @@ import type { Conversation } from '@/types/conversation'
import
{
processConversationData
}
from
'@/store/conversationSlice.helper'
import
{
clearCurrentToolId
,
clearShouldSendQuestion
,
setCurrentToolId
,
setShouldSendQuestion
}
from
'@/store/conversationSlice'
import
{
isMobile
}
from
'@/utils'
import
{
findToolRowBySessionToolId
,
readToolListFromSessionStorage
,
toolRowTriggersHistoryEmptyQuestionSend
}
from
'@/lib/toolRoleRecordTypes'
interface
HistoryBarListProps
{
searchValue
:
string
...
...
@@ -40,10 +41,11 @@ export const HistoryBarList: React.FC<HistoryBarListProps> = ({ searchValue, onS
dispatch
(
clearCurrentToolId
())
}
// 仅当历史记录为“提质增效”时,进入对话页后自动触发一次提问接口
// 用空格字符串触发 shouldSendQuestion 的监听,但不会在 UI 中新增用户问题气泡(question.trim() 为 false)。
const
isQualityImprovement
=
conversation
.
toolId
===
'6712395743241'
if
(
isQualityImprovement
)
{
// 工具会话且列表配置需要时:进入对话页后用空格触发 shouldSendQuestion(trim 后为空,不展示用户气泡)
const
list
=
readToolListFromSessionStorage
()
const
toolRow
=
findToolRowBySessionToolId
(
list
,
conversation
.
toolId
||
''
)
const
shouldHistoryAutoQuestion
=
toolRowTriggersHistoryEmptyQuestionSend
(
toolRow
)
if
(
shouldHistoryAutoQuestion
)
{
const
autoShouldSendQuestion
=
' '
dispatch
(
setShouldSendQuestion
(
autoShouldSendQuestion
))
console
.
log
(
'[HistoryBarList] shouldSendQuestion 000000:'
,
{
...
...
src/layouts/Navbar/Navbar.tsx
View file @
e879c818
...
...
@@ -14,6 +14,7 @@ import { useAppDispatch, useAppSelector } from '@/store/hook'
import
{
isMobile
}
from
'@/utils'
import
{
safeLocalStorageSetItem
,
waitForToken
}
from
'@/lib/utils'
import
{
fetchUserQaRecordPage
}
from
'@/api/conversation'
import
{
findToolRowBySessionToolId
,
getToolQuestionPairFromRow
,
readToolListFromSessionStorage
,
toolRowTriggersEmptyQuestionBootstrap
}
from
'@/lib/toolRoleRecordTypes'
// import { User } from './components/User'
interface
NavbarProps
{
...
...
@@ -129,39 +130,36 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
:
`/chat/
${
currentConversationId
}
`
// 通过 location.state 传递 toolId,避免在 Chat 页面被误判为"外链残留参数"而强制清空
// 如果有 shouldSendQuestion 或者是提质增效模式,传递 skipHistoryLoad 标记,跳过历史记录加载
const
isQualityImprovement
=
currentToolId
===
'6712395743241'
const
hasSessionTool
=
Boolean
(
currentToolId
?.
trim
())
navigate
(
url
,
{
state
:
{
toolId
:
currentToolId
||
null
,
skipHistoryLoad
:
Boolean
(
shouldSendQuestion
)
||
isQualityImprovement
,
skipHistoryLoad
:
Boolean
(
shouldSendQuestion
)
||
hasSessionTool
,
},
})
dispatch
(
clearNavigationFlag
())
// 提质增效模式下,创建会话后:
// 1. 使用新会话ID + toolId 主动拉取一次历史记录(与数据助手保持一致)
// 2. 再自动触发一次空问题提问(欢迎语)
if
(
isQualityImprovement
&&
currentToolId
)
{
// 1) 拉取历史记录并通过自定义事件交给 Chat 渲染
// 有当前工具且列表配置为需要首包空问题时:拉历史并触发 autoSubmitQuestion
if
(
hasSessionTool
&&
currentToolId
)
{
;(
async
()
=>
{
try
{
const
list
=
readToolListFromSessionStorage
()
const
row
=
findToolRowBySessionToolId
(
list
,
currentToolId
)
if
(
!
toolRowTriggersEmptyQuestionBootstrap
(
row
))
return
await
waitForToken
()
const
qaRes
=
await
fetchUserQaRecordPage
(
currentConversationId
,
currentToolId
)
window
.
dispatchEvent
(
new
CustomEvent
(
'toolHistoryLoaded'
,
{
detail
:
{
conversationId
:
currentConversationId
,
toolId
:
currentToolId
,
// 这里不传 toolName,由 Chat 自己根据 currentToolId 解析名称
qaRecords
:
qaRes
?.
data
||
[],
},
}))
}
catch
(
error
)
{
console
.
error
(
'[Navbar] 提质增效模式 - 创建会话后拉取历史记录失败:'
,
error
)
}
})()
// 2) 使用 requestAnimationFrame 确保在下一个渲染周期执行自动提问
const
pair
=
getToolQuestionPairFromRow
(
row
)
const
recordTypeForAuto
=
pair
.
recordType
||
'01'
requestAnimationFrame
(()
=>
{
requestAnimationFrame
(()
=>
{
window
.
dispatchEvent
(
new
CustomEvent
(
'autoSubmitQuestion'
,
{
...
...
@@ -169,13 +167,18 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
conversationId
:
currentConversationId
,
question
:
''
,
toolId
:
currentToolId
,
recordType
:
'B03'
,
recordType
:
recordTypeForAuto
,
busiType
:
'01'
,
},
}))
})
})
}
catch
(
error
)
{
console
.
error
(
'[Navbar] 创建会话后拉取历史或自动提问失败:'
,
error
)
}
})()
}
}
},
[
shouldNavigateToNewConversation
,
currentConversationId
,
currentToolId
,
shouldSendQuestion
,
navigate
,
dispatch
])
...
...
src/lib/toolRoleRecordTypes.ts
0 → 100644
View file @
e879c818
import
type
{
ToolListRow
}
from
'@/types/toolList'
import
{
getUserRolesForApi
}
from
'@/lib/utils'
/** 与 ChatEditor 写入的 session 缓存键一致 */
export
const
TOOL_LIST_SESSION_KEY
=
'toolList'
function
asToolRows
(
toolList
:
unknown
):
ToolListRow
[]
{
return
Array
.
isArray
(
toolList
)
?
(
toolList
as
ToolListRow
[])
:
[]
}
/** 规范化 toolId:空串视为无工具(制度活化会话) */
export
function
normalizeToolId
(
toolId
:
unknown
):
string
{
if
(
toolId
==
null
)
return
''
const
s
=
String
(
toolId
).
trim
()
return
s
}
/**
* userRole:逗号分隔,依次为
* 1 提问流 recordType;2 左侧常见问题 recordType;3 答后推荐问 recordType
*/
export
function
parseUserRoleRecordTypes
(
userRole
:
unknown
):
{
questionRecordType
:
string
commonQuestionRecordType
:
string
recommendRecordType
:
string
}
{
const
raw
=
userRole
!=
null
?
String
(
userRole
)
:
''
const
parts
=
raw
.
split
(
','
).
map
(
p
=>
p
.
trim
()).
filter
(
p
=>
p
.
length
>
0
)
return
{
questionRecordType
:
parts
[
0
]
??
''
,
commonQuestionRecordType
:
parts
[
1
]
??
''
,
recommendRecordType
:
parts
[
2
]
??
''
,
}
}
/** 列表中无 toolId(或空)的项视为「制度活化 / 通用」会话行 */
export
function
isGeneralInstitutionalToolRow
(
row
:
ToolListRow
|
null
|
undefined
):
boolean
{
if
(
!
row
)
return
false
if
(
row
.
isGeneralSession
===
true
||
row
.
isGeneralSession
===
1
)
return
true
return
!
normalizeToolId
(
row
.
toolId
)
}
export
function
findGeneralToolRow
(
toolList
:
unknown
):
ToolListRow
|
null
{
const
list
=
asToolRows
(
toolList
)
const
hit
=
list
.
find
(
isGeneralInstitutionalToolRow
)
return
hit
??
null
}
export
function
findToolRowBySessionToolId
(
toolList
:
unknown
,
sessionToolId
:
string
|
null
|
undefined
):
ToolListRow
|
null
{
const
list
=
asToolRows
(
toolList
)
const
tid
=
normalizeToolId
(
sessionToolId
)
if
(
!
tid
)
return
findGeneralToolRow
(
toolList
)
return
list
.
find
(
r
=>
normalizeToolId
(
r
.
toolId
)
===
tid
)
??
null
}
/**
* 提问接口用的 toolId + recordType(recordType 优先 userRole 首段,否则回退行上 recordType)
*/
export
function
getToolQuestionPairFromRow
(
row
:
ToolListRow
|
null
|
undefined
):
{
toolId
:
string
,
recordType
:
string
}
{
if
(
!
row
)
{
return
{
toolId
:
''
,
recordType
:
''
}
}
const
toolId
=
normalizeToolId
(
row
.
toolId
)
const
fromRole
=
parseUserRoleRecordTypes
(
row
.
userRole
)
const
recordType
=
fromRole
.
questionRecordType
||
(
row
.
recordType
!=
null
?
String
(
row
.
recordType
).
trim
()
:
''
)
return
{
toolId
,
recordType
}
}
/** 左侧常见问题 recordType */
export
function
getCommonQuestionRecordTypeForToolRow
(
row
:
ToolListRow
|
null
|
undefined
):
string
{
if
(
!
row
)
return
''
const
{
commonQuestionRecordType
,
questionRecordType
}
=
parseUserRoleRecordTypes
(
row
.
userRole
)
return
commonQuestionRecordType
||
questionRecordType
||
(
row
.
recordType
!=
null
?
String
(
row
.
recordType
).
trim
()
:
''
)
}
/** 答后推荐问 recordType */
export
function
getRecommendRecordTypeForToolRow
(
row
:
ToolListRow
|
null
|
undefined
):
string
{
if
(
!
row
)
return
''
const
{
recommendRecordType
,
questionRecordType
}
=
parseUserRoleRecordTypes
(
row
.
userRole
)
return
recommendRecordType
||
questionRecordType
||
(
row
.
recordType
!=
null
?
String
(
row
.
recordType
).
trim
()
:
''
)
}
/** 是否走业务洞察常见问题(由列表行配置驱动,不写死名称或 toolId) */
export
function
toolRowUsesBusinessInsightCommonQuestions
(
row
:
ToolListRow
|
null
|
undefined
):
boolean
{
if
(
!
row
)
return
false
if
(
row
.
commonQuestionApi
===
'business_insight'
)
return
true
return
Array
.
isArray
(
row
.
businessInsightConfigTypes
)
&&
row
.
businessInsightConfigTypes
.
length
>
0
}
export
function
getBusinessInsightConfigTypesForRow
(
row
:
ToolListRow
|
null
|
undefined
):
string
[]
{
if
(
!
row
)
return
[
'10'
]
if
(
Array
.
isArray
(
row
.
businessInsightConfigTypes
)
&&
row
.
businessInsightConfigTypes
.
length
>
0
)
return
row
.
businessInsightConfigTypes
.
map
(
String
)
return
[
'10'
]
}
/** 新建会话进入聊天页后,是否需要与底部工具一致:拉历史并触发空问题首包(由配置驱动) */
export
function
toolRowTriggersEmptyQuestionBootstrap
(
row
:
ToolListRow
|
null
|
undefined
):
boolean
{
if
(
!
row
||
!
normalizeToolId
(
row
.
toolId
))
return
false
return
Boolean
(
parseUserRoleRecordTypes
(
row
.
userRole
).
questionRecordType
)
}
/** 从历史进入会话时,是否派发 shouldSendQuestion 空格触发静默补问(与 toolRowTriggersEmptyQuestionBootstrap 一致) */
export
function
toolRowTriggersHistoryEmptyQuestionSend
(
row
:
ToolListRow
|
null
|
undefined
):
boolean
{
return
toolRowTriggersEmptyQuestionBootstrap
(
row
)
}
export
function
readToolListFromSessionStorage
():
ToolListRow
[]
{
try
{
const
raw
=
sessionStorage
.
getItem
(
TOOL_LIST_SESSION_KEY
)
if
(
!
raw
)
return
tryReadLegacyToolListCache
()
const
parsed
=
JSON
.
parse
(
raw
)
as
{
toolList
?:
unknown
,
timestamp
?:
number
}
if
(
parsed
?.
toolList
&&
Array
.
isArray
(
parsed
.
toolList
))
return
asToolRows
(
parsed
.
toolList
)
if
(
Array
.
isArray
(
parsed
))
return
asToolRows
(
parsed
)
}
catch
{
// ignore
}
return
tryReadLegacyToolListCache
()
}
function
tryReadLegacyToolListCache
():
ToolListRow
[]
{
try
{
const
userRoles
=
getUserRolesForApi
()
const
cacheKey
=
`toolList_
${
JSON
.
stringify
(
userRoles
)}
`
const
raw
=
sessionStorage
.
getItem
(
cacheKey
)
if
(
!
raw
)
return
[]
const
{
toolList
}
=
JSON
.
parse
(
raw
)
as
{
toolList
?:
unknown
}
return
asToolRows
(
toolList
)
}
catch
{
return
[]
}
}
src/lib/workflowToolParams.ts
0 → 100644
View file @
e879c818
import
{
getToolQuestionPairFromRow
}
from
'@/lib/toolRoleRecordTypes'
import
type
{
ToolListRow
}
from
'@/types/toolList'
/**
* 工作流提问参数:首次使用列表项的 userRole 首段(或行上 recordType);
* 之后各次提问使用接口流式返回中最后一次带上的 toolId / recordType。
*/
const
STORAGE_KEY
=
'workflowLastToolPair'
/** 本次点击会话写入的「列表第 0 项」快照,供 Chat 首次提问在流式返回前读取 */
const
SEED_KEY
=
'workflowRequestSeedPair'
export
function
clearWorkflowLastToolPair
():
void
{
try
{
sessionStorage
.
removeItem
(
STORAGE_KEY
)
}
catch
{
// ignore
}
}
export
function
setWorkflowRequestSeedPair
(
toolId
:
string
,
recordType
:
string
):
void
{
const
tid
=
toolId
?.
trim
?.()
??
''
const
rt
=
recordType
?.
trim
?.()
??
''
if
(
!
tid
||
!
rt
)
return
try
{
sessionStorage
.
setItem
(
SEED_KEY
,
JSON
.
stringify
({
toolId
:
tid
,
recordType
:
rt
}))
}
catch
{
// ignore
}
}
export
function
getWorkflowRequestSeedPair
():
{
toolId
:
string
,
recordType
:
string
}
|
null
{
try
{
const
raw
=
sessionStorage
.
getItem
(
SEED_KEY
)
if
(
!
raw
)
return
null
const
parsed
=
JSON
.
parse
(
raw
)
as
{
toolId
?:
unknown
,
recordType
?:
unknown
}
const
toolId
=
parsed
?.
toolId
!=
null
?
String
(
parsed
.
toolId
).
trim
()
:
''
const
recordType
=
parsed
?.
recordType
!=
null
?
String
(
parsed
.
recordType
).
trim
()
:
''
if
(
!
toolId
||
!
recordType
)
return
null
return
{
toolId
,
recordType
}
}
catch
{
return
null
}
}
export
function
clearWorkflowRequestSeedPair
():
void
{
try
{
sessionStorage
.
removeItem
(
SEED_KEY
)
}
catch
{
// ignore
}
}
/** 制度活化等场景:清空流式 last 与点击 seed */
export
function
clearWorkflowToolCaches
():
void
{
clearWorkflowLastToolPair
()
clearWorkflowRequestSeedPair
()
}
export
function
setWorkflowLastToolPair
(
toolId
:
string
,
recordType
:
string
):
void
{
const
tid
=
toolId
?.
trim
?.()
??
''
const
rt
=
recordType
?.
trim
?.()
??
''
if
(
!
tid
||
!
rt
)
return
try
{
sessionStorage
.
setItem
(
STORAGE_KEY
,
JSON
.
stringify
({
toolId
:
tid
,
recordType
:
rt
}))
}
catch
{
// ignore
}
}
export
function
getWorkflowLastToolPair
():
{
toolId
:
string
,
recordType
:
string
}
|
null
{
try
{
const
raw
=
sessionStorage
.
getItem
(
STORAGE_KEY
)
if
(
!
raw
)
return
null
const
parsed
=
JSON
.
parse
(
raw
)
as
{
toolId
?:
unknown
,
recordType
?:
unknown
}
const
toolId
=
parsed
?.
toolId
!=
null
?
String
(
parsed
.
toolId
).
trim
()
:
''
const
recordType
=
parsed
?.
recordType
!=
null
?
String
(
parsed
.
recordType
).
trim
()
:
''
if
(
!
toolId
||
!
recordType
)
return
null
return
{
toolId
,
recordType
}
}
catch
{
return
null
}
}
/** get_tool_list 第 0 项的提问用 toolId / recordType(来自 userRole 首段) */
export
function
getToolListFirstPair
(
toolList
:
unknown
[]):
{
toolId
:
string
,
recordType
:
string
}
{
const
row
=
Array
.
isArray
(
toolList
)
&&
toolList
.
length
>
0
?
(
toolList
[
0
]
as
ToolListRow
)
:
null
return
getToolQuestionPairFromRow
(
row
)
}
/**
* 组装「提问主工作流」等接口所需的 toolId / recordType:
* 若已有流式返回缓存则优先;否则使用列表第 0 项的 userRole 首段(首次)。
*/
export
function
getWorkflowToolIdAndRecordTypeForRequest
(
toolList
:
unknown
[]):
{
toolId
:
string
,
recordType
:
string
}
{
const
last
=
getWorkflowLastToolPair
()
if
(
last
)
return
last
const
seed
=
getWorkflowRequestSeedPair
()
if
(
seed
)
return
seed
return
getToolListFirstPair
(
toolList
)
}
/** Chat 提交:流式最后一次优先,否则为本次点击写入的 seed(get_tool_list[0]) */
export
function
getWorkflowToolIdAndRecordTypeForSubmit
():
{
toolId
:
string
,
recordType
:
string
}
|
null
{
return
getWorkflowLastToolPair
()
??
getWorkflowRequestSeedPair
()
}
src/pages/Chat/Chat.tsx
View file @
e879c818
...
...
@@ -4,7 +4,7 @@ import { Button } from '@heroui/react'
import
{
motion
}
from
'framer-motion'
import
{
useScroll
}
from
'ahooks'
import
styles
from
'./Chat.module.less'
import
{
processApiResponse
}
from
'./helper'
import
{
getLastQaRecordWorkflowContext
,
processApiResponse
}
from
'./helper'
import
{
ChatWelcome
}
from
'./components/ChatWelcome'
import
{
ChatItemUser
}
from
'./components/ChatItem/ChatItemUser'
import
{
ChatAnswerBox
}
from
'./components/ChatItem/ChatAnswerBox'
...
...
@@ -13,9 +13,9 @@ import { ChatEditor } from '@/components/ChatEditor'
import
type
{
ChatRecord
}
from
'@/types/chat'
import
{
fetchSessionConversationId
,
fetchUserQaRecordPage
}
from
'@/api/conversation'
import
{
fetchCheckTokenApi
,
fetchStreamResponse
}
from
'@/api/chat'
import
{
fetchToolList
}
from
'@/api/home'
import
{
clearBusinessInsightWorkflow
,
clearCurrentToolId
,
clearShouldSendQuestion
,
exitBusinessInsightSubWorkflow
,
fetchConversations
,
setBusinessInsightMainWorkflow
,
setBusinessInsightPendingQuestion
,
setBusinessInsightSubWorkflow
,
setCurrentToolId
,
updateBusinessInsightParamMap
}
from
'@/store/conversationSlice'
import
{
clearCurrentToolId
,
clearShouldSendQuestion
,
exitBusinessInsightSubWorkflow
,
fetchConversations
,
setBusinessInsightMainWorkflow
,
setBusinessInsightPendingQuestion
,
setBusinessInsightSubWorkflow
,
setCurrentToolId
}
from
'@/store/conversationSlice'
import
{
getUserRolesForApi
,
safeSessionStorageGetItem
,
waitForToken
}
from
'@/lib/utils'
import
{
findGeneralToolRow
,
findToolRowBySessionToolId
,
readToolListFromSessionStorage
}
from
'@/lib/toolRoleRecordTypes'
import
{
getWorkflowToolIdAndRecordTypeForSubmit
,
setWorkflowLastToolPair
}
from
'@/lib/workflowToolParams'
import
type
{
RootState
}
from
'@/store'
import
{
useAppDispatch
,
useAppSelector
}
from
'@/store/hook'
...
...
@@ -71,6 +71,12 @@ export const Chat: React.FC = () => {
const
fromCollectRef
=
useRef
<
boolean
>
(
false
)
// 记录正在处理的 conversationId,避免重复调用
const
processingConversationIdRef
=
useRef
<
string
|
null
>
(
null
)
/** 最近一次 query_user_qa_record_list 最后一条上的 workflow 字段,供自动提问透传 */
const
lastLoadedQaWorkflowContextRef
=
useRef
<
{
recordType
:
string
workFlowSessionId
:
string
busiType
?:
string
}
|
null
>
(
null
)
// 标记是否为提质增效模式的自动调用(用于提取欢迎语)
const
isAutoSubmitQualityImprovementRef
=
useRef
<
boolean
>
(
false
)
// 提质增效自动调用欢迎语候选(用于流结束后写入 sessionStorage)
...
...
@@ -92,6 +98,10 @@ export const Chat: React.FC = () => {
isInSubWorkflow
:
false
,
pendingQuestion
:
null
,
})
/** 主工作流返回「进子」JSON 后,已发起空问题「开子」请求,等待本轮流结束写入子 session */
const
expectingSubWorkflowOpenRef
=
useRef
(
false
)
const
pendingSubOpenRecordTypeRef
=
useRef
<
string
|
null
>
(
null
)
const
pendingSubQuestionRef
=
useRef
<
string
|
null
>
(
null
)
// 当外部系统直接以 /chat/:id 链接进入(没有 location.state,且 URL 中也没有 toolId)时,
// 视为一次新的会话入口:重置为制度活化,清除历史遗留的工具模式状态
...
...
@@ -347,9 +357,12 @@ export const Chat: React.FC = () => {
}
}
}
// 空问题 + 显式传入 recordType/busiType(如工具入口 autoSubmit)时走自动欢迎语流式逻辑
// 空问题 + 显式传入 recordType/busiType(如工具入口 autoSubmit)时走自动欢迎语流式逻辑;
// 已有主会话后的空问题(如「开子工作流」)不应走欢迎语分支
const
isEmptyQuestion
=
!
question
.
trim
()
const
isAutoToolEmptySubmit
=
isEmptyQuestion
&&
Boolean
(
extraParams
?.
recordType
&&
extraParams
?.
busiType
)
const
isAutoToolEmptySubmit
=
isEmptyQuestion
&&
Boolean
(
extraParams
?.
recordType
&&
extraParams
?.
busiType
)
&&
!
businessInsightWorkflowRef
.
current
.
mainWorkflowSessionId
isAutoSubmitQualityImprovementRef
.
current
=
isAutoToolEmptySubmit
if
(
isAutoSubmitQualityImprovementRef
.
current
)
{
// 重置欢迎语候选
...
...
@@ -364,10 +377,7 @@ export const Chat: React.FC = () => {
const
isNew
=
allItems
.
length
<=
1
dispatch
(
setIsAsking
(
true
))
// 检查token
await
fetchCheckTokenApi
()
// 如果 question 不为空,才添加用户问题和空的AI回答(自动提问时 question 为空,不显示)
// 先展示用户气泡与 AI 占位,避免 check_token 失败时界面毫无反馈且流式接口未发出
if
(
question
.
trim
())
{
setAllItems
(
prevItems
=>
[
...
prevItems
,
...
...
@@ -384,6 +394,14 @@ export const Chat: React.FC = () => {
])
}
// 检查 token:失败不阻断提问(流式请求仍带 X-Token,由服务端校验)
try
{
await
fetchCheckTokenApi
()
}
catch
(
e
)
{
console
.
warn
(
'[Chat] check_token 失败,仍尝试发起流式提问:'
,
e
)
}
// 创建新的 AbortController
abortControllerRef
.
current
=
new
AbortController
()
...
...
@@ -424,32 +442,31 @@ export const Chat: React.FC = () => {
// workFlowSessionId 的传递逻辑:
// 1. 如果是重新问答(isReask === true),不传递 workFlowSessionId
// 2.
如果是提质增效模式的自动调用(空问题),不传递 workFlowSessionId
// 2.
提质增效等空问题自动调用:未显式传入 workFlowSessionId 时不传(欢迎语首轮);若 extra/参数中已带历史最后一条的 session 则仍传递
// 3. 优先使用 workflowExtraParams 中的值
// 4. 其次使用直接传递的参数
// 5.
如果都没有
,从会话历史中找到最后一个有 workFlowSessionId 的答案
// 5.
若仍没有且非「无显式 session 的提质增效自动调用」
,从会话历史中找到最后一个有 workFlowSessionId 的答案
const
isReask
=
workflowExtraParams
?.
isReask
===
true
const
isAutoQualityImprovement
=
isAutoSubmitQualityImprovementRef
.
current
// 已选择工具(非制度活化)时允许携带 / 回填
workFlowSessionId
const
explicitWorkFlowSessionId
=
workflowExtraParams
?.
workFlowSessionId
||
workFlowSessionId
const
shouldUseWorkflowSession
=
Boolean
(
resolvedToolId
&&
String
(
resolvedToolId
).
trim
()
!==
''
)
if
(
shouldUseWorkflowSession
&&
!
isReask
&&
!
isAutoQualityImprovement
)
{
if
(
workflowExtraParams
?.
workFlowSessionId
)
{
requestBody
.
workFlowSessionId
=
workflowExtraParams
.
workFlowSessionId
}
else
if
(
workFlowSessionId
)
{
requestBody
.
workFlowSessionId
=
workFlowSessionId
if
(
shouldUseWorkflowSession
&&
!
isReask
)
{
let
resolvedWorkflowSessionId
:
string
|
undefined
if
(
explicitWorkFlowSessionId
)
{
resolvedWorkflowSessionId
=
explicitWorkFlowSessionId
}
else
{
// 从会话历史中找到最后一个有 workFlowSessionId 的答案
// 注意:这里使用 allItems 的当前值(在添加新记录之前),所以需要从倒数第二个开始查找
else
if
(
!
isAutoQualityImprovement
)
{
for
(
let
i
=
allItems
.
length
-
1
;
i
>=
0
;
i
--
)
{
const
item
=
allItems
[
i
]
if
(
item
.
role
===
'ai'
&&
item
.
answerList
?.[
0
]?.
workFlowSessionId
)
{
re
questBody
.
workF
lowSessionId
=
item
.
answerList
[
0
].
workFlowSessionId
re
solvedWorkf
lowSessionId
=
item
.
answerList
[
0
].
workFlowSessionId
break
}
}
}
if
(
resolvedWorkflowSessionId
)
{
requestBody
.
workFlowSessionId
=
resolvedWorkflowSessionId
}
}
// 如果传入了 recordType,优先使用传入的值(workflow 提交场景)
...
...
@@ -540,7 +557,7 @@ export const Chat: React.FC = () => {
// 如果是提质增效自动调用且本轮存在有效欢迎语候选,将其写入 sessionStorage 供欢迎语使用
if
(
isAutoSubmitQualityImprovementRef
.
current
&&
hasAutoWelcomeCandidateRef
.
current
)
{
try
{
const
key
=
'
quality
WelcomeText'
const
key
=
'
toolStream
WelcomeText'
sessionStorage
.
setItem
(
key
,
autoWelcomeCandidateRef
.
current
)
}
catch
(
error
)
{
...
...
@@ -595,23 +612,88 @@ export const Chat: React.FC = () => {
||
Boolean
(
wf
?.
mainWorkflowSessionId
)
||
Boolean
(
wf
?.
isInSubWorkflow
)
if
(
lastAnswer
&&
shouldHandleWorkflowAnswer
)
{
let
parsedAnswer
=
null
const
resolveWorkflowToolId
=
()
=>
{
try
{
parsedAnswer
=
JSON
.
parse
(
lastAnswer
.
answer
||
''
)
return
(
sessionStorage
.
getItem
(
'currentToolId'
)
??
''
).
trim
()
||
(
currentToolId
!=
null
?
String
(
currentToolId
).
trim
()
:
''
)
}
catch
{
// 不是 JSON,保持正常回答
return
currentToolId
!=
null
?
String
(
currentToolId
).
trim
()
:
''
}
if
(
parsedAnswer
&&
parsedAnswer
.
recordType
&&
parsedAnswer
.
question
)
{
}
// 刚结束的流是「开子工作流」空问题请求:写入子 session,无卡片则自动「问子」
if
(
expectingSubWorkflowOpenRef
.
current
&&
lastAnswer
.
workFlowSessionId
)
{
expectingSubWorkflowOpenRef
.
current
=
false
const
subRt
=
(
pendingSubOpenRecordTypeRef
.
current
?.
trim
()
||
(
lastAnswer
.
recordType
!=
null
?
String
(
lastAnswer
.
recordType
).
trim
()
:
''
)
||
''
).
trim
()
pendingSubOpenRecordTypeRef
.
current
=
null
if
(
subRt
)
{
dispatch
(
setBusinessInsightSubWorkflow
({
sessionId
:
''
,
recordType
:
parsedAnswer
.
recordType
,
sessionId
:
String
(
lastAnswer
.
workFlowSessionId
)
,
recordType
:
subRt
,
}))
dispatch
(
setBusinessInsightPendingQuestion
(
parsedAnswer
.
question
))
const
hasCards
=
Array
.
isArray
(
lastAnswer
.
cardList
)
&&
lastAnswer
.
cardList
.
length
>
0
const
pq
=
pendingSubQuestionRef
.
current
pendingSubQuestionRef
.
current
=
null
if
(
!
hasCards
&&
pq
&&
pq
.
trim
())
{
queueMicrotask
(()
=>
{
void
handleSubmitQuestion
(
pq
,
undefined
,
resolveWorkflowToolId
()
||
undefined
,
{
busiType
:
'01'
})
})
}
}
}
else
if
(
expectingSubWorkflowOpenRef
.
current
)
{
// 开子未带回 session,避免状态卡死
expectingSubWorkflowOpenRef
.
current
=
false
pendingSubOpenRecordTypeRef
.
current
=
null
pendingSubQuestionRef
.
current
=
null
}
else
{
let
parsedAnswer
:
{
recordType
?:
unknown
,
question
?:
unknown
,
end
?:
unknown
}
|
null
=
null
try
{
parsedAnswer
=
JSON
.
parse
(
lastAnswer
.
answer
||
''
)
}
catch
{
// 不是 JSON,保持正常回答
}
// 主工作流返回进子 JSON:先空问题开子(带主 session),再在流结束后写入子 session
if
(
parsedAnswer
&&
parsedAnswer
.
recordType
&&
parsedAnswer
.
question
!=
null
)
{
const
subRecordType
=
String
(
parsedAnswer
.
recordType
).
trim
()
const
pendingQ
=
String
(
parsedAnswer
.
question
)
if
(
subRecordType
)
{
dispatch
(
setBusinessInsightPendingQuestion
(
pendingQ
))
pendingSubQuestionRef
.
current
=
pendingQ
pendingSubOpenRecordTypeRef
.
current
=
subRecordType
expectingSubWorkflowOpenRef
.
current
=
true
queueMicrotask
(()
=>
{
void
handleSubmitQuestion
(
''
,
undefined
,
resolveWorkflowToolId
()
||
undefined
,
{
recordType
:
subRecordType
,
busiType
:
'01'
,
})
})
}
}
else
if
(
businessInsightWorkflowRef
.
current
.
isInSubWorkflow
)
{
if
(
parsedAnswer
&&
parsedAnswer
.
end
===
true
)
{
const
reopenRt
=
parsedAnswer
.
recordType
!=
null
?
String
(
parsedAnswer
.
recordType
).
trim
()
:
''
const
followQ
=
parsedAnswer
.
question
!=
null
?
String
(
parsedAnswer
.
question
).
trim
()
:
''
dispatch
(
exitBusinessInsightSubWorkflow
())
dispatch
(
setBusinessInsightPendingQuestion
(
null
))
queueMicrotask
(()
=>
{
void
handleSubmitQuestion
(
''
,
undefined
,
resolveWorkflowToolId
()
||
undefined
,
{
busiType
:
'01'
,
...(
reopenRt
?
{
recordType
:
reopenRt
}
:
{}),
})
if
(
followQ
)
{
window
.
setTimeout
(()
=>
{
void
handleSubmitQuestion
(
followQ
,
undefined
,
resolveWorkflowToolId
()
||
undefined
,
{
busiType
:
'01'
})
},
400
)
}
})
}
}
else
if
(
lastAnswer
.
workFlowSessionId
&&
!
businessInsightWorkflowRef
.
current
.
mainWorkflowSessionId
)
{
...
...
@@ -619,6 +701,7 @@ export const Chat: React.FC = () => {
}
}
}
}
})
.
catch
(()
=>
{
// 流式请求出错时的处理
...
...
@@ -653,12 +736,13 @@ export const Chat: React.FC = () => {
/** 处理 workflow 表单提交 */
const
handleWorkflowSubmit
=
useCallback
(
async
(
paramMap
:
Record
<
string
,
string
>
,
recordId
?:
string
,
workFlowSessionId
?:
string
)
=>
{
// workflow 提交时,question 可以为空字符串,但需要传递 paramMap、recordId 和 workFlowSessionId
const
recordType
=
businessInsightWorkflowRef
.
current
.
subWorkflowRecordType
?.
trim
()
||
'A01'
await
handleSubmitQuestion
(
''
,
undefined
,
undefined
,
{
paramMap
,
recordId
,
workFlowSessionId
,
recordType
:
'A01'
,
recordType
,
busiType
:
'01'
,
})
},
[])
...
...
@@ -715,6 +799,14 @@ export const Chat: React.FC = () => {
await
waitForToken
()
const
res
=
await
fetchUserQaRecordPage
(
finalConversationId
,
toolId
||
safeSessionStorageGetItem
(
'currentToolId'
)
||
''
)
const
qaRecords
=
res
.
data
||
[]
const
wfCtx
=
getLastQaRecordWorkflowContext
(
qaRecords
)
lastLoadedQaWorkflowContextRef
.
current
=
wfCtx
.
recordType
&&
wfCtx
.
workFlowSessionId
?
{
recordType
:
wfCtx
.
recordType
,
workFlowSessionId
:
wfCtx
.
workFlowSessionId
,
busiType
:
wfCtx
.
busiType
??
'01'
,
}
:
null
const
messages
=
[{
role
:
'system'
}
as
ChatRecord
,
...
processApiResponse
(
qaRecords
)]
// 处理历史记录中的参考文档标记
const
processedMessages
=
messages
.
map
((
item
)
=>
{
...
...
@@ -804,7 +896,7 @@ export const Chat: React.FC = () => {
}
}
catch
{
// 错误处理
lastLoadedQaWorkflowContextRef
.
current
=
null
}
finally
{
setIsLoading
(
false
)
...
...
@@ -825,8 +917,12 @@ export const Chat: React.FC = () => {
dispatch
(
clearShouldSendQuestion
())
// 延迟发送,确保状态已更新
setTimeout
(()
=>
{
// 左侧常见问题自动发送:按普通提问处理,允许沿用历史 workFlowSessionId
handleSubmitQuestion
(
questionToSend
,
undefined
,
currentToolId
)
// 左侧常见问题自动发送:若 QA 列表最后一条带 recordType + workFlowSessionId,一并透传
const
wfExtra
=
lastLoadedQaWorkflowContextRef
.
current
const
autoExtra
=
wfExtra
?.
recordType
&&
wfExtra
?.
workFlowSessionId
?
{
recordType
:
wfExtra
.
recordType
,
workFlowSessionId
:
wfExtra
.
workFlowSessionId
,
busiType
:
wfExtra
.
busiType
??
'01'
}
:
undefined
handleSubmitQuestion
(
questionToSend
,
undefined
,
currentToolId
,
autoExtra
)
},
100
)
}
}
...
...
@@ -865,6 +961,7 @@ export const Chat: React.FC = () => {
// 检查是否跳过历史记录加载(点击常见问题时)
const
skipHistoryLoad
=
Boolean
((
location
.
state
as
{
skipHistoryLoad
?:
boolean
}
|
null
)?.
skipHistoryLoad
)
if
(
skipHistoryLoad
)
{
lastLoadedQaWorkflowContextRef
.
current
=
null
setAllItems
(
prev
=>
(
prev
.
length
>
0
?
prev
:
([{
role
:
'system'
}
as
ChatRecord
])))
currentIdRef
.
current
=
id
// 更新 toolId 相关状态(如果有)
...
...
@@ -894,6 +991,7 @@ export const Chat: React.FC = () => {
pendingQuestionRef
.
current
=
null
// 清除待发送的问题
// 重置历史记录加载标记,确保新会话时能正确触发自动发送
historyLoadedRef
.
current
.
conversationId
=
null
lastLoadedQaWorkflowContextRef
.
current
=
null
// 保存 fromCollect 标记到 ref,避免 location.state 被清除后丢失
// 优先使用 location.state,其次使用 sessionStorage(用于 navigate(-1) 的情况)
fromCollectRef
.
current
=
Boolean
(
location
.
state
?.
fromCollect
)
||
Boolean
(
sessionStorage
.
getItem
(
'fromCollect'
))
...
...
@@ -935,8 +1033,11 @@ export const Chat: React.FC = () => {
// 确保历史记录加载完成后再发送问题
setTimeout
(()
=>
{
console
.
log
(
'[Chat] 执行自动发送问题:'
,
questionToSend
)
// 左侧常见问题自动发送:按普通提问处理,允许沿用历史 workFlowSessionId
handleSubmitQuestion
(
questionToSend
,
undefined
,
currentToolId
)
const
wfExtra
=
lastLoadedQaWorkflowContextRef
.
current
const
autoExtra
=
wfExtra
?.
recordType
&&
wfExtra
?.
workFlowSessionId
?
{
recordType
:
wfExtra
.
recordType
,
workFlowSessionId
:
wfExtra
.
workFlowSessionId
,
busiType
:
wfExtra
.
busiType
??
'01'
}
:
undefined
handleSubmitQuestion
(
questionToSend
,
undefined
,
currentToolId
,
autoExtra
)
},
100
)
}
else
{
...
...
@@ -951,20 +1052,23 @@ export const Chat: React.FC = () => {
const
getToolNameFromToolId
=
async
()
=>
{
if
(
currentToolId
)
{
try
{
// 优先从缓存读取工具列表,避免重复调用接口
const
cachedRow
=
findToolRowBySessionToolId
(
readToolListFromSessionStorage
(),
currentToolId
)
if
(
cachedRow
?.
toolName
?.
trim
())
{
setCurrentToolName
(
cachedRow
.
toolName
.
trim
())
return
}
// 兼容旧缓存键
const
userRoles
=
getUserRolesForApi
()
const
cache
Key
=
`toolList_
${
JSON
.
stringify
(
userRoles
)}
`
const
cached
=
sessionStorage
.
getItem
(
cache
Key
)
const
legacy
Key
=
`toolList_
${
JSON
.
stringify
(
userRoles
)}
`
const
cached
=
sessionStorage
.
getItem
(
legacy
Key
)
if
(
cached
)
{
try
{
const
{
toolList
,
timestamp
}
=
JSON
.
parse
(
cached
)
// 缓存有效(5分钟内)
if
(
Date
.
now
()
-
timestamp
<
5
*
60
*
1000
)
{
const
tool
=
toolList
.
find
((
t
:
any
)
=>
t
.
toolId
===
currentToolId
)
const
tool
=
toolList
.
find
((
t
:
any
)
=>
String
(
t
.
toolId
)
===
String
(
currentToolId
)
)
if
(
tool
?.
toolName
)
{
setCurrentToolName
(
tool
.
toolName
)
return
setCurrentToolName
(
String
(
tool
.
toolName
).
trim
())
}
}
}
...
...
@@ -972,24 +1076,15 @@ export const Chat: React.FC = () => {
// 缓存解析失败,继续调用接口
}
}
// 缓存无效或找不到工具时才调用接口
await
waitForToken
()
const
res
=
await
fetchToolList
()
if
(
res
?.
data
)
{
const
tool
=
res
.
data
.
find
((
t
:
any
)
=>
t
.
toolId
===
currentToolId
)
if
(
tool
?.
toolName
)
{
setCurrentToolName
(
tool
.
toolName
)
}
}
}
catch
(
error
)
{
console
.
error
(
'获取工具
列表
失败:'
,
error
)
console
.
error
(
'获取工具
名称
失败:'
,
error
)
}
}
else
{
// 制度活化
setCurrentToolName
(
'制度活化'
)
const
list
=
readToolListFromSessionStorage
()
const
general
=
findGeneralToolRow
(
list
)
setCurrentToolName
(
general
?.
toolName
?.
trim
()
||
undefined
)
}
}
getToolNameFromToolId
()
...
...
@@ -1024,12 +1119,21 @@ export const Chat: React.FC = () => {
const
{
conversationId
,
toolId
,
toolName
,
qaRecords
}
=
event
.
detail
setIsLoading
(
true
)
try
{
const
qaList
=
Array
.
isArray
(
qaRecords
)
?
qaRecords
:
[]
const
wfEvt
=
getLastQaRecordWorkflowContext
(
qaList
)
lastLoadedQaWorkflowContextRef
.
current
=
wfEvt
.
recordType
&&
wfEvt
.
workFlowSessionId
?
{
recordType
:
wfEvt
.
recordType
,
workFlowSessionId
:
wfEvt
.
workFlowSessionId
,
busiType
:
wfEvt
.
busiType
??
'01'
,
}
:
null
// 标记已通过事件加载数据,避免 useEffect 重复调用接口
toolHistoryLoadedRef
.
current
.
conversationId
=
conversationId
// 更新当前会话ID
currentIdRef
.
current
=
conversationId
// 处理历史记录数据
const
messages
=
[{
role
:
'system'
}
as
ChatRecord
,
...
processApiResponse
(
qa
Records
)]
const
messages
=
[{
role
:
'system'
}
as
ChatRecord
,
...
processApiResponse
(
qa
List
)]
// 处理历史记录中的参考文档标记
const
processedMessages
=
messages
.
map
((
item
)
=>
{
if
(
item
.
role
===
'ai'
&&
item
.
answerList
?.[
0
]?.
answer
)
{
...
...
@@ -1059,6 +1163,7 @@ export const Chat: React.FC = () => {
}
catch
(
error
)
{
console
.
error
(
'处理历史记录失败:'
,
error
)
lastLoadedQaWorkflowContextRef
.
current
=
null
}
finally
{
setIsLoading
(
false
)
...
...
@@ -1083,8 +1188,18 @@ export const Chat: React.FC = () => {
const
{
conversationId
,
question
,
toolId
,
recordType
,
busiType
}
=
event
.
detail
// 确保当前会话ID匹配
if
(
conversationId
===
currentIdRef
.
current
)
{
const
wf
=
lastLoadedQaWorkflowContextRef
.
current
if
(
wf
?.
recordType
&&
wf
?.
workFlowSessionId
)
{
handleSubmitQuestion
(
question
,
undefined
,
toolId
,
{
recordType
:
wf
.
recordType
,
workFlowSessionId
:
wf
.
workFlowSessionId
,
busiType
:
wf
.
busiType
??
busiType
??
'01'
,
})
}
else
{
handleSubmitQuestion
(
question
,
undefined
,
toolId
,
{
recordType
,
busiType
})
}
}
else
{
console
.
warn
(
'[Chat] conversationId 不匹配,跳过自动提交'
,
{
eventConversationId
:
conversationId
,
...
...
@@ -1184,7 +1299,7 @@ export const Chat: React.FC = () => {
</
div
>
<
ChatEditor
onSubmit=
{
(
question
,
toolId
)
=>
handleSubmitQuestion
(
question
,
undefined
,
toolId
)
}
onToolClick=
{
(
isToolBtn
,
toolId
,
toolName
,
shouldChangeStyle
,
conversationId
,
isLoading
)
=>
{
onToolClick=
{
(
isToolBtn
,
toolId
,
toolName
,
shouldChangeStyle
,
conversationId
,
isLoading
,
toolMeta
)
=>
{
// 如果传递了 loading 状态,更新 isLoading
if
(
isLoading
!==
undefined
)
{
setIsLoading
(
isLoading
)
...
...
@@ -1192,7 +1307,7 @@ export const Chat: React.FC = () => {
// 只在真正的工具切换时触发事件(有 conversationId 时),避免 loading 状态更新时重复触发
if
(
conversationId
)
{
window
.
dispatchEvent
(
new
CustomEvent
(
'toolButtonClick'
,
{
detail
:
{
isToolBtn
,
toolId
,
toolName
,
shouldChangeStyle
,
conversationId
,
source
:
'toolButton'
},
detail
:
{
isToolBtn
,
toolId
,
toolName
,
shouldChangeStyle
,
conversationId
,
source
:
'toolButton'
,
toolMeta
},
}))
}
}
}
...
...
src/pages/Chat/components/ChatItem/ChatAnswerRecommend.tsx
View file @
e879c818
...
...
@@ -3,6 +3,7 @@ import { useSearchParams } from 'react-router-dom'
import
{
Button
,
Skeleton
}
from
'@heroui/react'
import
type
{
Answer
}
from
'@/types/chat'
import
{
fetchOrderQueryRecommendQuestion
}
from
'@/api/chat'
import
{
findToolRowBySessionToolId
,
getRecommendRecordTypeForToolRow
,
readToolListFromSessionStorage
}
from
'@/lib/toolRoleRecordTypes'
import
SendIcon
from
'@/assets/svg/sendBlack.svg?react'
interface
ChatAnswerRecommendProps
{
...
...
@@ -47,17 +48,13 @@ export const ChatAnswerRecommend: React.FC<ChatAnswerRecommendProps> = ({ answer
recordType
=
'A19'
}
}
// 非策略场景
else
{
// 制度活化:toolId 为空或不存在
if
(
!
toolId
)
{
const
list
=
readToolListFromSessionStorage
()
const
row
=
findToolRowBySessionToolId
(
list
,
toolId
||
''
)
const
rt
=
getRecommendRecordTypeForToolRow
(
row
)
if
(
rt
)
{
busiType
=
'01'
recordType
=
'A17'
}
// 特殊工具ID
else
if
(
toolId
===
'6712395743241'
)
{
busiType
=
'01'
recordType
=
'A15'
recordType
=
rt
}
}
...
...
src/pages/Chat/components/ChatWelcome/index.tsx
View file @
e879c818
...
...
@@ -2,6 +2,7 @@ import { Avatar } from '@heroui/react'
import
{
motion
}
from
'framer-motion'
import
AvatarBot
from
'@/assets/avatarBot.png'
import
AIIcon
from
'@/assets/ai-icon.png'
import
{
findToolRowBySessionToolId
,
readToolListFromSessionStorage
}
from
'@/lib/toolRoleRecordTypes'
interface
ChatWelcomeProps
{
toolName
?:
string
...
...
@@ -11,39 +12,31 @@ interface ChatWelcomeProps {
export
const
ChatWelcome
:
React
.
FC
<
ChatWelcomeProps
>
=
({
toolName
:
_toolName
,
welcomeText
})
=>
{
const
viteOutputObj
=
import
.
meta
.
env
.
VITE_OUTPUT_OBJ
||
'open'
// 根据不同的 toolName 显示不同的提示语
const
getWelcomeText
=
()
=>
{
// 优先使用从接口返回的欢迎语(历史逻辑)
if
(
welcomeText
)
{
return
welcomeText
}
// 回退到基于当前工具的欢迎语逻辑
const
currentToolId
=
typeof
window
!==
'undefined'
?
sessionStorage
.
getItem
(
'currentToolId'
)
:
''
const
list
=
readToolListFromSessionStorage
()
const
row
=
findToolRowBySessionToolId
(
list
,
currentToolId
||
''
)
const
name
=
row
?.
toolName
?.
trim
()
// 提质增效:若 sessionStorage 中已有自动调用写入的欢迎语,优先使用该文案
if
(
currentToolId
===
'6712395743241'
)
{
try
{
const
stored
=
typeof
window
!==
'undefined'
?
sessionStorage
.
getItem
(
'quality
WelcomeText'
)
:
null
if
(
stored
&&
stored
.
trim
())
{
const
stored
=
typeof
window
!==
'undefined'
?
sessionStorage
.
getItem
(
'toolStream
WelcomeText'
)
:
null
if
(
stored
&&
stored
.
trim
()
&&
currentToolId
?
.
trim
())
{
return
stored
}
}
catch
{
// 读取失败时回退到默认文案
}
return
'Hi,我是AI提质增效助手,可以为您解答研发需求计划和业务功能流程等相关问题。请问有什么可以帮您?'
}
if
(
currentToolId
===
'6712395743240'
)
{
return
'Hi~我是您的数据助手,可以帮你查询业务数据哦'
// 读取失败时回退
}
if
(
currentToolId
===
'6712395743242'
)
{
return
'业务洞察助手姨就位!当前支持客群洞察,分析客户特征、识别客群价值,请告诉我您的需求吧~'
if
(
name
)
{
return
`Hi,我是
${
name
}
,请问有什么可以帮您?`
}
return
'Hi,我是
AI制度活化助手,可以为您提供公司规章制度、政策条款的查询与解读服务。请问您想了解什么呢
?'
return
'Hi,我是
您的智能助手,请问有什么可以帮您
?'
}
return
(
...
...
@@ -62,7 +55,6 @@ export const ChatWelcome: React.FC<ChatWelcomeProps> = ({ toolName: _toolName, w
>
{
getWelcomeText
()
}
</
p
>
{
/* <p className="text-[15px] mt-[4px] sm:mt-[8px] sm:text-13px text-[#27353C] font-300">作为您的智能保险伙伴,您有各类专业相关的问题都可以抛给我哟~让我们互相帮助共同成长吧~</p> */
}
</
div
>
</
motion
.
div
>
</
div
>
...
...
src/pages/Chat/helper.ts
View file @
e879c818
import
type
{
ChatRecord
,
OriginalRecord
}
from
'@/types/chat'
import
type
{
Answer
,
ChatRecord
,
OriginalRecord
}
from
'@/types/chat'
/**
* 从 query_user_qa_record_list 返回列表的「最后一条」中提取 workflow 上下文。
* 优先在最后一条记录的 answerList 中从后往前找到同时带有 recordType 与 workFlowSessionId 的答案。
*/
export
function
getLastQaRecordWorkflowContext
(
qaRecords
:
OriginalRecord
[]
|
null
|
undefined
):
{
recordType
?:
string
workFlowSessionId
?:
string
busiType
?:
string
}
{
if
(
!
Array
.
isArray
(
qaRecords
)
||
qaRecords
.
length
===
0
)
return
{}
const
lastRecord
=
qaRecords
[
qaRecords
.
length
-
1
]
const
list
=
lastRecord
?.
answerList
if
(
!
Array
.
isArray
(
list
)
||
list
.
length
===
0
)
return
{}
for
(
let
i
=
list
.
length
-
1
;
i
>=
0
;
i
--
)
{
const
a
=
list
[
i
]
as
Answer
const
recordType
=
a
?.
recordType
!=
null
?
String
(
a
.
recordType
).
trim
()
:
''
const
workFlowSessionId
=
a
?.
workFlowSessionId
!=
null
?
String
(
a
.
workFlowSessionId
).
trim
()
:
''
if
(
recordType
&&
workFlowSessionId
)
{
const
busiType
=
a
?.
busiType
!=
null
?
String
(
a
.
busiType
).
trim
()
:
''
return
{
recordType
,
workFlowSessionId
,
...(
busiType
?
{
busiType
}
:
{}),
}
}
}
return
{}
}
export
function
processApiResponse
(
data
:
OriginalRecord
[]):
ChatRecord
[]
{
const
chatRecord
:
ChatRecord
[]
=
[]
...
...
src/pages/Home/HomeNew.tsx
View file @
e879c818
...
...
@@ -15,6 +15,13 @@ import { fetchLoginByToken, fetchLoginByUid } from '@/api/common'
import
{
fetchSessionConversationId
}
from
'@/api/conversation'
import
{
fetchBusinessInsightList
,
fetchCheckTokenApi
}
from
'@/api/chat'
import
{
getUserRolesFromRoute
,
safeLocalStorageGetItem
,
safeLocalStorageSetItem
,
safeSessionStorageGetItem
,
safeSessionStorageRemoveItem
,
safeSessionStorageSetItem
,
waitForToken
}
from
'@/lib/utils'
import
{
findToolRowBySessionToolId
,
getBusinessInsightConfigTypesForRow
,
getCommonQuestionRecordTypeForToolRow
,
readToolListFromSessionStorage
,
toolRowUsesBusinessInsightCommonQuestions
,
}
from
'@/lib/toolRoleRecordTypes'
// 从 localStorage 获取 token 的辅助函数
function
getTokenFromStorage
():
string
|
null
{
...
...
@@ -68,6 +75,7 @@ export const Home: React.FC = () => {
const
dispatch
=
useAppDispatch
()
const
location
=
useLocation
()
const
currentConversationId
=
useAppSelector
(
state
=>
state
.
conversation
.
currentConversationId
)
const
businessInsightConfigTypesRef
=
useRef
<
string
[]
>
([
'10'
])
const
hasFetched
=
useRef
(
false
)
const
hasCalledBusinessApis
=
useRef
(
false
)
// 防止业务接口重复调用
const
prevPathRef
=
useRef
<
string
>
(
location
.
pathname
)
// 记录上一次路径,用于检测从收藏页面返回
...
...
@@ -106,7 +114,7 @@ export const Home: React.FC = () => {
}
const
fetchBusinessInsightQuestions
=
useCallback
(
async
()
=>
{
const
res
=
await
fetchBusinessInsightList
({
configTypeList
:
[
'10'
]
})
const
res
=
await
fetchBusinessInsightList
({
configTypeList
:
businessInsightConfigTypesRef
.
current
})
const
data
=
Array
.
isArray
(
res
?.
data
)
?
res
.
data
[
0
]
:
res
?.
data
const
normalized
=
parseBusinessInsightQuestions
(
data
?.
content
??
data
?.
questionList
)
setOtherQuestions
((
prev
:
any
)
=>
({
...
...
@@ -116,20 +124,11 @@ export const Home: React.FC = () => {
}))
},
[])
// 根据 toolId 获取对应的 recordType
const
getRecordTypeByToolId
=
(
toolId
:
string
|
null
|
undefined
):
string
=>
{
if
(
!
toolId
)
{
return
'A16'
// 制度活化
}
if
(
toolId
===
'6712395743240'
)
{
return
'A18'
// 数据助手
}
if
(
toolId
===
'6712395743241'
)
{
return
'A14'
// 提质增效
}
// 未知的 toolId,默认返回制度活化
return
'A16'
}
const
resolveCommonQuestionRecordType
=
useCallback
(
async
(
sessionToolId
:
string
):
Promise
<
string
>
=>
{
const
list
=
readToolListFromSessionStorage
()
const
row
=
findToolRowBySessionToolId
(
list
,
sessionToolId
)
return
getCommonQuestionRecordTypeForToolRow
(
row
)
||
'01'
},
[])
// 获取会话ID并加载历史记录
const
getSessionConversationId
=
async
(
data
?:
any
):
Promise
<
string
|
null
>
=>
{
...
...
@@ -185,9 +184,8 @@ export const Home: React.FC = () => {
return
}
// 从 sessionStorage 获取 toolId 并确定 recordType
const
toolId
=
safeSessionStorageGetItem
(
'currentToolId'
)
||
''
const
recordType
=
getRecordTypeByToolId
(
toolId
)
const
recordType
=
await
resolveCommonQuestionRecordType
(
toolId
)
const
res
=
await
fetchEfficiencyQuestionList
({
conversationId
,
...
...
@@ -208,7 +206,7 @@ export const Home: React.FC = () => {
finally
{
setIsDataLoaded
(
true
)
// 无论成功失败都标记为已加载
}
},
[
location
.
search
,
isBusinessInsightMode
,
fetchBusinessInsightQuestions
])
},
[
location
.
search
,
isBusinessInsightMode
,
fetchBusinessInsightQuestions
,
resolveCommonQuestionRecordType
])
// 处理工具按钮点击
const
_handleToolClick
=
useCallback
(
async
(
isToolBtn
:
boolean
,
toolId
?:
string
,
ignoreUrlToolId
?:
boolean
,
conversationIdParam
?:
string
|
null
)
=>
{
...
...
@@ -249,9 +247,8 @@ export const Home: React.FC = () => {
return
}
// 从 sessionStorage 获取 toolId 并确定 recordType
const
currentToolId
=
safeSessionStorageGetItem
(
'currentToolId'
)
||
''
const
recordType
=
getRecordTypeByToolId
(
currentToolId
)
const
recordType
=
await
resolveCommonQuestionRecordType
(
currentToolId
)
// 调用真实 API 获取常见问题列表
const
res
=
await
fetchEfficiencyQuestionList
({
...
...
@@ -272,12 +269,12 @@ export const Home: React.FC = () => {
finally
{
setIsDataLoaded
(
true
)
// 无论成功失败都标记为已加载
}
},
[
originalOtherQuestions
,
location
.
search
,
location
.
pathname
,
currentConversationId
])
},
[
originalOtherQuestions
,
location
.
search
,
location
.
pathname
,
currentConversationId
,
resolveCommonQuestionRecordType
])
// 监听工具按钮点击事件
useEffect
(()
=>
{
const
handleToolClickEvent
=
(
event
:
CustomEvent
)
=>
{
const
{
isToolBtn
,
toolId
,
conversationId
,
toolName
,
source
}
=
event
.
detail
const
{
isToolBtn
,
toolId
,
conversationId
,
source
}
=
event
.
detail
setIsToolBtnActive
(
isToolBtn
)
// 更新样式控制状态
setShouldChangeStyle
(
true
)
...
...
@@ -292,16 +289,14 @@ export const Home: React.FC = () => {
dispatch
(
clearCurrentToolId
())
}
const
isBusinessInsightClick
=
source
===
'toolButton'
&&
toolName
===
'业务洞察'
const
list
=
readToolListFromSessionStorage
()
const
rowForTool
=
findToolRowBySessionToolId
(
list
,
toolId
||
''
)
const
isBusinessInsightClick
=
source
===
'toolButton'
&&
toolRowUsesBusinessInsightCommonQuestions
(
rowForTool
)
// 仅在点击“业务洞察”工具按钮时调用业务洞察常见问题接口;其余情况保持原有逻辑
if
(
isBusinessInsightClick
)
{
setIsBusinessInsightMode
(
true
)
// 清空业务洞察工作流状态
dispatch
(
clearBusinessInsightWorkflow
())
// 保存业务洞察工具ID
dispatch
(
setCurrentToolId
(
'6712395743242'
))
safeSessionStorageSetItem
(
'currentToolId'
,
'6712395743242'
)
businessInsightConfigTypesRef
.
current
=
getBusinessInsightConfigTypesForRow
(
rowForTool
)
// 先清空旧数据
setOtherQuestions
((
prev
:
any
)
=>
({
...
prev
,
...
...
src/types/toolList.ts
0 → 100644
View file @
e879c818
/**
* get_tool_list 单条(字段以后端为准;部分为前端可选扩展)
*/
export
interface
ToolListRow
{
toolId
?:
string
|
number
|
null
toolName
?:
string
|
null
userRole
?:
string
|
null
/** 兼容旧字段:无 userRole 时可回退 */
recordType
?:
string
|
null
toolType
?:
string
|
null
toolIcon
?:
string
|
null
/** 左侧常见问题走业务洞察配置接口时传入的 configType 列表,如 ['10'] */
businessInsightConfigTypes
?:
string
[]
|
null
/** 左侧常见问题接口类型:efficiency = query_strategy_recommend_question;business_insight = query_config_list */
commonQuestionApi
?:
'efficiency'
|
'business_insight'
|
null
/** 为 true 时展示数据类输入提示并跳过答后推荐问请求 */
showStructuredQueryPlaceholder
?:
boolean
|
null
/**
* 为 true 或 1 时表示该列表项对应「无 busiId / 通用」会话(与 toolId 是否为空无关,便于后端仍返回 toolId)
*/
isGeneralSession
?:
boolean
|
number
|
null
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment