Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
A
ai-chat-xy
Project
Project
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
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
ai-chat-xy
Commits
c49ab07e
Commit
c49ab07e
authored
Jun 15, 2026
by
David Yang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 应用切换
parent
3489b2a6
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
1806 additions
and
353 deletions
+1806
-353
.umirc.ts
.umirc.ts
+3
-3
manifest.json
public/manifest.json
+6
-3
附件1_费用开支明细表_XXX_XXX有限公司2024年产品审计及其他鉴证业务.xlsx
...ic/templates/附件1_费用开支明细表_XXX_XXX有限公司2024年产品审计及其他鉴证业务.xlsx
+0
-0
附件2_可抵扣交通费明细表_XXX_XXX有限公司2024年产品审计及其他鉴证业务.xlsx
.../templates/附件2_可抵扣交通费明细表_XXX_XXX有限公司2024年产品审计及其他鉴证业务.xlsx
+0
-0
附件3_电子发票登记表_XXX_XXX有限公司2024年产品审计及其他鉴证业务.xlsx
...ic/templates/附件3_电子发票登记表_XXX_XXX有限公司2024年产品审计及其他鉴证业务.xlsx
+0
-0
index.jsx
src/components/ApplicationSwitcherDialog/index.jsx
+870
-0
index.jsx
src/components/LoginModal/index.jsx
+124
-8
index.jsx
src/layouts/index.jsx
+15
-10
index.jsx
src/pages/Chat/index.jsx
+420
-150
index.jsx
src/pages/Home/index.jsx
+347
-178
DataService.js
src/services/DataService.js
+21
-1
No files found.
.umirc.ts
View file @
c49ab07e
import
{
defineConfig
}
from
'@umijs/max'
;
import
{
defineConfig
}
from
'@umijs/max'
;
export
default
defineConfig
({
export
default
defineConfig
({
base
:
'
ai
'
,
base
:
'
/ai_chat/
'
,
publicPath
:
'/ai/'
,
publicPath
:
'/ai
_chat
/'
,
antd
:
{},
antd
:
{},
access
:
{},
access
:
{},
model
:
{},
model
:
{},
...
@@ -33,7 +33,7 @@ export default defineConfig({
...
@@ -33,7 +33,7 @@ export default defineConfig({
npmClient
:
'pnpm'
,
npmClient
:
'pnpm'
,
proxy
:
{
proxy
:
{
'/api'
:
{
'/api'
:
{
target
:
'https://
awm.rsmchina.com.cn
/api'
,
target
:
'https://
xy.streams7.com
/api'
,
// target: 'http://localhost:3000',
// target: 'http://localhost:3000',
changeOrigin
:
true
,
changeOrigin
:
true
,
pathRewrite
:
{
'^/api'
:
''
},
pathRewrite
:
{
'^/api'
:
''
},
...
...
public/manifest.json
View file @
c49ab07e
{
{
"name"
:
"S-Agent"
,
"name"
:
"ai_chat"
,
"version"
:
"1.0.26"
,
"version"
:
"1.0.1"
,
"build"
:
"1"
"build"
:
"abc"
,
"description"
:
"merge master"
,
"date"
:
"2025-07-14 19:27:20"
,
"author"
:
"yang"
}
}
public/templates/附件1_费用开支明细表_XXX_XXX有限公司2024年产品审计及其他鉴证业务.xlsx
deleted
100644 → 0
View file @
3489b2a6
File deleted
public/templates/附件2_可抵扣交通费明细表_XXX_XXX有限公司2024年产品审计及其他鉴证业务.xlsx
deleted
100644 → 0
View file @
3489b2a6
File deleted
public/templates/附件3_电子发票登记表_XXX_XXX有限公司2024年产品审计及其他鉴证业务.xlsx
deleted
100644 → 0
View file @
3489b2a6
File deleted
src/components/ApplicationSwitcherDialog/index.jsx
0 → 100644
View file @
c49ab07e
import
{
AppstoreOutlined
,
ExclamationCircleOutlined
,
ExportOutlined
,
ThunderboltOutlined
,
}
from
'@ant-design/icons'
;
import
{
Alert
,
Empty
,
Modal
,
Skeleton
}
from
'antd'
;
import
{
createStyles
}
from
'antd-style'
;
import
{
useEffect
,
useMemo
,
useState
}
from
'react'
;
import
{
getApplications
,
getSystemInfo
}
from
'../../services/DataService'
;
function
normalizeLabelList
(
items
)
{
if
(
!
Array
.
isArray
(
items
))
{
return
[];
}
return
items
.
map
((
item
)
=>
{
if
(
typeof
item
===
'string'
)
{
return
item
;
}
return
item
?.
display_name
||
item
?.
title
||
item
?.
name
||
''
;
})
.
filter
(
Boolean
);
}
function
resolveApplicationUrl
(
application
)
{
if
(
application
?.
name
)
{
return
`/
${
application
.
name
}
`
;
}
return
'#'
;
}
function
normalizeApplication
(
application
,
fallback
=
{})
{
const
name
=
application
?.
name
||
fallback
?.
name
||
''
;
return
{
name
,
title
:
application
?.
title
||
fallback
?.
title
||
application
?.
name
||
fallback
?.
name
||
'未命名应用'
,
description
:
application
?.
description
||
fallback
?.
description
||
fallback
?.
title
||
application
?.
title
||
'进入对应应用继续处理业务'
,
logo
:
application
?.
logo
||
fallback
?.
logo
||
null
,
url
:
resolveApplicationUrl
({
name
}),
access
:
application
?.
access
||
fallback
?.
access
||
''
,
status
:
application
?.
status
||
fallback
?.
status
||
''
,
type
:
application
?.
type
||
fallback
?.
type
||
''
,
order
:
application
?.
order
??
fallback
?.
order
??
0
,
roles
:
normalizeLabelList
(
application
?.
roles
?.
length
?
application
.
roles
:
fallback
?.
roles
,
),
groups
:
normalizeLabelList
(
application
?.
groups
?.
length
?
application
.
groups
:
fallback
?.
groups
,
),
};
}
function
formatAccessLabel
(
access
)
{
if
(
access
===
'public'
)
{
return
'公开'
;
}
if
(
access
===
'protected'
)
{
return
'受限'
;
}
if
(
access
===
'private'
)
{
return
'私有'
;
}
if
(
access
===
'internal'
)
{
return
'内部'
;
}
return
''
;
}
function
extractApplications
(
response
)
{
if
(
Array
.
isArray
(
response
))
{
return
response
;
}
if
(
Array
.
isArray
(
response
?.
records
))
{
return
response
.
records
;
}
if
(
Array
.
isArray
(
response
?.
applications
))
{
return
response
.
applications
;
}
if
(
Array
.
isArray
(
response
?.
data
))
{
return
response
.
data
;
}
return
[];
}
function
mergeApplications
(
fetchedApplications
,
fallbackApplications
)
{
const
fallbackMap
=
new
Map
(
fallbackApplications
.
map
((
application
)
=>
[
application
.
name
,
application
]),
);
const
normalizedFetched
=
fetchedApplications
.
map
((
application
)
=>
normalizeApplication
(
application
,
fallbackMap
.
get
(
application
?.
name
)),
)
.
filter
((
application
)
=>
application
.
name
);
const
mergedMap
=
new
Map
(
normalizedFetched
.
map
((
application
)
=>
[
application
.
name
,
application
]),
);
fallbackApplications
.
forEach
((
application
)
=>
{
if
(
!
application
?.
name
||
mergedMap
.
has
(
application
.
name
))
{
return
;
}
mergedMap
.
set
(
application
.
name
,
normalizeApplication
(
application
,
application
),
);
});
return
Array
.
from
(
mergedMap
.
values
()).
sort
((
left
,
right
)
=>
{
const
orderDifference
=
Number
(
left
.
order
||
0
)
-
Number
(
right
.
order
||
0
);
if
(
orderDifference
!==
0
)
{
return
orderDifference
;
}
return
left
.
title
.
localeCompare
(
right
.
title
,
'zh-CN'
);
});
}
function
buildApplicationSections
(
applications
,
applicationGroups
)
{
if
(
!
Array
.
isArray
(
applicationGroups
)
||
applicationGroups
.
length
===
0
)
{
return
[
{
key
:
'all'
,
label
:
'全部应用'
,
applications
,
},
];
}
const
applicationMap
=
new
Map
(
applications
.
map
((
application
)
=>
[
application
.
name
,
application
]),
);
const
groupedApplicationNames
=
new
Set
();
const
sections
=
applicationGroups
.
map
((
group
,
index
)
=>
{
const
groupApplications
=
Array
.
isArray
(
group
?.
applications
)
?
group
.
applications
.
map
((
name
)
=>
{
const
application
=
applicationMap
.
get
(
name
);
if
(
application
)
{
groupedApplicationNames
.
add
(
name
);
}
return
application
;
})
.
filter
(
Boolean
)
:
[];
return
{
key
:
group
?.
label
||
`group-
${
index
}
`
,
label
:
group
?.
label
||
'应用分组'
,
applications
:
groupApplications
,
};
})
.
filter
((
section
)
=>
section
.
applications
.
length
>
0
);
const
ungroupedApplications
=
applications
.
filter
(
(
application
)
=>
!
groupedApplicationNames
.
has
(
application
.
name
),
);
if
(
ungroupedApplications
.
length
>
0
)
{
sections
.
push
({
key
:
'ungrouped'
,
label
:
'其他应用'
,
applications
:
ungroupedApplications
,
});
}
return
sections
;
}
function
isCurrentApplication
(
application
)
{
if
(
typeof
window
===
'undefined'
)
{
return
false
;
}
const
currentPath
=
window
.
location
.
pathname
;
const
applicationRoot
=
application
?.
name
?
`/
${
application
.
name
}
`
:
''
;
if
(
application
?.
url
?.
startsWith
(
'http'
))
{
try
{
const
url
=
new
URL
(
application
.
url
);
return
url
.
pathname
===
currentPath
;
}
catch
(
error
)
{
return
false
;
}
}
return
(
application
?.
url
===
currentPath
||
(
!!
applicationRoot
&&
(
currentPath
===
applicationRoot
||
currentPath
.
startsWith
(
`
${
applicationRoot
}
/`
)))
);
}
const
useStyles
=
createStyles
(({
css
,
responsive
})
=>
({
modal
:
css
`
.ant-modal-content {
overflow: hidden;
border-radius: 28px;
border: 1px solid rgba(255, 255, 255, 0.72);
background: linear-gradient(
180deg,
rgba(255, 255, 255, 0.96),
rgba(248, 250, 252, 0.94)
);
box-shadow: 0 30px 80px rgba(15, 23, 42, 0.22);
padding: 0;
}
.ant-modal-body {
padding: 0;
}
`
,
header
:
css
`
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
padding: 20px;
border-bottom: 1px solid rgba(228, 228, 231, 0.82);
${
responsive
.
xs
}
{
flex-direction: column;
padding: 18px;
}
`
,
eyebrow
:
css
`
display: inline-flex;
align-items: center;
gap: 8px;
height: 24px;
padding: 0 10px;
border-radius: 999px;
background: #fff1f2;
color: #e11d48;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
`
,
title
:
css
`
margin: 10px 0 0;
color: #020617;
font-size: 24px;
line-height: 1.25;
font-weight: 700;
letter-spacing: 0;
`
,
subtitle
:
css
`
margin: 6px 0 0;
max-width: 640px;
color: #64748b;
font-size: 13px;
line-height: 1.7;
`
,
headerActions
:
css
`
display: flex;
align-items: center;
gap: 12px;
`
,
countBox
:
css
`
min-width: 90px;
padding: 8px 12px;
border-radius: 16px;
border: 1px solid #ffe4e6;
background: rgba(255, 241, 242, 0.76);
text-align: right;
`
,
countLabel
:
css
`
color: #f43f5e;
font-size: 11px;
line-height: 1.2;
font-weight: 600;
`
,
countValue
:
css
`
margin-top: 4px;
color: #020617;
font-size: 22px;
line-height: 1;
font-weight: 700;
`
,
closeButton
:
css
`
height: 40px;
padding: 0 14px;
border: 1px solid #e4e4e7;
border-radius: 12px;
background: #ffffff;
color: #475569;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease;
&:hover {
border-color: #d4d4d8;
color: #0f172a;
background: #fafafa;
}
`
,
body
:
css
`
max-height: min(68dvh, 720px);
overflow-y: auto;
padding: 18px 20px 20px;
scrollbar-gutter: stable;
${
responsive
.
xs
}
{
max-height: 64dvh;
padding: 16px 18px 18px;
}
`
,
section
:
css
`
& + & {
margin-top: 18px;
}
`
,
sectionHeader
:
css
`
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 10px;
`
,
sectionTitle
:
css
`
color: #0f172a;
font-size: 14px;
font-weight: 700;
white-space: nowrap;
`
,
sectionLine
:
css
`
height: 1px;
flex: 1;
background: rgba(228, 228, 231, 0.86);
`
,
sectionCount
:
css
`
color: #94a3b8;
font-size: 12px;
font-weight: 600;
`
,
grid
:
css
`
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
@media (min-width: 1680px) {
grid-template-columns: repeat(6, minmax(0, 1fr));
}
${
responsive
.
lg
}
{
grid-template-columns: repeat(3, minmax(0, 1fr));
}
${
responsive
.
md
}
{
grid-template-columns: repeat(2, minmax(0, 1fr));
}
${
responsive
.
xs
}
{
grid-template-columns: 1fr;
}
`
,
card
:
css
`
position: relative;
min-height: 84px;
overflow: hidden;
display: block;
padding: 10px;
border-radius: 16px;
border: 1px solid rgba(228, 228, 231, 0.88);
background: linear-gradient(
180deg,
rgba(255, 255, 255, 0.98),
rgba(248, 250, 252, 0.94)
);
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.06);
color: inherit;
text-decoration: none;
transition: transform 0.2s ease, border-color 0.2s ease,
box-shadow 0.2s ease;
&:hover {
transform: translateY(-2px);
border-color: #fecdd3;
box-shadow: 0 16px 32px rgba(244, 63, 94, 0.12);
color: inherit;
}
&:focus-visible {
outline: 2px solid rgba(244, 63, 94, 0.48);
outline-offset: 2px;
}
`
,
cardCurrent
:
css
`
border-color: #fda4af;
background: linear-gradient(
180deg,
rgba(255, 241, 242, 0.95),
rgba(255, 255, 255, 0.96)
);
`
,
cardGlow
:
css
`
position: absolute;
inset: 0 0 auto;
height: 56px;
background: radial-gradient(
circle at top right,
rgba(251, 113, 133, 0.14),
transparent 50%
);
opacity: 0;
transition: opacity 0.2s ease;
a:hover & {
opacity: 1;
}
`
,
cardTop
:
css
`
position: relative;
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
`
,
cardMain
:
css
`
min-width: 0;
display: flex;
align-items: center;
gap: 10px;
`
,
logo
:
css
`
width: 36px;
height: 36px;
flex-shrink: 0;
object-fit: cover;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.86);
background: #ffffff;
box-shadow: 0 4px 10px rgba(15, 23, 42, 0.08);
`
,
logoFallback
:
css
`
width: 36px;
height: 36px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background: linear-gradient(135deg, #b42318, #f43f5e);
color: #ffffff;
font-size: 14px;
font-weight: 700;
box-shadow: 0 8px 18px rgba(180, 35, 24, 0.22);
`
,
cardText
:
css
`
min-width: 0;
`
,
cardTitle
:
css
`
overflow: hidden;
color: #020617;
font-size: 14px;
line-height: 1.35;
font-weight: 700;
text-overflow: ellipsis;
white-space: nowrap;
`
,
cardDesc
:
css
`
margin-top: 3px;
overflow: hidden;
color: #64748b;
font-size: 11px;
line-height: 16px;
text-overflow: ellipsis;
white-space: nowrap;
`
,
cardActions
:
css
`
flex-shrink: 0;
display: inline-flex;
align-items: center;
gap: 8px;
`
,
currentBadge
:
css
`
height: 22px;
display: inline-flex;
align-items: center;
padding: 0 8px;
border-radius: 999px;
background: #ffe4e6;
color: #be123c;
font-size: 11px;
font-weight: 700;
`
,
openIcon
:
css
`
width: 28px;
height: 28px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 999px;
background: #f1f5f9;
color: #64748b;
transition: background 0.2s ease, color 0.2s ease;
a:hover & {
background: #fff1f2;
color: #e11d48;
}
`
,
tags
:
css
`
position: relative;
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 9px;
`
,
tag
:
css
`
display: inline-flex;
align-items: center;
max-width: 100%;
height: 22px;
padding: 0 8px;
border-radius: 999px;
background: #f4f4f5;
color: #3f3f46;
font-size: 11px;
line-height: 1;
font-weight: 600;
`
,
roleTag
:
css
`
background: #f1f5f9;
color: #334155;
`
,
groupTag
:
css
`
background: #fff1f2;
color: #be123c;
`
,
skeletonCard
:
css
`
min-height: 84px;
padding: 10px;
border-radius: 16px;
border: 1px solid #e4e4e7;
background: rgba(255, 255, 255, 0.92);
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
`
,
empty
:
css
`
min-height: 288px;
display: flex;
align-items: center;
justify-content: center;
border: 1px dashed #e4e4e7;
border-radius: 28px;
background: rgba(250, 250, 250, 0.82);
`
,
}));
function
ApplicationCard
({
application
})
{
const
{
styles
,
cx
}
=
useStyles
();
const
current
=
isCurrentApplication
(
application
);
const
target
=
application
?.
url
?.
startsWith
(
'http'
)
?
'_blank'
:
undefined
;
const
accessLabel
=
formatAccessLabel
(
application
.
access
);
const
firstRole
=
application
.
roles
?.[
0
];
const
firstGroup
=
application
.
groups
?.[
0
];
return
(
<
a
href=
{
application
.
url
}
target=
{
target
}
rel=
{
target
?
'noreferrer'
:
undefined
}
className=
{
cx
(
styles
.
card
,
current
&&
styles
.
cardCurrent
)
}
>
<
div
className=
{
styles
.
cardGlow
}
/>
<
div
className=
{
styles
.
cardTop
}
>
<
div
className=
{
styles
.
cardMain
}
>
{
application
.
logo
?
(
<
img
src=
{
application
.
logo
}
alt=
{
application
.
title
}
className=
{
styles
.
logo
}
/>
)
:
(
<
div
className=
{
styles
.
logoFallback
}
>
{
application
.
title
.
slice
(
0
,
1
)
}
</
div
>
)
}
<
div
className=
{
styles
.
cardText
}
>
<
div
className=
{
styles
.
cardTitle
}
>
{
application
.
title
}
</
div
>
<
div
className=
{
styles
.
cardDesc
}
>
{
application
.
description
}
</
div
>
</
div
>
</
div
>
<
div
className=
{
styles
.
cardActions
}
>
{
current
?
<
span
className=
{
styles
.
currentBadge
}
>
当前
</
span
>
:
null
}
<
span
className=
{
styles
.
openIcon
}
>
<
ExportOutlined
/>
</
span
>
</
div
>
</
div
>
{
accessLabel
||
firstRole
||
firstGroup
?
(
<
div
className=
{
styles
.
tags
}
>
{
accessLabel
?
(
<
span
className=
{
styles
.
tag
}
>
{
accessLabel
}
</
span
>
)
:
null
}
{
firstRole
?
(
<
span
className=
{
`${styles.tag} ${styles.roleTag}`
}
>
{
firstRole
}
</
span
>
)
:
null
}
{
firstGroup
?
(
<
span
className=
{
`${styles.tag} ${styles.groupTag}`
}
>
{
firstGroup
}
</
span
>
)
:
null
}
</
div
>
)
:
null
}
</
a
>
);
}
function
LoadingSkeleton
()
{
const
{
styles
}
=
useStyles
();
return
(
<
div
className=
{
styles
.
grid
}
>
{
Array
.
from
({
length
:
6
}).
map
((
_
,
index
)
=>
(
<
div
key=
{
index
}
className=
{
styles
.
skeletonCard
}
>
<
Skeleton
active
avatar
paragraph=
{
{
rows
:
2
}
}
title=
{
false
}
/>
</
div
>
))
}
</
div
>
);
}
export
default
function
ApplicationSwitcherDialog
({
open
,
onClose
,
userInfo
,
siteInfo
,
})
{
const
{
styles
}
=
useStyles
();
const
[
loadedSiteInfo
,
setLoadedSiteInfo
]
=
useState
(
null
);
const
fallbackApplications
=
useMemo
(()
=>
{
if
(
!
Array
.
isArray
(
userInfo
?.
applications
))
{
return
[];
}
return
userInfo
.
applications
.
map
((
application
)
=>
normalizeApplication
(
application
,
{
...
application
,
description
:
application
?.
title
,
}),
)
.
filter
((
application
)
=>
application
.
name
);
},
[
userInfo
]);
const
[
applications
,
setApplications
]
=
useState
(
fallbackApplications
);
const
[
status
,
setStatus
]
=
useState
(
fallbackApplications
.
length
>
0
?
'success'
:
'idle'
,
);
const
[
errorMessage
,
setErrorMessage
]
=
useState
(
''
);
const
effectiveSiteInfo
=
siteInfo
||
loadedSiteInfo
;
const
applicationSections
=
useMemo
(()
=>
{
return
buildApplicationSections
(
applications
,
effectiveSiteInfo
?.
group
);
},
[
applications
,
effectiveSiteInfo
?.
group
]);
useEffect
(()
=>
{
if
(
fallbackApplications
.
length
===
0
)
{
return
;
}
setApplications
((
previous
)
=>
{
if
(
previous
.
length
>
0
)
{
return
previous
;
}
return
fallbackApplications
;
});
},
[
fallbackApplications
]);
useEffect
(()
=>
{
if
(
!
open
||
siteInfo
?.
group
||
loadedSiteInfo
)
{
return
;
}
let
cancelled
=
false
;
const
loadSiteInfo
=
async
()
=>
{
try
{
const
response
=
await
getSystemInfo
();
if
(
cancelled
)
{
return
;
}
setLoadedSiteInfo
({
...(
response
?.
site
||
response
||
{}),
key
:
response
?.
key
,
});
}
catch
(
error
)
{
if
(
!
cancelled
)
{
setLoadedSiteInfo
({});
}
}
};
loadSiteInfo
();
return
()
=>
{
cancelled
=
true
;
};
},
[
loadedSiteInfo
,
open
,
siteInfo
?.
group
]);
useEffect
(()
=>
{
if
(
!
open
)
{
return
;
}
let
cancelled
=
false
;
const
loadApplications
=
async
()
=>
{
setStatus
((
previous
)
=>
(
previous
===
'success'
?
previous
:
'loading'
));
setErrorMessage
(
''
);
try
{
const
response
=
await
getApplications
();
const
fetchedApplications
=
extractApplications
(
response
);
if
(
cancelled
)
{
return
;
}
const
mergedApplications
=
mergeApplications
(
fetchedApplications
,
fallbackApplications
,
);
setApplications
(
mergedApplications
);
setStatus
(
'success'
);
}
catch
(
error
)
{
if
(
cancelled
)
{
return
;
}
setApplications
(
fallbackApplications
);
setStatus
(
'error'
);
setErrorMessage
(
'应用列表加载失败,已回退展示当前账号上的应用信息。'
);
}
};
loadApplications
();
return
()
=>
{
cancelled
=
true
;
};
},
[
fallbackApplications
,
open
]);
return
(
<
Modal
open=
{
open
}
centered
footer=
{
null
}
closeIcon=
{
null
}
width=
"min(92vw, 1472px)"
onCancel=
{
onClose
}
className=
{
styles
.
modal
}
>
<
div
className=
{
styles
.
header
}
>
<
div
>
<
div
className=
{
styles
.
eyebrow
}
>
<
ThunderboltOutlined
/>
Applications
</
div
>
<
h2
className=
{
styles
.
title
}
>
应用切换
</
h2
>
<
p
className=
{
styles
.
subtitle
}
>
这里展示当前账号可访问的全部应用,点击卡片即可进入对应工作区。
</
p
>
</
div
>
<
div
className=
{
styles
.
headerActions
}
>
<
div
className=
{
styles
.
countBox
}
>
<
div
className=
{
styles
.
countLabel
}
>
可见应用
</
div
>
<
div
className=
{
styles
.
countValue
}
>
{
applications
.
length
}
</
div
>
</
div
>
<
button
type=
"button"
onClick=
{
onClose
}
className=
{
styles
.
closeButton
}
>
关闭
</
button
>
</
div
>
</
div
>
<
div
className=
{
styles
.
body
}
>
{
status
===
'error'
&&
errorMessage
?
(
<
Alert
showIcon
type=
"warning"
icon=
{
<
ExclamationCircleOutlined
/>
}
message=
{
errorMessage
}
style=
{
{
marginBottom
:
16
}
}
/>
)
:
null
}
{
status
===
'loading'
&&
applications
.
length
===
0
?
(
<
LoadingSkeleton
/>
)
:
applications
.
length
>
0
?
(
applicationSections
.
map
((
section
)
=>
(
<
section
key=
{
section
.
key
}
className=
{
styles
.
section
}
>
<
div
className=
{
styles
.
sectionHeader
}
>
<
div
className=
{
styles
.
sectionTitle
}
>
{
section
.
label
}
</
div
>
<
span
className=
{
styles
.
sectionLine
}
/>
<
span
className=
{
styles
.
sectionCount
}
>
{
section
.
applications
.
length
}
</
span
>
</
div
>
<
div
className=
{
styles
.
grid
}
>
{
section
.
applications
.
map
((
application
)
=>
(
<
ApplicationCard
key=
{
application
.
name
}
application=
{
application
}
/>
))
}
</
div
>
</
section
>
))
)
:
(
<
div
className=
{
styles
.
empty
}
>
<
Empty
image=
{
<
AppstoreOutlined
style=
{
{
fontSize
:
42
}
}
/>
}
description=
{
<
span
>
暂无可切换应用
<
br
/>
当前账号还没有分配其它应用权限,后续分配后这里会自动展示。
</
span
>
}
/>
</
div
>
)
}
</
div
>
</
Modal
>
);
}
src/components/LoginModal/index.jsx
View file @
c49ab07e
import
{
login
}
from
'@/services/DataService'
;
import
{
getCaptcha
,
login
}
from
'@/services/DataService'
;
import
{
LockOutlined
,
UserOutlined
}
from
'@ant-design/icons'
;
import
{
LockOutlined
,
ReloadOutlined
,
SafetyCertificateOutlined
,
UserOutlined
,
}
from
'@ant-design/icons'
;
import
{
LoginForm
,
ProFormText
}
from
'@ant-design/pro-components'
;
import
{
LoginForm
,
ProFormText
}
from
'@ant-design/pro-components'
;
import
{
Alert
,
Flex
,
Modal
,
Typography
}
from
'antd'
;
import
{
Alert
,
Button
,
Flex
,
Modal
,
Typography
}
from
'antd'
;
import
{
createStyles
}
from
'antd-style'
;
import
{
createStyles
}
from
'antd-style'
;
import
{
useState
}
from
'react'
;
import
{
use
Callback
,
useEffect
,
useRef
,
use
State
}
from
'react'
;
const
useStyle
=
createStyles
(({
css
})
=>
{
const
useStyle
=
createStyles
(({
css
})
=>
{
return
{
return
{
...
@@ -14,10 +19,10 @@ const useStyle = createStyles(({ css }) => {
...
@@ -14,10 +19,10 @@ const useStyle = createStyles(({ css }) => {
overflow: hidden;
overflow: hidden;
background: radial-gradient(
background: radial-gradient(
circle at top right,
circle at top right,
rgba(
125, 211, 252, 0.22
),
rgba(
251, 207, 232, 0.26
),
transparent 20%
transparent 20%
),
),
linear-gradient(180deg, #ffffff 0%, #f
8fbff
100%);
linear-gradient(180deg, #ffffff 0%, #f
ff7f7
100%);
}
}
.ant-modal-body {
.ant-modal-body {
...
@@ -33,7 +38,7 @@ const useStyle = createStyles(({ css }) => {
...
@@ -33,7 +38,7 @@ const useStyle = createStyles(({ css }) => {
padding: 38px 34px;
padding: 38px 34px;
background: radial-gradient(
background: radial-gradient(
circle at top left,
circle at top left,
rgba(
0, 166, 232
, 0.22),
rgba(
200, 30, 58
, 0.22),
transparent 22%
transparent 22%
),
),
linear-gradient(160deg, #0f172a 0%, #1e293b 100%);
linear-gradient(160deg, #0f172a 0%, #1e293b 100%);
...
@@ -132,6 +137,8 @@ const useStyle = createStyles(({ css }) => {
...
@@ -132,6 +137,8 @@ const useStyle = createStyles(({ css }) => {
height: 46px;
height: 46px;
border-radius: 16px;
border-radius: 16px;
font-weight: 600;
font-weight: 600;
background: linear-gradient(135deg, #c81e3a 0%, #9f1239 100%);
border: none;
}
}
`
,
`
,
formInner
:
css
`
formInner
:
css
`
...
@@ -142,14 +149,76 @@ const useStyle = createStyles(({ css }) => {
...
@@ -142,14 +149,76 @@ const useStyle = createStyles(({ css }) => {
margin-bottom: 20px;
margin-bottom: 20px;
border-radius: 14px;
border-radius: 14px;
`
,
`
,
captchaRow
:
css
`
display: grid;
grid-template-columns: minmax(0, 1fr) 128px;
gap: 12px;
align-items: flex-start;
.ant-form-item {
margin-bottom: 0;
}
`
,
captchaButton
:
css
`
height: 48px;
border-radius: 16px;
padding: 0 10px;
overflow: hidden;
border-color: #e2e8f0;
color: #64748b;
.ant-btn-icon {
display: inline-flex;
}
`
,
captchaImage
:
css
`
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
svg {
width: 100%;
height: 100%;
display: block;
}
`
,
};
};
});
});
export
default
(
props
)
=>
{
export
default
(
props
)
=>
{
const
{
onError
,
onSuccess
}
=
props
;
const
{
onError
,
onSuccess
}
=
props
;
const
[
errorMessage
,
setErrorMessage
]
=
useState
(
null
);
const
[
errorMessage
,
setErrorMessage
]
=
useState
(
null
);
const
[
captchaData
,
setCaptchaData
]
=
useState
(
null
);
const
[
captchaLoading
,
setCaptchaLoading
]
=
useState
(
false
);
const
formRef
=
useRef
();
const
{
styles
}
=
useStyle
();
const
{
styles
}
=
useStyle
();
const
refreshCaptcha
=
useCallback
(
async
()
=>
{
setCaptchaLoading
(
true
);
try
{
const
nextCaptcha
=
await
getCaptcha
();
setCaptchaData
(
nextCaptcha
||
null
);
}
catch
(
error
)
{
const
nextErrorMessage
=
error
.
response
?.
data
?.
message
||
error
.
message
||
'验证码加载失败'
;
setCaptchaData
(
null
);
setErrorMessage
(
nextErrorMessage
);
}
finally
{
setCaptchaLoading
(
false
);
}
},
[]);
useEffect
(()
=>
{
if
(
props
.
open
)
{
setErrorMessage
(
null
);
formRef
.
current
?.
setFieldsValue
({
captcha
:
undefined
});
refreshCaptcha
();
}
},
[
props
.
open
,
refreshCaptcha
]);
return
(
return
(
<
Modal
<
Modal
{
...
props
}
{
...
props
}
...
@@ -189,12 +258,18 @@ export default (props) => {
...
@@ -189,12 +258,18 @@ export default (props) => {
<
div
className=
{
styles
.
formPane
}
>
<
div
className=
{
styles
.
formPane
}
>
<
div
className=
{
styles
.
formInner
}
>
<
div
className=
{
styles
.
formInner
}
>
<
LoginForm
<
LoginForm
formRef=
{
formRef
}
logo=
"https://oss.streams7.com/rsm/BfNsqGPGTKE2.svg"
logo=
"https://oss.streams7.com/rsm/BfNsqGPGTKE2.svg"
title=
"智能助手"
title=
"智能助手"
subTitle=
"登录后继续使用统一 AI 对话工作台"
subTitle=
"登录后继续使用统一 AI 对话工作台"
onFinish=
{
async
(
form
)
=>
{
onFinish=
{
async
(
form
)
=>
{
try
{
try
{
const
userInfo
=
await
login
(
form
.
username
,
form
.
password
);
const
userInfo
=
await
login
(
form
.
username
,
form
.
password
,
form
.
captcha
,
captchaData
?.
token
,
);
localStorage
.
setItem
(
localStorage
.
setItem
(
'current_user'
,
'current_user'
,
...
@@ -211,6 +286,8 @@ export default (props) => {
...
@@ -211,6 +286,8 @@ export default (props) => {
error
.
message
||
error
.
message
||
'网络错误'
;
'网络错误'
;
setErrorMessage
(
nextErrorMessage
);
setErrorMessage
(
nextErrorMessage
);
formRef
.
current
?.
setFieldsValue
({
captcha
:
undefined
});
refreshCaptcha
();
onError
?.(
nextErrorMessage
,
error
);
onError
?.(
nextErrorMessage
,
error
);
}
}
}
}
}
}
...
@@ -244,6 +321,45 @@ export default (props) => {
...
@@ -244,6 +321,45 @@ export default (props) => {
},
},
]
}
]
}
/>
/>
<
div
className=
{
styles
.
captchaRow
}
>
<
ProFormText
name=
"captcha"
fieldProps=
{
{
size
:
'large'
,
prefix
:
(
<
SafetyCertificateOutlined
className=
{
'prefixIcon'
}
/>
),
}
}
placeholder=
"验证码"
rules=
{
[
{
required
:
true
,
message
:
'请输入验证码'
,
},
]
}
/>
<
Button
htmlType=
"button"
className=
{
styles
.
captchaButton
}
loading=
{
captchaLoading
}
disabled=
{
captchaLoading
}
icon=
{
captchaData
?.
image
?
undefined
:
<
ReloadOutlined
/>
}
onClick=
{
()
=>
{
setErrorMessage
(
null
);
formRef
.
current
?.
setFieldsValue
({
captcha
:
undefined
});
refreshCaptcha
();
}
}
>
{
captchaData
?.
image
?
(
<
span
className=
{
styles
.
captchaImage
}
dangerouslySetInnerHTML=
{
{
__html
:
captchaData
.
image
}
}
/>
)
:
(
'刷新'
)
}
</
Button
>
</
div
>
{
errorMessage
&&
(
{
errorMessage
&&
(
<
Alert
<
Alert
className=
{
styles
.
error
}
className=
{
styles
.
error
}
...
...
src/layouts/index.jsx
View file @
c49ab07e
...
@@ -12,17 +12,19 @@ const GlobalStyle = createGlobalStyle`
...
@@ -12,17 +12,19 @@ const GlobalStyle = createGlobalStyle`
--app-shell-bg:
--app-shell-bg:
radial-gradient(
radial-gradient(
circle at top left,
circle at top left,
rgba(
0, 156, 217, 0.16
),
rgba(
200, 30, 58, 0.14
),
transparent 26%
transparent 26%
),
),
radial-gradient(
radial-gradient(
circle at
bottom right
,
circle at
right center
,
rgba(1
26, 34, 206
, 0.08),
rgba(1
59, 18, 57
, 0.08),
transparent 2
6
%
transparent 2
4
%
),
),
linear-gradient(180deg, #f
8fbff 0%, #eef4fb
100%);
linear-gradient(180deg, #f
ff7f7 0%, #fff1f2
100%);
--app-surface: rgba(255, 255, 255, 0.86);
--app-surface: rgba(255, 255, 255, 0.86);
--app-surface-strong: #ffffff;
--app-surface-strong: #ffffff;
--app-brand: #c81e3a;
--app-brand-strong: #9f1239;
--app-sidebar: #0f172a;
--app-sidebar: #0f172a;
--app-sidebar-soft: rgba(30, 41, 59, 0.9);
--app-sidebar-soft: rgba(30, 41, 59, 0.9);
--app-border: rgba(148, 163, 184, 0.2);
--app-border: rgba(148, 163, 184, 0.2);
...
@@ -38,7 +40,10 @@ const GlobalStyle = createGlobalStyle`
...
@@ -38,7 +40,10 @@ const GlobalStyle = createGlobalStyle`
html,
html,
body,
body,
#root {
#root {
width: 100%;
height: 100%;
min-height: 100%;
min-height: 100%;
overflow: hidden;
}
}
body {
body {
...
@@ -84,12 +89,12 @@ const GlobalStyle = createGlobalStyle`
...
@@ -84,12 +89,12 @@ const GlobalStyle = createGlobalStyle`
export
default
()
=>
{
export
default
()
=>
{
const
themeToken
=
defaultAlgorithm
({
const
themeToken
=
defaultAlgorithm
({
...
defaultSeed
,
...
defaultSeed
,
colorPrimary
:
'#
009cd9
'
,
colorPrimary
:
'#
c81e3a
'
,
colorInfo
:
'#
009cd9
'
,
colorInfo
:
'#
c81e3a
'
,
colorSuccess
:
'#1d9b62'
,
colorSuccess
:
'#1d9b62'
,
colorWarning
:
'#d97706'
,
colorWarning
:
'#d97706'
,
colorError
:
'#dc2626'
,
colorError
:
'#dc2626'
,
colorBgBase
:
'#f
6f9fc
'
,
colorBgBase
:
'#f
ff7f7
'
,
colorBgContainer
:
'#ffffff'
,
colorBgContainer
:
'#ffffff'
,
colorTextBase
:
'#0f172a'
,
colorTextBase
:
'#0f172a'
,
borderRadius
:
18
,
borderRadius
:
18
,
...
@@ -121,7 +126,7 @@ export default () => {
...
@@ -121,7 +126,7 @@ export default () => {
},
},
Input
:
{
Input
:
{
borderRadius
:
14
,
borderRadius
:
14
,
activeShadow
:
'0 0 0 3px rgba(
0, 156, 217, 0.12
)'
,
activeShadow
:
'0 0 0 3px rgba(
200, 30, 58, 0.14
)'
,
},
},
Modal
:
{
Modal
:
{
borderRadiusLG
:
24
,
borderRadiusLG
:
24
,
...
@@ -134,7 +139,7 @@ export default () => {
...
@@ -134,7 +139,7 @@ export default () => {
colorBgElevated
:
'#0f172a'
,
colorBgElevated
:
'#0f172a'
,
},
},
Steps
:
{
Steps
:
{
colorPrimary
:
'#
009cd9
'
,
colorPrimary
:
'#
c81e3a
'
,
},
},
},
},
}
}
}
}
...
...
src/pages/Chat/index.jsx
View file @
c49ab07e
import
{
import
{
AuditOutlined
,
BulbOutlined
,
BulbOutlined
,
CloudUploadOutlined
,
CloudUploadOutlined
,
CopyOutlined
,
CopyOutlined
,
DatabaseOutlined
,
FileDoneOutlined
,
FileSearchOutlined
,
LoadingOutlined
,
LoadingOutlined
,
PaperClipOutlined
,
PaperClipOutlined
,
PayCircleOutlined
,
PayCircleOutlined
,
...
@@ -18,15 +22,12 @@ import {
...
@@ -18,15 +22,12 @@ import {
Welcome
,
Welcome
,
XStream
,
XStream
,
}
from
'@ant-design/x'
;
}
from
'@ant-design/x'
;
import
{
useQuery
}
from
'@umijs/max'
;
import
{
import
{
Button
,
Button
,
Checkbox
,
Collapse
,
Collapse
,
Flex
,
Flex
,
message
,
message
,
Space
,
Space
,
Tag
,
theme
,
theme
,
Typography
,
Typography
,
}
from
'antd'
;
}
from
'antd'
;
...
@@ -51,7 +52,6 @@ import {
...
@@ -51,7 +52,6 @@ import {
imageAnalyze
,
imageAnalyze
,
llmChat
,
llmChat
,
queryMcps
,
queryMcps
,
queryTopics
,
semanticSearch
,
semanticSearch
,
updateConversation
,
updateConversation
,
}
from
'../../services/DataService'
;
}
from
'../../services/DataService'
;
...
@@ -80,6 +80,14 @@ const AGENTS = {
...
@@ -80,6 +80,14 @@ const AGENTS = {
key
:
'2'
,
key
:
'2'
,
label
:
'智能体'
,
label
:
'智能体'
,
children
:
[
children
:
[
{
key
:
'2-5'
,
icon
:
<
FileDoneOutlined
/>,
label
:
'审计检查清单生成'
,
description
:
'根据当前审计项目、整改问题和责任单位上下文,生成可执行的审计检查清单。'
,
disabled
:
false
,
},
{
{
key
:
'2-4'
,
key
:
'2-4'
,
icon
:
<
PayCircleOutlined
/>,
icon
:
<
PayCircleOutlined
/>,
...
@@ -104,6 +112,159 @@ const AGENTS = {
...
@@ -104,6 +112,159 @@ const AGENTS = {
],
],
};
};
const
AUDIT_TOPIC
=
{
name
:
'mock_audit_rectification_query'
,
title
:
'审计整改情况查询'
,
metadata
:
{
prompt
:
'你是内网审计业务工作台助手,专注审计整改情况查询、问题跟踪、责任单位协同和检查清单生成。'
,
context
:
'回答时优先使用已选择的审计对象上下文,说明数据来源于当前内网业务对象;如果上下文不足,指出需要补充的对象或字段。'
,
enable_search
:
false
,
questions
:
[
'汇总当前项目的整改进度和高风险事项'
,
'分析该整改问题逾期的主要原因'
,
'生成责任单位现场复核需要关注的检查点'
,
],
},
};
const
AUDIT_OBJECT_GROUPS
=
[
{
key
:
'project'
,
title
:
'审计项目'
,
icon
:
<
AuditOutlined
/>,
records
:
[
{
id
:
'AP-2026-001'
,
title
:
'2026 年度财务收支审计'
,
status
:
'整改跟踪中'
,
fields
:
{
项目编号
:
'AP-2026-001'
,
审计期间
:
'2026-01 至 2026-05'
,
牵头部门
:
'审计部一组'
,
问题总数
:
'18'
,
已完成整改
:
'11'
,
高风险问题
:
'3'
,
},
},
{
id
:
'AP-2026-004'
,
title
:
'采购合规专项审计'
,
status
:
'现场复核中'
,
fields
:
{
项目编号
:
'AP-2026-004'
,
审计期间
:
'2026-03 至 2026-06'
,
牵头部门
:
'审计部二组'
,
问题总数
:
'12'
,
已完成整改
:
'7'
,
高风险问题
:
'2'
,
},
},
],
},
{
key
:
'issue'
,
title
:
'整改问题'
,
icon
:
<
FileSearchOutlined
/>,
records
:
[
{
id
:
'RI-2026-014'
,
title
:
'供应商准入资料不完整'
,
status
:
'逾期未完成'
,
fields
:
{
问题编号
:
'RI-2026-014'
,
风险等级
:
'高'
,
整改要求
:
'补齐供应商准入审批、资质复核和年度评价记录'
,
计划完成日
:
'2026-06-15'
,
当前进展
:
'已补充 60% 资料,缺少两家供应商复核记录'
,
逾期风险
:
'影响采购合规闭环和后续责任认定'
,
},
},
{
id
:
'RI-2026-021'
,
title
:
'费用报销附件审核不充分'
,
status
:
'整改中'
,
fields
:
{
问题编号
:
'RI-2026-021'
,
风险等级
:
'中'
,
整改要求
:
'完善附件校验规则,抽样复核历史报销单据'
,
计划完成日
:
'2026-06-30'
,
当前进展
:
'规则已上线,抽样复核完成 45%'
,
逾期风险
:
'可能导致重复报销或附件缺失问题持续发生'
,
},
},
],
},
{
key
:
'unit'
,
title
:
'责任单位'
,
icon
:
<
DatabaseOutlined
/>,
records
:
[
{
id
:
'OU-1203'
,
title
:
'采购管理部'
,
status
:
'重点跟踪'
,
fields
:
{
单位编码
:
'OU-1203'
,
负责人
:
'张敏'
,
整改联系人
:
'李卓'
,
未完成问题
:
'4'
,
近三月逾期次数
:
'2'
,
协同部门
:
'法务部、财务共享中心'
,
},
},
{
id
:
'OU-0816'
,
title
:
'财务共享中心'
,
status
:
'正常推进'
,
fields
:
{
单位编码
:
'OU-0816'
,
负责人
:
'王珂'
,
整改联系人
:
'陈宁'
,
未完成问题
:
'3'
,
近三月逾期次数
:
'0'
,
协同部门
:
'采购管理部、信息技术部'
,
},
},
],
},
];
const
DEFAULT_AUDIT_SELECTION
=
AUDIT_OBJECT_GROUPS
.
reduce
((
result
,
group
)
=>
{
result
[
group
.
key
]
=
group
.
records
[
0
]?.
id
;
return
result
;
},
{});
function
buildAuditContext
(
selection
)
{
return
AUDIT_OBJECT_GROUPS
.
map
((
group
)
=>
{
const
record
=
group
.
records
.
find
((
item
)
=>
item
.
id
===
selection
[
group
.
key
])
||
group
.
records
[
0
];
return
{
type
:
group
.
key
,
typeLabel
:
group
.
title
,
id
:
record
.
id
,
title
:
record
.
title
,
status
:
record
.
status
,
fields
:
record
.
fields
,
};
});
}
function
formatAuditContext
(
context
=
[])
{
return
context
.
map
((
item
)
=>
{
const
fields
=
Object
.
entries
(
item
.
fields
||
{})
.
map
(([
key
,
value
])
=>
`
${
key
}
:
${
value
}
`
)
.
join
(
'
\
n'
);
return
`##
${
item
.
typeLabel
}
:
${
item
.
title
}
\n状态:
${
item
.
status
}
\n
${
fields
}
`
;
})
.
join
(
'
\
n
\
n'
);
}
const
useStyle
=
createStyles
(({
token
,
css
,
responsive
})
=>
{
const
useStyle
=
createStyles
(({
token
,
css
,
responsive
})
=>
{
return
{
return
{
chat
:
css
`
chat
:
css
`
...
@@ -112,56 +273,171 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -112,56 +273,171 @@ const useStyle = createStyles(({ token, css, responsive }) => {
width: 100%;
width: 100%;
display: flex;
display: flex;
flex-direction: column;
flex-direction: column;
gap: 1
8
px;
gap: 1
2
px;
padding: 18px;
padding: 1
6px 22px 1
8px;
background: transparent;
background: transparent;
overflow: hidden;
${
responsive
.
xs
}
{
${
responsive
.
xs
}
{
padding: 12px;
padding: 12px;
gap: 1
4
px;
gap: 1
0
px;
}
}
`
,
`
,
chatPrompt
:
css
`
chatPrompt
:
css
`
.ant-prompts-item {
width: 100%;
border-radius: 22px !important;
min-width: 0;
border: 1px solid rgba(148, 163, 184, 0.16) !important;
overflow: hidden;
background: linear-gradient(
180deg,
.ant-prompts,
rgba(255, 255, 255, 0.98) 0%,
.ant-prompts-list,
rgba(248, 250, 252, 0.9) 100%
.ant-prompts-group,
) !important;
.ant-prompts-group-content {
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.06) !important;
width: 100%;
transition: transform 0.2s ease, box-shadow 0.2s ease;
min-width: 0;
}
& > .ant-prompts-list,
.ant-prompts-item-has-nest {
height: 100%;
}
}
.ant-prompts-item:hover {
.ant-prompts-item-has-nest {
transform: translateY(-2px);
display: flex !important;
box-shadow: 0 18px 32px rgba(15, 23, 42, 0.1) !important;
width: 100% !important;
min-width: 0 !important;
overflow: hidden;
flex-direction: column !important;
border-radius: 20px !important;
border: 1px solid rgba(200, 30, 58, 0.1) !important;
background: rgba(255, 255, 255, 0.92) !important;
box-shadow: 0 10px 26px rgba(159, 18, 57, 0.06) !important;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.ant-prompts-item-has-nest:hover {
border-color: rgba(200, 30, 58, 0.2) !important;
box-shadow: 0 16px 34px rgba(159, 18, 57, 0.1) !important;
}
}
.ant-prompts-label {
.ant-prompts-label {
color: #0f172a !important;
color: #0f172a !important;
font-weight: 600 !important;
font-weight: 600 !important;
min-width: 0;
max-width: 100%;
}
}
.ant-prompts-desc {
.ant-prompts-desc {
color: #64748b !important;
color: #64748b !important;
width: 100%;
width: 100%;
line-height: 1.6;
min-width: 0;
line-height: 1.45;
white-space: normal;
overflow-wrap: anywhere;
}
.ant-prompts-content {
min-width: 0 !important;
max-width: 100% !important;
overflow: hidden;
}
}
.ant-prompts-icon {
.ant-prompts-icon {
color:
${
token
.
colorPrimary
}
!important;
color:
${
token
.
colorPrimary
}
!important;
flex: 0 0 auto;
}
}
.ant-prompts-sub-item {
.ant-prompts-item-has-nest > .ant-prompts-content {
border-radius: 16px !important;
display: flex !important;
background: rgba(255, 255, 255, 0.72) !important;
flex: 1 1 auto;
border: 1px solid rgba(148, 163, 184, 0.12) !important;
min-height: 0;
overflow: visible;
}
.ant-prompts-item-has-nest > .ant-prompts-content > .ant-prompts-label {
padding: 24px 28px 14px !important;
color: #0f172a !important;
font-size: 20px !important;
font-weight: 700 !important;
line-height: 1.25 !important;
letter-spacing: 0 !important;
height: auto !important;
margin: 0 !important;
}
.ant-prompts-nested {
height: auto !important;
overflow: visible !important;
padding: 0 28px 26px !important;
}
.ant-prompts-nested .ant-prompts-list {
display: grid !important;
height: auto !important;
overflow: visible !important;
gap: 10px !important;
}
.ant-prompts-nested .ant-prompts-item {
width: 100% !important;
height: auto !important;
max-width: 100% !important;
min-width: 0 !important;
display: flex !important;
align-items: center !important;
gap: 12px !important;
min-height: 46px !important;
padding: 10px 12px !important;
border-radius: 14px !important;
background: #ffffff !important;
border: 1px solid rgba(200, 30, 58, 0.1) !important;
box-shadow: none !important;
transition: background-color 0.18s ease, border-color 0.18s ease,
box-shadow 0.18s ease;
&:hover {
background: #fff6f7 !important;
border-color: rgba(200, 30, 58, 0.22) !important;
box-shadow: 0 8px 18px rgba(159, 18, 57, 0.08) !important;
}
.ant-prompts-label,
.ant-prompts-desc {
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
}
.ant-prompts-label {
-webkit-line-clamp: 1;
}
.ant-prompts-desc {
-webkit-line-clamp: 3;
}
}
${
responsive
.
xs
}
{
.ant-prompts-item-has-nest > .ant-prompts-content > .ant-prompts-label {
padding: 18px 18px 12px !important;
font-size: 18px !important;
}
.ant-prompts-nested {
padding: 0 18px 18px !important;
}
.ant-prompts-nested .ant-prompts-list {
gap: 8px !important;
}
.ant-prompts-nested .ant-prompts-item {
align-items: flex-start !important;
min-height: 44px !important;
}
}
}
`
,
`
,
chatList
:
css
`
chatList
:
css
`
flex: 1;
flex: 1
1 auto
;
min-height: 0;
min-height: 0;
overflow: auto;
overflow: auto;
`
,
`
,
...
@@ -170,8 +446,9 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -170,8 +446,9 @@ const useStyle = createStyles(({ token, css, responsive }) => {
margin: 0 auto;
margin: 0 auto;
display: flex;
display: flex;
flex-direction: column;
flex-direction: column;
gap: 16px;
gap: 12px;
min-height: 100%;
min-height: 0;
padding-top: 0;
`
,
`
,
bubbleList
:
css
`
bubbleList
:
css
`
.ant-bubble-list {
.ant-bubble-list {
...
@@ -194,10 +471,10 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -194,10 +471,10 @@ const useStyle = createStyles(({ token, css, responsive }) => {
}
}
`
,
`
,
userBubble
:
css
`
userBubble
:
css
`
background: linear-gradient(135deg, #
0ea5e9 0%, #0284c7
100%);
background: linear-gradient(135deg, #
c81e3a 0%, #9f1239
100%);
color: #ffffff;
color: #ffffff;
border-radius: 22px 22px 10px 22px;
border-radius: 22px 22px 10px 22px;
box-shadow: 0 18px 36px rgba(1
4, 165, 233, 0.24
);
box-shadow: 0 18px 36px rgba(1
59, 18, 57, 0.22
);
padding: 14px 18px;
padding: 14px 18px;
`
,
`
,
assistantBubble
:
css
`
assistantBubble
:
css
`
...
@@ -219,39 +496,48 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -219,39 +496,48 @@ const useStyle = createStyles(({ token, css, responsive }) => {
padding: 14px 16px;
padding: 14px 16px;
`
,
`
,
loadingMessage
:
css
`
loadingMessage
:
css
`
background-image: linear-gradient(90deg, #
3f9c35 0%, #009cde
100%);
background-image: linear-gradient(90deg, #
c81e3a 0%, #fb7185
100%);
background-size: 100% 3px;
background-size: 100% 3px;
background-repeat: no-repeat;
background-repeat: no-repeat;
background-position: bottom;
background-position: bottom;
`
,
`
,
placeholder
:
css
`
placeholder
:
css
`
justify-content: center;
min-height: 100%;
padding-top: 10px;
justify-content: flex-start;
`
,
padding-top: 26px;
heroCard
:
css
`
padding-bottom: 2px;
padding: 32px;
border-radius: 30px;
background: radial-gradient(
circle at top right,
rgba(125, 211, 252, 0.36),
transparent 24%
),
linear-gradient(
180deg,
rgba(255, 255, 255, 0.98) 0%,
rgba(248, 250, 252, 0.92) 100%
);
border: 1px solid rgba(148, 163, 184, 0.16);
box-shadow: 0 22px 54px rgba(15, 23, 42, 0.08);
${
responsive
.
xs
}
{
${
responsive
.
xs
}
{
padding: 24px 18px;
padding-top: 12px;
}
`
,
auditContextCard
:
css
`
margin-top: 16px;
padding: 16px;
border-radius: 18px;
background: #0f172a;
color: #e2e8f0;
display: grid;
gap: 10px;
strong {
color: #ffffff;
font-size: 14px;
}
p {
margin: 0;
color: rgba(226, 232, 240, 0.74);
font-size: 13px;
line-height: 1.7;
}
}
`
,
`
,
promptRow
:
css
`
promptRow
:
css
`
display: grid;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
gap: 16px;
width: 100%;
min-width: 0;
${
responsive
.
md
}
{
${
responsive
.
md
}
{
grid-template-columns: 1fr;
grid-template-columns: 1fr;
...
@@ -263,11 +549,11 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -263,11 +549,11 @@ const useStyle = createStyles(({ token, css, responsive }) => {
margin: 0 auto;
margin: 0 auto;
.ant-sender {
.ant-sender {
border-radius: 2
4
px !important;
border-radius: 2
6
px !important;
border: 1px solid rgba(
148, 163, 184, 0.1
8) !important;
border: 1px solid rgba(
200, 30, 58, 0.2
8) !important;
background: rgba(255, 255, 255, 0.9) !important;
background: rgba(255, 255, 255, 0.9
6
) !important;
box-shadow: 0 18px
40
px rgba(15, 23, 42, 0.08) !important;
box-shadow: 0 18px
38
px rgba(15, 23, 42, 0.08) !important;
padding: 1
0
px !important;
padding: 1
2
px !important;
}
}
.ant-sender-textarea {
.ant-sender-textarea {
...
@@ -276,6 +562,7 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -276,6 +562,7 @@ const useStyle = createStyles(({ token, css, responsive }) => {
.ant-sender-input {
.ant-sender-input {
font-size: 15px;
font-size: 15px;
color: #0f172a !important;
}
}
.ant-sender-header {
.ant-sender-header {
...
@@ -291,11 +578,11 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -291,11 +578,11 @@ const useStyle = createStyles(({ token, css, responsive }) => {
senderShell
:
css
`
senderShell
:
css
`
width: min(100%, calc(var(--app-content-width) + 24px));
width: min(100%, calc(var(--app-content-width) + 24px));
margin: 0 auto;
margin: 0 auto;
padding:
8
px;
padding:
6
px;
border-radius:
30
px;
border-radius:
26
px;
background: rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.5
6
);
border: 1px solid rgba(255, 255, 255, 0.
68
);
border: 1px solid rgba(255, 255, 255, 0.
82
);
box-shadow: 0
20px 46
px rgba(15, 23, 42, 0.08);
box-shadow: 0
16px 34
px rgba(15, 23, 42, 0.08);
`
,
`
,
senderPrompt
:
css
`
senderPrompt
:
css
`
width: 100%;
width: 100%;
...
@@ -352,7 +639,7 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -352,7 +639,7 @@ const useStyle = createStyles(({ token, css, responsive }) => {
}
}
`
,
`
,
assistantActions
:
css
`
assistantActions
:
css
`
padding-top:
8
px;
padding-top:
10
px;
`
,
`
,
};
};
});
});
...
@@ -367,10 +654,20 @@ export default forwardRef((props, ref) => {
...
@@ -367,10 +654,20 @@ export default forwardRef((props, ref) => {
const
[
attachmentsOpen
,
setAttachmentsOpen
]
=
useState
(
false
);
const
[
attachmentsOpen
,
setAttachmentsOpen
]
=
useState
(
false
);
const
[
attachedFiles
,
setAttachedFiles
]
=
useState
([]);
const
[
attachedFiles
,
setAttachedFiles
]
=
useState
([]);
const
[
knowledgeBases
,
setKnowledgeBases
]
=
useState
([]);
const
[
inputValue
,
setInputValue
]
=
useState
(
''
);
const
[
inputValue
,
setInputValue
]
=
useState
(
''
);
const
senderRef
=
useRef
(
null
);
const
senderRef
=
useRef
(
null
);
const
abortController
=
useRef
(
null
);
const
abortController
=
useRef
(
null
);
const
auditContext
=
useMemo
(
()
=>
buildAuditContext
(
DEFAULT_AUDIT_SELECTION
),
[],
);
const
auditSettings
=
useMemo
(
()
=>
({
auditContext
,
auditContextText
:
formatAuditContext
(
auditContext
),
}),
[
auditContext
],
);
useImperativeHandle
(
ref
,
()
=>
{
useImperativeHandle
(
ref
,
()
=>
{
return
{
return
{
...
@@ -475,26 +772,21 @@ export default forwardRef((props, ref) => {
...
@@ -475,26 +772,21 @@ export default forwardRef((props, ref) => {
);
);
};
};
const
{
data
:
topics
=
[],
refetch
:
refetchConversations
}
=
useQuery
({
queryKey
:
[
'topics'
,
userInfo
],
queryFn
:
async
()
=>
{
if
(
!
userInfo
)
{
return
[];
}
try
{
const
res
=
await
queryTopics
();
return
res
.
records
;
}
catch
(
err
)
{}
},
});
const
getSystemPrompt
=
async
()
=>
{
const
getSystemPrompt
=
async
()
=>
{
const
topic
=
conversation
.
metadata
.
topic
;
const
topic
=
conversation
.
metadata
.
topic
;
const
auditContextText
=
formatAuditContext
(
conversation
.
metadata
.
auditContext
||
[],
);
const
auditPrompt
=
auditContextText
?
`\n\n当前审计对象上下文:\n
${
auditContextText
}
`
:
''
;
if
(
!
topic
)
{
if
(
!
topic
)
{
return
SYSTEM_PROMPT
;
return
`
${
SYSTEM_PROMPT
}${
auditPrompt
}
`
;
}
}
return
`
${
topic
.
metadata
.
prompt
||
''
}
\n
${
topic
.
metadata
.
context
||
''
}
`
;
return
`
${
topic
.
metadata
.
prompt
||
''
}
\n
${
topic
.
metadata
.
context
||
''
}${
auditPrompt
}
`
;
};
};
const
getMessagePrompt
=
async
(
message
)
=>
{
const
getMessagePrompt
=
async
(
message
)
=>
{
...
@@ -509,6 +801,12 @@ export default forwardRef((props, ref) => {
...
@@ -509,6 +801,12 @@ export default forwardRef((props, ref) => {
let
searchPrompt
=
''
;
let
searchPrompt
=
''
;
let
filePrompt
=
''
;
let
filePrompt
=
''
;
let
kbPrompt
=
''
;
let
kbPrompt
=
''
;
const
auditContextText
=
message
.
settings
?.
auditContextText
||
formatAuditContext
(
conversation
.
metadata
.
auditContext
||
[]);
const
auditPrompt
=
auditContextText
?
`当前审计对象上下文:\n
${
auditContextText
}
\n\n`
:
''
;
for
(
const
searchSubject
of
searchSubjects
)
{
for
(
const
searchSubject
of
searchSubjects
)
{
const
res
=
await
semanticSearch
(
searchSubject
.
subject
,
{
const
res
=
await
semanticSearch
(
searchSubject
.
subject
,
{
...
@@ -629,7 +927,7 @@ export default forwardRef((props, ref) => {
...
@@ -629,7 +927,7 @@ export default forwardRef((props, ref) => {
filePrompt
=
`用户上传的文件内容:\n
${
fileContent
}
\n\n`
;
filePrompt
=
`用户上传的文件内容:\n
${
fileContent
}
\n\n`
;
}
}
return
`
${
searchPrompt
}${
filePrompt
}${
kbPrompt
}
用户提问:
${
message
.
content
}
`
;
return
`
${
auditPrompt
}${
searchPrompt
}${
filePrompt
}${
kbPrompt
}
用户提问:
${
message
.
content
}
`
;
};
};
const
[
agent
]
=
useXAgent
({
const
[
agent
]
=
useXAgent
({
...
@@ -1091,39 +1389,6 @@ export default forwardRef((props, ref) => {
...
@@ -1091,39 +1389,6 @@ export default forwardRef((props, ref) => {
}
}
},
[
messages
]);
},
[
messages
]);
useEffect
(()
=>
{
setKnowledgeBases
([
{
key
:
'external_law'
,
description
:
'法律法规库'
,
icon
:
<
Checkbox
/>,
selected
:
false
,
},
{
key
:
'regulatory_penalty'
,
description
:
'监管处罚库'
,
icon
:
<
Checkbox
/>,
selected
:
false
,
},
{
key
:
'am_material'
,
description
:
'AM材料库'
,
icon
:
<
Checkbox
/>,
selected
:
false
,
},
]);
},
[]);
const
toggleKnowledgeBase
=
(
key
)
=>
{
const
knowledgeBase
=
knowledgeBases
.
find
((
kb
)
=>
kb
.
key
===
key
);
if
(
knowledgeBase
)
{
knowledgeBase
.
selected
=
!
knowledgeBase
.
selected
;
knowledgeBase
.
icon
=
<
Checkbox
checked=
{
knowledgeBase
.
selected
}
/>;
}
setKnowledgeBases
([...
knowledgeBases
]);
};
const
addConversation
=
async
(
data
,
message
)
=>
{
const
addConversation
=
async
(
data
,
message
)
=>
{
const
conversation
=
await
createConversation
(
data
);
const
conversation
=
await
createConversation
(
data
);
conversation
.
metadata
=
{
conversation
.
metadata
=
{
...
@@ -1140,6 +1405,10 @@ export default forwardRef((props, ref) => {
...
@@ -1140,6 +1405,10 @@ export default forwardRef((props, ref) => {
settings
=
null
,
settings
=
null
,
agentLabel
=
null
,
agentLabel
=
null
,
})
=>
{
})
=>
{
const
nextSettings
=
{
...
auditSettings
,
...
settings
,
};
const
userMessage
=
content
&&
{
const
userMessage
=
content
&&
{
role
:
'user'
,
role
:
'user'
,
content
,
content
,
...
@@ -1148,14 +1417,15 @@ export default forwardRef((props, ref) => {
...
@@ -1148,14 +1417,15 @@ export default forwardRef((props, ref) => {
attachedFiles
attachedFiles
.
filter
((
file
)
=>
file
.
status
===
'done'
)
.
filter
((
file
)
=>
file
.
status
===
'done'
)
.
map
((
file
)
=>
file
.
response
),
.
map
((
file
)
=>
file
.
response
),
settings
:
s
ettings
,
settings
:
nextS
ettings
,
};
};
if
(
!
conversation
)
{
if
(
!
conversation
)
{
await
addConversation
(
await
addConversation
(
{
{
label
:
agentLabel
||
content
||
topic
?.
title
,
label
:
agentLabel
||
content
||
topic
?.
title
||
AUDIT_TOPIC
.
title
,
topic
:
topic
,
topic
:
topic
,
auditContext
:
nextSettings
.
auditContext
,
},
},
userMessage
,
userMessage
,
);
);
...
@@ -1206,6 +1476,19 @@ export default forwardRef((props, ref) => {
...
@@ -1206,6 +1476,19 @@ export default forwardRef((props, ref) => {
}
}
}
}
/>
/>
)
}
)
}
{
conversation
.
metadata
.
auditContext
?.
length
>
0
&&
(
<
div
className=
{
styles
.
auditContextCard
}
>
<
strong
>
已读取审计对象上下文
</
strong
>
<
p
>
{
conversation
.
metadata
.
auditContext
.
map
(
(
item
)
=>
`${item.typeLabel}:${item.title}(${item.status})`
,
)
.
join
(
' / '
)
}
</
p
>
</
div
>
)
}
<
div
className=
{
styles
.
bubbleList
}
>
<
div
className=
{
styles
.
bubbleList
}
>
<
Bubble
.
List
<
Bubble
.
List
items=
{
messageItems
}
items=
{
messageItems
}
...
@@ -1241,45 +1524,27 @@ export default forwardRef((props, ref) => {
...
@@ -1241,45 +1524,27 @@ export default forwardRef((props, ref) => {
</
div
>
</
div
>
)
:
(
)
:
(
<
div
className=
{
`${styles.chatListInner} ${styles.placeholder}`
}
>
<
div
className=
{
`${styles.chatListInner} ${styles.placeholder}`
}
>
<
div
className=
{
styles
.
heroCard
}
>
<
Space
direction=
"vertical"
size=
{
18
}
style=
{
{
width
:
'100%'
}
}
>
<
Tag
color=
"cyan"
bordered=
{
false
}
style=
{
{
width
:
'fit-content'
}
}
>
企业 AI 对话应用
</
Tag
>
<
Welcome
variant=
"borderless"
icon=
"https://oss.streams7.com/rsm/BfNsqGPGTKE2.svg"
title=
"智能助手"
description=
"统一承载知识问答、业务理解、附件分析与专属智能体协作。"
/>
</
Space
>
</
div
>
<
div
className=
{
styles
.
promptRow
}
>
<
div
className=
{
styles
.
promptRow
}
>
<
Prompts
<
Prompts
items=
{
[
items=
{
[
{
{
key
:
'topics'
,
key
:
AUDIT_TOPIC
.
name
,
label
:
'话题'
,
label
:
'话题'
,
children
:
topics
.
map
((
topic
,
i
)
=>
({
children
:
[
key
:
topic
.
name
,
{
description
:
topic
.
title
,
key
:
AUDIT_TOPIC
.
name
,
icon
:
(
description
:
AUDIT_TOPIC
.
title
,
<
span
icon
:
<
FileSearchOutlined
/>,
style=
{
{
topic
:
AUDIT_TOPIC
,
fontWeight
:
'bold'
,
},
fontFamily
:
'monospace'
,
...
AUDIT_TOPIC
.
metadata
.
questions
.
map
((
question
)
=>
({
color
:
token
.
colorPrimary
,
key
:
question
,
}
}
description
:
question
,
>
icon
:
<
AuditOutlined
/>,
{
i
+
1
}
topic
:
AUDIT_TOPIC
,
</
span
>
content
:
question
,
),
topic
:
topic
,
})),
})),
],
},
},
]
}
]
}
style=
{
{
style=
{
{
...
@@ -1287,10 +1552,11 @@ export default forwardRef((props, ref) => {
...
@@ -1287,10 +1552,11 @@ export default forwardRef((props, ref) => {
}
}
}
}
styles=
{
{
styles=
{
{
list
:
{
height
:
'100%'
},
list
:
{
height
:
'100%'
},
subItem
:
{
padding
:
0
,
background
:
'
transparent
'
},
subItem
:
{
padding
:
0
,
background
:
'
#ffffff
'
},
}
}
}
}
onItemClick=
{
async
(
info
)
=>
{
onItemClick=
{
async
(
info
)
=>
{
onSubmit
({
onSubmit
({
content
:
info
.
data
.
content
,
topic
:
info
.
data
.
topic
,
topic
:
info
.
data
.
topic
,
});
});
}
}
}
}
...
@@ -1302,12 +1568,16 @@ export default forwardRef((props, ref) => {
...
@@ -1302,12 +1568,16 @@ export default forwardRef((props, ref) => {
flex
:
1
,
flex
:
1
,
}
}
}
}
styles=
{
{
styles=
{
{
subItem
:
{
background
:
'rgba(255,255,255,0.72)'
},
list
:
{
height
:
'100%'
},
subItem
:
{
background
:
'#ffffff'
},
}
}
}
}
onItemClick=
{
(
info
)
=>
{
onItemClick=
{
(
info
)
=>
{
// 所有智能体都正常发送描述信息,包括发票助手
onSubmit
({
onSubmit
({
content
:
info
.
data
.
description
,
content
:
info
.
data
.
description
,
topic
:
info
.
data
.
label
===
AGENTS
.
children
[
0
].
label
?
AUDIT_TOPIC
:
undefined
,
agentLabel
:
info
.
data
.
label
,
agentLabel
:
info
.
data
.
label
,
});
});
}
}
}
}
...
...
src/pages/Home/index.jsx
View file @
c49ab07e
...
@@ -6,6 +6,7 @@ import {
...
@@ -6,6 +6,7 @@ import {
updateConversation
,
updateConversation
,
}
from
'@/services/DataService'
;
}
from
'@/services/DataService'
;
import
{
import
{
AppstoreOutlined
,
BorderlessTableOutlined
,
BorderlessTableOutlined
,
CommentOutlined
,
CommentOutlined
,
DeleteOutlined
,
DeleteOutlined
,
...
@@ -28,20 +29,22 @@ import {
...
@@ -28,20 +29,22 @@ import {
Input
,
Input
,
Modal
,
Modal
,
Spin
,
Spin
,
T
ypography
,
T
ooltip
,
message
,
message
,
theme
,
theme
,
}
from
'antd'
;
}
from
'antd'
;
import
{
createStyles
}
from
'antd-style'
;
import
{
createStyles
}
from
'antd-style'
;
import
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
'react'
;
import
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
'react'
;
import
ApplicationSwitcherDialog
from
'../../components/ApplicationSwitcherDialog'
;
import
LoginModal
from
'../../components/LoginModal'
;
import
LoginModal
from
'../../components/LoginModal'
;
const
useStyle
=
createStyles
(({
token
,
css
,
responsive
})
=>
{
const
useStyle
=
createStyles
(({
token
,
css
,
responsive
})
=>
{
return
{
return
{
shell
:
css
`
shell
:
css
`
min-height: 100
vh;
height: 100d
vh;
padding: 16px;
padding: 16px;
display: flex;
display: flex;
overflow: hidden;
${
responsive
.
xs
}
{
${
responsive
.
xs
}
{
padding: 12px;
padding: 12px;
...
@@ -49,7 +52,7 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -49,7 +52,7 @@ const useStyle = createStyles(({ token, css, responsive }) => {
`
,
`
,
frame
:
css
`
frame
:
css
`
width: 100%;
width: 100%;
min-height: calc(100
vh - 32px);
height: calc(100d
vh - 32px);
display: flex;
display: flex;
gap: 16px;
gap: 16px;
border-radius: 36px;
border-radius: 36px;
...
@@ -57,36 +60,39 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -57,36 +60,39 @@ const useStyle = createStyles(({ token, css, responsive }) => {
overflow: hidden;
overflow: hidden;
${
responsive
.
xs
}
{
${
responsive
.
xs
}
{
min-height: calc(100
vh - 24px);
height: calc(100d
vh - 24px);
border-radius: 26px;
border-radius: 26px;
}
}
`
,
`
,
sidebar
:
css
`
sidebar
:
css
`
width: 3
04
px;
width: 3
16
px;
flex-shrink: 0;
flex-shrink: 0;
display: flex;
display: flex;
flex-direction: column;
flex-direction: column;
gap: 16px;
gap: 12px;
background: linear-gradient(
background: radial-gradient(
180deg,
circle at top left,
rgba(15, 23, 42, 0.98) 0%,
rgba(200, 30, 58, 0.12),
rgba(15, 23, 42, 0.92) 100%
transparent 26%
);
),
border: 1px solid rgba(255, 255, 255, 0.08);
linear-gradient(180deg, #0f172a 0%, #162033 58%, #1b2434 100%);
box-shadow: 0 26px 60px rgba(15, 23, 42, 0.24);
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow: 0 24px 52px rgba(15, 23, 42, 0.24);
color: rgba(255, 255, 255, 0.95);
color: rgba(255, 255, 255, 0.95);
padding:
22px 18px 18
px;
padding:
18px 16px 14
px;
border-radius: 3
2
px;
border-radius: 3
4
px;
`
,
`
,
sidebarCollapsed
:
css
`
sidebarCollapsed
:
css
`
width: 92px;
width: 108px;
padding-inline: 14px;
padding-inline: 12px;
align-items: center;
`
,
`
,
sidebarHeader
:
css
`
sidebarHeader
:
css
`
display: flex;
display: flex;
align-items: center;
align-items: center;
justify-content: space-between;
justify-content: space-between;
gap: 12px;
gap: 10px;
min-height: 52px;
`
,
`
,
brand
:
css
`
brand
:
css
`
display: flex;
display: flex;
...
@@ -95,188 +101,269 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -95,188 +101,269 @@ const useStyle = createStyles(({ token, css, responsive }) => {
min-width: 0;
min-width: 0;
`
,
`
,
brandMark
:
css
`
brandMark
:
css
`
width: 4
0
px;
width: 4
4
px;
height: 4
0
px;
height: 4
4
px;
border-radius: 1
4
px;
border-radius: 1
6
px;
background: linear-gradient(135deg, #
00a6e8 0%, #7dd3fc
100%);
background: linear-gradient(135deg, #
c81e3a 0%, #9f1239
100%);
display: inline-flex;
display: inline-flex;
align-items: center;
align-items: center;
justify-content: center;
justify-content: center;
color: #ffffff;
color: #ffffff;
font-size: 1
8
px;
font-size: 1
9
px;
font-weight: 700;
font-weight: 700;
box-shadow: 0 1
2px 24px rgba(0, 166, 232, 0.28
);
box-shadow: 0 1
4px 28px rgba(159, 18, 57, 0.24
);
`
,
`
,
brandText
:
css
`
brandText
:
css
`
min-width: 0;
min-width: 0;
h1 {
h1 {
margin: 0;
margin: 0;
font-size: 1
7
px;
font-size: 1
8
px;
line-height: 1.2;
line-height: 1.2;
color: #f8fafc;
color: #f8fbff;
letter-spacing: -0.01em;
}
}
p {
p {
margin:
4
px 0 0;
margin:
6
px 0 0;
color: rgba(226, 232, 240, 0.
72
);
color: rgba(226, 232, 240, 0.
86
);
font-size: 12px;
font-size: 12px;
line-height: 1.55;
max-width: 180px;
}
}
`
,
`
,
collapseButton
:
css
`
collapseButton
:
css
`
color: rgba(255, 255, 255, 0.78);
width: 40px;
height: 40px;
color: rgba(255, 255, 255, 0.94);
border-radius: 14px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.06);
&:hover {
&:hover {
color: #ffffff !important;
color: #ffffff !important;
background: rgba(255, 255, 255, 0.
08
) !important;
background: rgba(255, 255, 255, 0.
1
) !important;
}
}
`
,
`
,
newButton
:
css
`
newButton
:
css
`
width: 100%;
width: 100%;
height: 4
6
px;
height: 4
8
px;
border: none;
border: none;
background: linear-gradient(135deg, #00a6e8 0%, #0ea5e9 100%);
border-radius: 18px;
box-shadow: 0 14px 24px rgba(0, 166, 232, 0.24);
background: linear-gradient(135deg, #c81e3a 0%, #9f1239 100%);
box-shadow: 0 16px 28px rgba(159, 18, 57, 0.24);
color: #ffffff;
color: #ffffff;
font-weight: 600;
font-weight: 600;
font-size: 16px;
&:hover {
&:hover {
color: #ffffff !important;
color: #ffffff !important;
background: linear-gradient(
background: linear-gradient(
135deg,
135deg,
#
13b5f5
0%,
#
d92a47
0%,
#
38bdf8
100%
#
be123c
100%
) !important;
) !important;
}
}
`
,
`
,
sectionLabel
:
css
`
font-size: 12px;
line-height: 1;
text-transform: uppercase;
letter-spacing: 0.08em;
color: rgba(226, 232, 240, 0.78);
font-weight: 700;
margin: 4px 6px 0;
`
,
conversations
:
css
`
conversations
:
css
`
flex: 1;
flex: 1;
overflow-y: auto;
overflow-y: auto;
padding-right: 2px;
padding-right: 2px;
position: relative;
position: relative;
&::before {
content: '';
position: sticky;
top: 0;
display: block;
height: 6px;
background: linear-gradient(
180deg,
rgba(15, 23, 42, 0.95) 0%,
rgba(15, 23, 42, 0) 100%
);
z-index: 1;
pointer-events: none;
}
.ant-conversations-list {
.ant-conversations-list {
margin: 0;
padding-top: 0;
padding-inline-start: 0;
padding-inline-start: 0;
}
}
.ant-conversations-list .ant-conversations-item {
padding-inline-start: 12px;
}
.ant-conversations-group {
.ant-conversations-group {
margin-top:
10px
;
margin-top:
0
;
}
}
.ant-conversations-group-title {
.ant-conversations-group-title {
color: rgba(226, 232, 240, 0.72);
color: rgba(248, 250, 252, 0.96) !important;
font-size: 12px;
font-size: 14px;
padding: 0 6px 10px;
line-height: 1.2;
padding: 2px 8px 8px;
font-weight: 700;
font-weight: 700;
letter-spacing: 0.06em;
letter-spacing: 0;
text-shadow: 0 1px 2px rgba(15, 23, 42, 0.4);
}
.ant-conversations-group-title * {
color: inherit !important;
}
}
.ant-conversations-item {
.ant-conversations-item {
margin-bottom: 8px;
margin-bottom: 8px;
border-radius: 18px;
border-radius: 18px;
color: #e5eefb;
color: #f8fafc !important;
background: rgba(255, 255, 255, 0.04);
background: linear-gradient(
border: 1px solid rgba(148, 163, 184, 0.08);
180deg,
rgba(255, 255, 255, 0.13) 0%,
rgba(255, 255, 255, 0.08) 100%
);
border: 1px solid rgba(255, 255, 255, 0.12);
transition: all 0.2s ease, transform 0.2s ease;
transition: all 0.2s ease, transform 0.2s ease;
backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
min-height: 72px;
height: 60px;
min-height: 60px;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}
}
.ant-conversations-item:hover {
.ant-conversations-item:hover {
background: rgba(255, 255, 255, 0.11);
background: linear-gradient(
border-color: rgba(125, 211, 252, 0.22);
180deg,
rgba(255, 255, 255, 0.18) 0%,
rgba(255, 255, 255, 0.12) 100%
);
border-color: rgba(251, 113, 133, 0.32);
transform: translateY(-1px);
transform: translateY(-1px);
}
}
.ant-conversations-item-active {
.ant-conversations-item-active {
background: linear-gradient(
background: linear-gradient(
135deg,
135deg,
rgba(
14, 165, 233, 0.3
) 0%,
rgba(
200, 30, 58, 0.36
) 0%,
rgba(
59, 130, 246, 0.22
) 100%
rgba(
159, 18, 57, 0.26
) 100%
);
);
border-color: rgba(
125, 211, 252, 0.3
8);
border-color: rgba(
251, 113, 133, 0.4
8);
box-shadow: inset 0 0 0 1px rgba(
125, 211, 252, 0.2
),
box-shadow: inset 0 0 0 1px rgba(
251, 113, 133, 0.24
),
0 1
2px 26px rgba(2, 132, 199, 0.18
);
0 1
4px 26px rgba(15, 23, 42, 0.22
);
}
}
.ant-conversations-item-label,
.ant-conversations-label,
.ant-conversations-item-icon {
.ant-conversations-icon,
.ant-conversations-menu-icon {
color: inherit !important;
color: inherit !important;
}
}
.ant-conversations-item-label {
.ant-conversations-label {
font-size: 15px;
color: #f8fafc !important;
display: block;
font-size: 16px;
font-weight: 600;
font-weight: 600;
line-height: 1.
4
5;
line-height: 1.
3
5;
color: #edf4ff !important
;
min-width: 0
;
text-shadow: 0 1px
0 rgba(15, 23, 42, 0.3
5);
text-shadow: 0 1px
2px rgba(15, 23, 42, 0.4
5);
}
}
.ant-conversations-
item-desc
{
.ant-conversations-
label *
{
color:
rgba(203, 213, 225, 0.68)
!important;
color:
inherit
!important;
}
}
.ant-conversations-item-icon {
.ant-conversations-icon {
font-size: 16px;
flex: 0 0 22px;
opacity: 0.94;
width: 22px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 17px;
opacity: 1;
color: rgba(255, 255, 255, 0.96) !important;
}
}
.ant-conversations-item-active .ant-conversations-
item-
label {
.ant-conversations-item-active .ant-conversations-label {
color: #ffffff !important;
color: #ffffff !important;
}
}
.ant-conversations-item-active .ant-conversations-item-icon {
.ant-conversations-item:hover .ant-conversations-label,
color: #e0f2fe !important;
.ant-conversations-item:hover .ant-conversations-label *,
.ant-conversations-item-active .ant-conversations-label,
.ant-conversations-item-active .ant-conversations-label * {
color: #ffffff !important;
}
}
.ant-conversations-item-
icon {
.ant-conversations-item-
active .ant-conversations-icon,
width: 18px;
.ant-conversations-item-active .ant-conversations-menu-icon {
height: 18px
;
color: #ffffff !important
;
}
}
.ant-conversations-item-content {
.ant-conversations-menu-icon {
padding-block: 2px;
color: rgba(255, 255, 255, 0.86) !important;
opacity: 0.74;
padding: 6px;
border-radius: 10px;
transition: color 0.2s ease, background 0.2s ease, opacity 0.2s ease;
}
}
.ant-conversations-item
-menu
{
.ant-conversations-item
:hover .ant-conversations-menu-icon
{
color: rgba(226, 232, 240, 0.72) !important
;
opacity: 1
;
}
}
.ant-conversations-
item-menu
:hover {
.ant-conversations-
menu-icon
:hover {
color: #ffffff !important;
color: #ffffff !important;
background: rgba(255, 255, 255, 0.12);
}
}
`
,
`
,
emptyHistory
:
css
`
emptyHistory
:
css
`
padding-top:
28
px;
padding-top:
12
px;
.ant-empty-description {
.ant-empty-description {
color: rgba(203, 213, 225, 0.62);
color: rgba(226, 232, 240, 0.72);
}
`
,
compactNav
:
css
`
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
padding-top: 8px;
overflow-y: auto;
`
,
compactNavItem
:
css
`
width: 100%;
display: flex;
justify-content: center;
`
,
compactNavButton
:
css
`
position: relative;
width: 58px;
height: 58px;
border-radius: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.06);
color: rgba(241, 245, 249, 0.92);
cursor: pointer;
transition: transform 0.2s ease, background 0.2s ease,
border-color 0.2s ease;
&:hover {
transform: translateY(-1px);
background: rgba(255, 255, 255, 0.12);
border-color: rgba(251, 113, 133, 0.24);
}
}
`
,
`
,
compactNavButtonActive
:
css
`
background: linear-gradient(
135deg,
rgba(159, 18, 57, 0.52) 0%,
rgba(225, 29, 72, 0.26) 100%
);
border-color: rgba(251, 113, 133, 0.3);
box-shadow: 0 12px 24px rgba(127, 29, 29, 0.16);
`
,
compactNavPin
:
css
`
position: absolute;
top: 8px;
right: 8px;
width: 8px;
height: 8px;
border-radius: 999px;
background: #fb7185;
box-shadow: 0 0 0 4px rgba(251, 113, 133, 0.14);
`
,
sidebarFooter
:
css
`
sidebarFooter
:
css
`
padding-top: 12px;
padding-top: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.08);
border-top: 1px solid rgba(255, 255, 255, 0.08);
...
@@ -285,17 +372,34 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -285,17 +372,34 @@ const useStyle = createStyles(({ token, css, responsive }) => {
display: flex;
display: flex;
align-items: center;
align-items: center;
gap: 12px;
gap: 12px;
padding: 10px 12px;
padding: 12px;
border-radius: 18px;
border-radius: 20px;
background: rgba(255, 255, 255, 0.06);
background: rgba(255, 255, 255, 0.07);
border: 1px solid rgba(255, 255, 255, 0.06);
cursor: pointer;
cursor: pointer;
transition: background 0.2s ease;
transition: background 0.2s ease
, border-color 0.2s ease
;
&:hover {
&:hover {
background: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.12);
border-color: rgba(251, 113, 133, 0.24);
}
}
`
,
`
,
accountCardCollapsed
:
css
`
flex-direction: column;
padding: 10px 8px;
gap: 8px;
`
,
accountIdentity
:
css
`
flex: 1;
min-width: 0;
display: flex;
align-items: center;
gap: 12px;
color: inherit;
cursor: pointer;
`
,
accountMeta
:
css
`
accountMeta
:
css
`
flex: 1;
min-width: 0;
min-width: 0;
strong {
strong {
...
@@ -307,59 +411,67 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -307,59 +411,67 @@ const useStyle = createStyles(({ token, css, responsive }) => {
span {
span {
display: block;
display: block;
color: rgba(2
03, 213, 225, 0.7
);
color: rgba(2
26, 232, 240, 0.76
);
font-size: 12px;
font-size: 12px;
line-height: 1.3;
line-height: 1.3;
margin-top: 2px;
margin-top: 4px;
}
`
,
applicationSwitcherButton
:
css
`
width: 44px;
height: 44px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 16px;
border: 1px solid rgba(251, 113, 133, 0.34);
background: linear-gradient(
180deg,
rgba(255, 255, 255, 0.1),
rgba(255, 255, 255, 0.06)
);
color: #fb7185;
cursor: pointer;
box-shadow: 0 12px 22px rgba(159, 18, 57, 0.14);
transition: transform 0.2s ease, border-color 0.2s ease,
background 0.2s ease, color 0.2s ease;
&:hover {
transform: translateY(-1px);
border-color: rgba(251, 113, 133, 0.72);
background: rgba(251, 113, 133, 0.14);
color: #fecdd3;
}
&:focus-visible {
outline: 2px solid rgba(251, 113, 133, 0.58);
outline-offset: 2px;
}
}
`
,
`
,
contentShell
:
css
`
contentShell
:
css
`
flex: 1;
flex: 1;
min-width: 0;
min-width: 0;
min-height: 0;
display: flex;
display: flex;
flex-direction: column;
flex-direction: column;
background: rgba(255, 255, 255, 0.58);
position: relative;
background: rgba(255, 255, 255, 0.64);
border: 1px solid rgba(255, 255, 255, 0.72);
border: 1px solid rgba(255, 255, 255, 0.72);
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.1);
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.1);
backdrop-filter: blur(18px);
backdrop-filter: blur(18px);
border-radius: 32px;
border-radius: 32px;
overflow: hidden;
overflow: hidden;
`
,
`
,
toolbar
:
css
`
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 20px 24px 12px;
flex-wrap: wrap;
`
,
toolbarTitle
:
css
`
min-width: 0;
h2 {
margin: 0;
font-size: 24px;
line-height: 1.25;
color: #0f172a;
}
p {
margin: 6px 0 0;
font-size: 13px;
line-height: 1.5;
color: #64748b;
}
`
,
toolbarActions
:
css
`
display: flex;
align-items: center;
gap: 10px;
`
,
mobileMenuButton
:
css
`
mobileMenuButton
:
css
`
display: none;
display: none;
${
responsive
.
lg
}
{
${
responsive
.
lg
}
{
display: inline-flex;
display: inline-flex;
position: absolute;
top: 14px;
right: 14px;
z-index: 4;
}
}
`
,
`
,
mobileOnly
:
css
`
mobileOnly
:
css
`
...
@@ -379,7 +491,7 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -379,7 +491,7 @@ const useStyle = createStyles(({ token, css, responsive }) => {
body
:
css
`
body
:
css
`
flex: 1;
flex: 1;
min-height: 0;
min-height: 0;
padding: 0 20px
20
px;
padding: 0 20px
16
px;
${
responsive
.
xs
}
{
${
responsive
.
xs
}
{
padding: 0 12px 12px;
padding: 0 12px 12px;
...
@@ -388,12 +500,17 @@ const useStyle = createStyles(({ token, css, responsive }) => {
...
@@ -388,12 +500,17 @@ const useStyle = createStyles(({ token, css, responsive }) => {
chatStage
:
css
`
chatStage
:
css
`
height: 100%;
height: 100%;
border-radius: 28px;
border-radius: 28px;
background: linear-gradient(
background: radial-gradient(
circle at top center,
rgba(251, 207, 232, 0.28),
transparent 22%
),
linear-gradient(
180deg,
180deg,
rgba(255, 255, 255, 0.8
2) 0%,
rgba(255, 255, 255, 0.9
2) 0%,
rgba(248, 250, 252, 0.94
) 100%
rgba(255, 247, 247, 0.98
) 100%
);
);
border: 1px solid rgba(148, 163, 184, 0.1
8
);
border: 1px solid rgba(148, 163, 184, 0.1
6
);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
overflow: hidden;
overflow: hidden;
`
,
`
,
...
@@ -424,6 +541,15 @@ function buildConversationItems(conversations = []) {
...
@@ -424,6 +541,15 @@ function buildConversationItems(conversations = []) {
}));
}));
}
}
function
buildCompactConversationItems
(
conversations
=
[])
{
return
conversations
.
slice
(
0
,
6
).
map
((
c
)
=>
({
key
:
c
.
name
,
label
:
c
.
metadata
.
label
||
'新会话'
,
icon
:
c
.
metadata
.
topic
?
<
BorderlessTableOutlined
/>
:
<
CommentOutlined
/>,
pinned
:
c
.
metadata
.
pinned
,
}));
}
export
default
()
=>
{
export
default
()
=>
{
const
{
token
}
=
theme
.
useToken
();
const
{
token
}
=
theme
.
useToken
();
const
[
collapsed
,
setCollapsed
]
=
useState
(
false
);
const
[
collapsed
,
setCollapsed
]
=
useState
(
false
);
...
@@ -440,6 +566,7 @@ export default () => {
...
@@ -440,6 +566,7 @@ export default () => {
const
[
renamingConversationLabel
,
setRenamingConversationLabel
]
=
const
[
renamingConversationLabel
,
setRenamingConversationLabel
]
=
useState
(
null
);
useState
(
null
);
const
[
renamingModalOpen
,
setRenamingModalOpen
]
=
useState
(
false
);
const
[
renamingModalOpen
,
setRenamingModalOpen
]
=
useState
(
false
);
const
[
applicationSwitcherOpen
,
setApplicationSwitcherOpen
]
=
useState
(
false
);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
chatRef
=
useRef
(
null
);
const
chatRef
=
useRef
(
null
);
...
@@ -494,30 +621,14 @@ export default () => {
...
@@ -494,30 +621,14 @@ export default () => {
};
};
},
[]);
},
[]);
const
currentTitle
=
useMemo
(()
=>
{
if
(
!
currentConversation
)
{
return
'智能工作台'
;
}
return
currentConversation
.
metadata
?.
label
||
'当前会话'
;
},
[
currentConversation
]);
const
currentSubtitle
=
useMemo
(()
=>
{
if
(
!
currentConversation
)
{
return
'统一承载知识问答、智能体协作、附件分析与业务流程型对话。'
;
}
if
(
currentConversation
.
metadata
?.
topic
?.
title
)
{
return
`当前话题:
${
currentConversation
.
metadata
.
topic
.
title
}
`
;
}
return
'保持连续上下文,支持流式回复、知识检索与工具调用。'
;
},
[
currentConversation
]);
const
conversationItems
=
useMemo
(
const
conversationItems
=
useMemo
(
()
=>
buildConversationItems
(
conversations
),
()
=>
buildConversationItems
(
conversations
),
[
conversations
],
[
conversations
],
);
);
const
compactConversationItems
=
useMemo
(
()
=>
buildCompactConversationItems
(
conversations
),
[
conversations
],
);
const
openConversation
=
async
(
name
)
=>
{
const
openConversation
=
async
(
name
)
=>
{
const
conversation
=
await
getRecord
(
name
);
const
conversation
=
await
getRecord
(
name
);
...
@@ -583,15 +694,15 @@ export default () => {
...
@@ -583,15 +694,15 @@ export default () => {
const
sidebarContent
=
(
const
sidebarContent
=
(
<
div
className=
{
cx
(
styles
.
sidebar
,
collapsed
&&
styles
.
sidebarCollapsed
)
}
>
<
div
className=
{
cx
(
styles
.
sidebar
,
collapsed
&&
styles
.
sidebarCollapsed
)
}
>
<
div
className=
{
styles
.
sidebarHeader
}
>
<
div
className=
{
styles
.
sidebarHeader
}
>
{
!
collapsed
&&
(
<
div
className=
{
styles
.
brand
}
>
<
div
className=
{
styles
.
brand
}
>
<
div
className=
{
styles
.
brandMark
}
>
AI
</
div
>
<
div
className=
{
styles
.
brandMark
}
>
AI
</
div
>
{
!
collapsed
&&
(
<
div
className=
{
styles
.
brandText
}
>
<
div
className=
{
styles
.
brandText
}
>
<
h1
>
智能助手
</
h1
>
<
h1
>
智能助手
</
h1
>
<
p
>
面向业务场景的统一 AI 对话工作台
</
p
>
<
p
>
面向业务场景的统一 AI 对话工作台
</
p
>
</
div
>
</
div
>
)
}
</
div
>
</
div
>
)
}
{
!
isMobile
&&
(
{
!
isMobile
&&
(
<
Button
<
Button
type=
"text"
type=
"text"
...
@@ -613,8 +724,8 @@ export default () => {
...
@@ -613,8 +724,8 @@ export default () => {
{
!
collapsed
?
'新建会话'
:
null
}
{
!
collapsed
?
'新建会话'
:
null
}
</
Button
>
</
Button
>
{
!
collapsed
&&
<
div
className=
{
styles
.
sectionLabel
}
>
Conversations
</
div
>
}
{
!
collapsed
?
(
<>
{
conversations
?.
length
>
0
?
(
{
conversations
?.
length
>
0
?
(
<
Conversations
<
Conversations
items=
{
conversationItems
}
items=
{
conversationItems
}
...
@@ -624,7 +735,7 @@ export default () => {
...
@@ -624,7 +735,7 @@ export default () => {
groupable
groupable
styles=
{
{
styles=
{
{
item
:
{
item
:
{
padding
:
collapsed
?
'0 4px'
:
'0 8px'
,
padding
:
'0 8px'
,
},
},
}
}
}
}
menu=
{
conversationMenu
}
menu=
{
conversationMenu
}
...
@@ -632,15 +743,63 @@ export default () => {
...
@@ -632,15 +743,63 @@ export default () => {
)
:
(
)
:
(
<
div
className=
{
styles
.
conversations
}
>
<
div
className=
{
styles
.
conversations
}
>
<
Empty
<
Empty
description=
{
collapsed
?
''
:
'还没有历史会话'
}
description=
"还没有历史会话"
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
className=
{
styles
.
emptyHistory
}
className=
{
styles
.
emptyHistory
}
/>
/>
</
div
>
</
div
>
)
}
)
}
</>
)
:
(
<
div
className=
{
styles
.
compactNav
}
>
{
compactConversationItems
.
length
>
0
?
(
compactConversationItems
.
map
((
item
)
=>
(
<
Tooltip
key=
{
item
.
key
}
title=
{
item
.
label
}
placement=
"right"
>
<
div
className=
{
styles
.
compactNavItem
}
>
<
div
className=
{
cx
(
styles
.
compactNavButton
,
currentConversation
?.
name
===
item
.
key
&&
styles
.
compactNavButtonActive
,
)
}
onClick=
{
()
=>
openConversation
(
item
.
key
)
}
role=
"button"
tabIndex=
{
0
}
onKeyDown=
{
(
event
)
=>
{
if
(
event
.
key
===
'Enter'
||
event
.
key
===
' '
)
{
event
.
preventDefault
();
openConversation
(
item
.
key
);
}
}
}
>
{
item
.
icon
}
{
item
.
pinned
?
(
<
span
className=
{
styles
.
compactNavPin
}
/>
)
:
null
}
</
div
>
</
div
>
</
Tooltip
>
))
)
:
(
<
Tooltip
title=
"还没有历史会话"
placement=
"right"
>
<
div
className=
{
styles
.
compactNavItem
}
>
<
div
className=
{
styles
.
compactNavButton
}
>
<
CommentOutlined
/>
</
div
>
</
div
>
</
Tooltip
>
)
}
</
div
>
)
}
<
div
className=
{
styles
.
sidebarFooter
}
>
<
div
className=
{
styles
.
sidebarFooter
}
>
{
userInfo
&&
(
{
userInfo
&&
(
<
div
className=
{
cx
(
styles
.
accountCard
,
collapsed
&&
styles
.
accountCardCollapsed
,
)
}
>
<
Dropdown
<
Dropdown
trigger=
{
[
'click'
]
}
trigger=
{
[
'click'
]
}
menu=
{
{
menu=
{
{
...
@@ -657,7 +816,7 @@ export default () => {
...
@@ -657,7 +816,7 @@ export default () => {
],
],
}
}
}
}
>
>
<
div
className=
{
styles
.
accountCard
}
>
<
div
className=
{
styles
.
accountIdentity
}
>
<
Avatar
<
Avatar
size=
{
collapsed
?
40
:
42
}
size=
{
collapsed
?
40
:
42
}
src=
{
userInfo
?.
avatar
}
src=
{
userInfo
?.
avatar
}
...
@@ -677,6 +836,18 @@ export default () => {
...
@@ -677,6 +836,18 @@ export default () => {
)
}
)
}
</
div
>
</
div
>
</
Dropdown
>
</
Dropdown
>
<
Tooltip
title=
"应用切换"
placement=
{
collapsed
?
'right'
:
'top'
}
>
<
button
type=
"button"
aria
-
label=
"应用切换"
className=
{
styles
.
applicationSwitcherButton
}
onClick=
{
()
=>
setApplicationSwitcherOpen
(
true
)
}
>
<
AppstoreOutlined
/>
</
button
>
</
Tooltip
>
</
div
>
)
}
)
}
</
div
>
</
div
>
</
div
>
</
div
>
...
@@ -700,12 +871,6 @@ export default () => {
...
@@ -700,12 +871,6 @@ export default () => {
</
Drawer
>
</
Drawer
>
<
div
className=
{
styles
.
contentShell
}
>
<
div
className=
{
styles
.
contentShell
}
>
<
div
className=
{
styles
.
toolbar
}
>
<
div
className=
{
styles
.
toolbarTitle
}
>
<
Typography
.
Title
level=
{
2
}
>
{
currentTitle
}
</
Typography
.
Title
>
<
Typography
.
Paragraph
>
{
currentSubtitle
}
</
Typography
.
Paragraph
>
</
div
>
<
div
className=
{
styles
.
toolbarActions
}
>
<
Button
<
Button
type=
"default"
type=
"default"
icon=
{
<
MenuUnfoldOutlined
/>
}
icon=
{
<
MenuUnfoldOutlined
/>
}
...
@@ -714,8 +879,6 @@ export default () => {
...
@@ -714,8 +879,6 @@ export default () => {
>
>
菜单
菜单
</
Button
>
</
Button
>
</
div
>
</
div
>
<
div
className=
{
styles
.
body
}
>
<
div
className=
{
styles
.
body
}
>
<
div
className=
{
styles
.
chatStage
}
>
<
div
className=
{
styles
.
chatStage
}
>
...
@@ -775,6 +938,12 @@ export default () => {
...
@@ -775,6 +938,12 @@ export default () => {
/>
/>
</
Modal
>
</
Modal
>
<
ApplicationSwitcherDialog
open=
{
applicationSwitcherOpen
}
onClose=
{
()
=>
setApplicationSwitcherOpen
(
false
)
}
userInfo=
{
userInfo
}
/>
<
LoginModal
<
LoginModal
open=
{
!
userInfo
}
open=
{
!
userInfo
}
onSuccess=
{
(
currentUser
)
=>
{
onSuccess=
{
(
currentUser
)
=>
{
...
...
src/services/DataService.js
View file @
c49ab07e
...
@@ -12,6 +12,18 @@ export function getApplication(name) {
...
@@ -12,6 +12,18 @@ export function getApplication(name) {
});
});
}
}
export
function
getSystemInfo
()
{
return
request
(
'/api/info'
,
{
method
:
'get'
,
});
}
export
function
getApplications
()
{
return
request
(
'/api/application/info'
,
{
method
:
'get'
,
});
}
export
function
getRecord
(
name
)
{
export
function
getRecord
(
name
)
{
return
request
(
`/api/record/
${
name
}
`
,
{
return
request
(
`/api/record/
${
name
}
`
,
{
method
:
'get'
,
method
:
'get'
,
...
@@ -50,13 +62,21 @@ export function getUserInfo() {
...
@@ -50,13 +62,21 @@ export function getUserInfo() {
return
currentUser
&&
JSON
.
parse
(
currentUser
);
return
currentUser
&&
JSON
.
parse
(
currentUser
);
}
}
export
async
function
login
(
username
,
password
)
{
export
function
getCaptcha
()
{
return
request
(
'/api/auth/captcha'
,
{
method
:
'get'
,
});
}
export
async
function
login
(
username
,
password
,
captcha
,
token
)
{
await
request
(
'/api/auth/login'
,
{
await
request
(
'/api/auth/login'
,
{
method
:
'post'
,
method
:
'post'
,
data
:
{
data
:
{
type
:
'password'
,
type
:
'password'
,
identity
:
username
,
identity
:
username
,
password
:
password
,
password
:
password
,
captcha
,
token
,
},
},
});
});
...
...
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