Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
Internet-hospital
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
yuguo
Internet-hospital
Commits
e26e5d42
Commit
e26e5d42
authored
Mar 06, 2026
by
yuguo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix
parent
45960530
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
453 additions
and
186 deletions
+453
-186
server/internal/service/user/service.go
server/internal/service/user/service.go
+11
-1
web/src/app/(auth)/login/page.tsx
web/src/app/(auth)/login/page.tsx
+142
-37
web/src/app/(main)/doctor/consult/AIPanel.tsx
web/src/app/(main)/doctor/consult/AIPanel.tsx
+24
-5
web/src/app/(main)/doctor/consult/ChatPanel.tsx
web/src/app/(main)/doctor/consult/ChatPanel.tsx
+13
-8
web/src/app/(main)/doctor/consult/PatientList.tsx
web/src/app/(main)/doctor/consult/PatientList.tsx
+131
-71
web/src/app/(main)/doctor/consult/page.tsx
web/src/app/(main)/doctor/consult/page.tsx
+65
-47
web/src/app/(main)/doctor/schedule/page.tsx
web/src/app/(main)/doctor/schedule/page.tsx
+7
-8
web/src/app/(main)/patient/chronic/page.tsx
web/src/app/(main)/patient/chronic/page.tsx
+60
-9
No files found.
server/internal/service/user/service.go
View file @
e26e5d42
...
@@ -122,7 +122,7 @@ func (s *Service) Register(ctx context.Context, req *RegisterRequest) (*LoginRes
...
@@ -122,7 +122,7 @@ func (s *Service) Register(ctx context.Context, req *RegisterRequest) (*LoginRes
return
nil
,
err
return
nil
,
err
}
}
//
如果是患者,创建患者
扩展信息
//
根据角色创建
扩展信息
if
role
==
"patient"
{
if
role
==
"patient"
{
patientProfile
:=
&
model
.
PatientProfile
{
patientProfile
:=
&
model
.
PatientProfile
{
ID
:
uuid
.
New
()
.
String
(),
ID
:
uuid
.
New
()
.
String
(),
...
@@ -131,6 +131,16 @@ func (s *Service) Register(ctx context.Context, req *RegisterRequest) (*LoginRes
...
@@ -131,6 +131,16 @@ func (s *Service) Register(ctx context.Context, req *RegisterRequest) (*LoginRes
if
err
:=
s
.
db
.
Create
(
patientProfile
)
.
Error
;
err
!=
nil
{
if
err
:=
s
.
db
.
Create
(
patientProfile
)
.
Error
;
err
!=
nil
{
fmt
.
Printf
(
"创建患者扩展信息失败: %v
\n
"
,
err
)
fmt
.
Printf
(
"创建患者扩展信息失败: %v
\n
"
,
err
)
}
}
}
else
if
role
==
"doctor"
{
doctor
:=
&
model
.
Doctor
{
ID
:
uuid
.
New
()
.
String
(),
UserID
:
user
.
ID
,
Name
:
req
.
RealName
,
Status
:
"pending"
,
// 注册后需审核
}
if
err
:=
s
.
db
.
Create
(
doctor
)
.
Error
;
err
!=
nil
{
fmt
.
Printf
(
"创建医生档案失败: %v
\n
"
,
err
)
}
}
}
tokenPair
,
err
:=
utils
.
GenerateTokenPair
(
user
.
ID
,
user
.
Role
)
tokenPair
,
err
:=
utils
.
GenerateTokenPair
(
user
.
ID
,
user
.
Role
)
...
...
web/src/app/(auth)/login/page.tsx
View file @
e26e5d42
This diff is collapsed.
Click to expand it.
web/src/app/(main)/doctor/consult/AIPanel.tsx
View file @
e26e5d42
...
@@ -362,6 +362,7 @@ const AIPanel: React.FC<AIPanelProps> = ({
...
@@ -362,6 +362,7 @@ const AIPanel: React.FC<AIPanelProps> = ({
display
:
'
flex
'
,
display
:
'
flex
'
,
flexDirection
:
'
column
'
,
flexDirection
:
'
column
'
,
overflow
:
'
hidden
'
,
overflow
:
'
hidden
'
,
boxShadow
:
'
0 1px 3px rgba(0,0,0,0.04)
'
,
}
}
>
}
}
>
{
/* 标题 */
}
{
/* 标题 */
}
<
div
style=
{
{
<
div
style=
{
{
...
@@ -371,9 +372,17 @@ const AIPanel: React.FC<AIPanelProps> = ({
...
@@ -371,9 +372,17 @@ const AIPanel: React.FC<AIPanelProps> = ({
alignItems
:
'
center
'
,
alignItems
:
'
center
'
,
gap
:
8
,
gap
:
8
,
flexShrink
:
0
,
flexShrink
:
0
,
background
:
'
linear-gradient(135deg, #f6ffed 0%, #e6fffb 100%)
'
,
}
}
>
}
}
>
<
RobotOutlined
style=
{
{
color
:
'
#52c41a
'
,
fontSize
:
16
}
}
/>
<
div
style=
{
{
<
span
style=
{
{
fontWeight
:
600
,
fontSize
:
14
}
}
>
AI 辅助
</
span
>
width
:
28
,
height
:
28
,
borderRadius
:
8
,
background
:
'
linear-gradient(135deg, #52c41a 0%, #73d13d 100%)
'
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
justifyContent
:
'
center
'
,
boxShadow
:
'
0 2px 6px rgba(82, 196, 26, 0.3)
'
,
}
}
>
<
RobotOutlined
style=
{
{
color
:
'
#fff
'
,
fontSize
:
14
}
}
/>
</
div
>
<
span
style=
{
{
fontWeight
:
600
,
fontSize
:
14
,
color
:
'
#1d2129
'
}
}
>
AI 辅助
</
span
>
{
hasActiveConsult
&&
(
{
hasActiveConsult
&&
(
<
Badge
status=
"processing"
style=
{
{
marginLeft
:
2
}
}
/>
<
Badge
status=
"processing"
style=
{
{
marginLeft
:
2
}
}
/>
)
}
)
}
...
@@ -470,9 +479,19 @@ const AIPanel: React.FC<AIPanelProps> = ({
...
@@ -470,9 +479,19 @@ const AIPanel: React.FC<AIPanelProps> = ({
]
}
]
}
/>
/>
)
:
(
)
:
(
<
div
style=
{
{
textAlign
:
'
center
'
,
paddingTop
:
60
}
}
>
<
div
style=
{
{
textAlign
:
'
center
'
,
paddingTop
:
60
,
padding
:
'
60px 24px
'
}
}
>
<
RobotOutlined
style=
{
{
fontSize
:
40
,
color
:
'
#d9d9d9
'
,
display
:
'
block
'
,
marginBottom
:
14
}
}
/>
<
div
style=
{
{
<
Text
type=
"secondary"
style=
{
{
fontSize
:
13
}
}
>
接诊后自动开始AI辅助
</
Text
>
width
:
64
,
height
:
64
,
borderRadius
:
16
,
margin
:
'
0 auto 16px
'
,
background
:
'
linear-gradient(135deg, #f6ffed 0%, #e6fffb 100%)
'
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
justifyContent
:
'
center
'
,
}
}
>
<
RobotOutlined
style=
{
{
fontSize
:
28
,
color
:
'
#b7eb8f
'
}
}
/>
</
div
>
<
Text
strong
style=
{
{
fontSize
:
14
,
display
:
'
block
'
,
marginBottom
:
6
,
color
:
'
#8c8c8c
'
}
}
>
AI 辅助诊疗
</
Text
>
<
Text
type=
"secondary"
style=
{
{
fontSize
:
12
,
lineHeight
:
1.6
}
}
>
接诊后将自动启动 AI 辅助分析,
<
br
/>
为您提供鉴别诊断、用药建议等支持
</
Text
>
</
div
>
</
div
>
)
}
)
}
</
div
>
</
div
>
...
...
web/src/app/(main)/doctor/consult/ChatPanel.tsx
View file @
e26e5d42
...
@@ -382,6 +382,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
...
@@ -382,6 +382,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
border
:
'
1px solid #E5E8EF
'
,
border
:
'
1px solid #E5E8EF
'
,
background
:
'
#fff
'
,
background
:
'
#fff
'
,
overflow
:
'
hidden
'
,
overflow
:
'
hidden
'
,
boxShadow
:
'
0 1px 3px rgba(0,0,0,0.04)
'
,
}
}
>
}
}
>
{
/* 顶部患者信息栏 */
}
{
/* 顶部患者信息栏 */
}
<
div
style=
{
{
<
div
style=
{
{
...
@@ -391,7 +392,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
...
@@ -391,7 +392,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
alignItems
:
'
center
'
,
alignItems
:
'
center
'
,
gap
:
10
,
gap
:
10
,
flexShrink
:
0
,
flexShrink
:
0
,
background
:
'
#fff
'
,
background
:
'
linear-gradient(135deg, #ffffff 0%, #f9fafb 100%)
'
,
}
}
>
}
}
>
{
activeConsult
?
(
{
activeConsult
?
(
<>
<>
...
@@ -429,8 +430,9 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
...
@@ -429,8 +430,9 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
{
activeConsult
.
started_at
&&
activeConsult
.
status
===
'
in_progress
'
&&
(
{
activeConsult
.
started_at
&&
activeConsult
.
status
===
'
in_progress
'
&&
(
<
div
style=
{
{
<
div
style=
{
{
display
:
'
flex
'
,
alignItems
:
'
center
'
,
gap
:
4
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
gap
:
4
,
background
:
'
#f6ffed
'
,
border
:
'
1px solid #b7eb8f
'
,
borderRadius
:
6
,
background
:
'
linear-gradient(135deg, #f6ffed 0%, #e8ffd6 100%)
'
,
padding
:
'
2px 10px
'
,
fontSize
:
13
,
fontWeight
:
500
,
color
:
'
#52c41a
'
,
border
:
'
1px solid #b7eb8f
'
,
borderRadius
:
8
,
padding
:
'
3px 12px
'
,
fontSize
:
13
,
fontWeight
:
600
,
color
:
'
#389e0d
'
,
fontVariantNumeric
:
'
tabular-nums
'
,
fontVariantNumeric
:
'
tabular-nums
'
,
}
}
>
}
}
>
<
ClockCircleOutlined
style=
{
{
fontSize
:
12
}
}
/>
<
ClockCircleOutlined
style=
{
{
fontSize
:
12
}
}
/>
...
@@ -488,7 +490,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
...
@@ -488,7 +490,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
</
div
>
</
div
>
{
/* 消息区域 */
}
{
/* 消息区域 */
}
<
div
style=
{
{
flex
:
1
,
overflow
:
'
auto
'
,
padding
:
16
,
background
:
'
#f5f7fb
'
}
}
>
<
div
style=
{
{
flex
:
1
,
overflow
:
'
auto
'
,
padding
:
16
,
background
:
'
linear-gradient(180deg, #f5f7fb 0%, #f0f2f5 100%)
'
}
}
>
{
activeConsult
?
(
{
activeConsult
?
(
<>
<>
{
messages
.
length
===
0
&&
(
{
messages
.
length
===
0
&&
(
...
@@ -617,13 +619,16 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
...
@@ -617,13 +619,16 @@ const ChatPanel: React.FC<ChatPanelProps> = ({
onClick=
{
handleSend
}
onClick=
{
handleSend
}
loading=
{
sending
}
loading=
{
sending
}
style=
{
{
style=
{
{
background
:
'
#52c41a
'
,
background
:
'
linear-gradient(135deg, #52c41a 0%, #73d13d 100%)
'
,
borderColor
:
'
#52c41a
'
,
borderColor
:
'
#52c41a
'
,
borderRadius
:
8
,
borderRadius
:
10
,
height
:
'
auto
'
,
height
:
'
auto
'
,
alignSelf
:
'
flex-end
'
,
alignSelf
:
'
flex-end
'
,
paddingBottom
:
8
,
paddingBottom
:
10
,
paddingTop
:
8
,
paddingTop
:
10
,
paddingLeft
:
16
,
paddingRight
:
16
,
boxShadow
:
'
0 2px 6px rgba(82, 196, 26, 0.3)
'
,
}
}
}
}
>
>
发送
发送
...
...
web/src/app/(main)/doctor/consult/PatientList.tsx
View file @
e26e5d42
This diff is collapsed.
Click to expand it.
web/src/app/(main)/doctor/consult/page.tsx
View file @
e26e5d42
...
@@ -287,74 +287,92 @@ const ConsultPage: React.FC = () => {
...
@@ -287,74 +287,92 @@ const ConsultPage: React.FC = () => {
{
label
:
'
AI使用率
'
,
value
:
workbenchStats
.
ai_usage_rate
.
toFixed
(
0
),
unit
:
'
%
'
,
icon
:
<
RobotOutlined
/>,
color
:
'
#0891B2
'
},
{
label
:
'
AI使用率
'
,
value
:
workbenchStats
.
ai_usage_rate
.
toFixed
(
0
),
unit
:
'
%
'
,
icon
:
<
RobotOutlined
/>,
color
:
'
#0891B2
'
},
]
:
[];
]
:
[];
const
statBgMap
:
Record
<
string
,
string
>
=
{
'
候诊中
'
:
'
linear-gradient(135deg, #FFF7E6 0%, #FFE7BA 100%)
'
,
'
接诊中
'
:
'
linear-gradient(135deg, #F6FFED 0%, #D9F7BE 100%)
'
,
'
今日完成
'
:
'
linear-gradient(135deg, #E6FFFB 0%, #B5F5EC 100%)
'
,
'
今日收入
'
:
'
linear-gradient(135deg, #E6F7FF 0%, #BAE7FF 100%)
'
,
};
return
(
return
(
<
div
style=
{
{
height
:
'
calc(100vh - 72px)
'
,
display
:
'
flex
'
,
flexDirection
:
'
column
'
,
gap
:
1
6
,
padding
:
'
12px 16px
'
}
}
>
<
div
style=
{
{
height
:
'
calc(100vh - 72px)
'
,
display
:
'
flex
'
,
flexDirection
:
'
column
'
,
gap
:
1
2
,
padding
:
'
12px 16px
'
}
}
>
{
/* 顶部
标题 + 统计卡片
*/
}
{
/* 顶部
统计栏
*/
}
<
div
style=
{
{
flexShrink
:
0
}
}
>
<
div
style=
{
{
flexShrink
:
0
}
}
>
<
div
style=
{
{
display
:
'
flex
'
,
alignItems
:
'
center
'
,
gap
:
16
}
}
>
<
div
style=
{
{
display
:
'
flex
'
,
alignItems
:
'
stretch
'
,
gap
:
10
}
}
>
<
div
style=
{
{
marginLeft
:
'
auto
'
,
display
:
'
flex
'
,
gap
:
10
,
alignItems
:
'
center
'
}
}
>
{
statItems
.
map
(({
label
,
value
,
color
,
icon
,
unit
})
=>
(
{
statItems
.
map
(({
label
,
value
,
color
,
icon
,
unit
})
=>
(
<
div
key=
{
label
}
style=
{
{
background
:
'
#fff
'
,
borderRadius
:
10
,
padding
:
'
8px 18px
'
,
border
:
'
1px solid #E5E8EF
'
,
minWidth
:
100
,
boxShadow
:
'
0 1px 4px rgba(0,0,0,0.04)
'
,
}
}
>
<
div
style=
{
{
fontSize
:
11
,
color
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
gap
:
4
,
marginBottom
:
2
}
}
>
{
icon
}
{
label
}
</
div
>
<
div
style=
{
{
fontSize
:
22
,
fontWeight
:
700
,
color
,
lineHeight
:
1.2
}
}
>
{
value
}
<
span
style=
{
{
fontSize
:
11
,
fontWeight
:
400
,
marginLeft
:
2
}
}
>
{
unit
}
</
span
>
</
div
>
</
div
>
))
}
<
div
<
div
onClick=
{
()
=>
setShowEfficiency
(
!
showEfficiency
)
}
key=
{
label
}
style=
{
{
style=
{
{
cursor
:
'
pointer
'
,
padding
:
'
6px 10px
'
,
borderRadius
:
8
,
flex
:
1
,
background
:
showEfficiency
?
'
#F0FFF5
'
:
'
#f5f5f5
'
,
background
:
statBgMap
[
label
]
||
'
#fff
'
,
border
:
`1px solid ${showEfficiency ? '#91d5ff' : '#e8e8e8'}`
,
borderRadius
:
12
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
gap
:
4
,
padding
:
'
12px 16px
'
,
fontSize
:
12
,
color
:
showEfficiency
?
'
#2B9E6E
'
:
'
#8c8c8c
'
,
border
:
'
1px solid rgba(0,0,0,0.04)
'
,
transition
:
'
all 0.2s
'
,
boxShadow
:
'
0 1px 3px rgba(0,0,0,0.04)
'
,
transition
:
'
transform 0.2s, box-shadow 0.2s
'
,
cursor
:
'
default
'
,
}
}
onMouseEnter=
{
e
=>
{
(
e
.
currentTarget
as
HTMLDivElement
).
style
.
transform
=
'
translateY(-1px)
'
;
(
e
.
currentTarget
as
HTMLDivElement
).
style
.
boxShadow
=
'
0 4px 12px rgba(0,0,0,0.08)
'
;
}
}
onMouseLeave=
{
e
=>
{
(
e
.
currentTarget
as
HTMLDivElement
).
style
.
transform
=
'
translateY(0)
'
;
(
e
.
currentTarget
as
HTMLDivElement
).
style
.
boxShadow
=
'
0 1px 3px rgba(0,0,0,0.04)
'
;
}
}
}
}
>
>
<
DashboardOutlined
/>
<
div
style=
{
{
fontSize
:
12
,
color
:
'
#666
'
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
gap
:
5
,
marginBottom
:
6
}
}
>
效率
{
React
.
cloneElement
(
icon
as
React
.
ReactElement
,
{
style
:
{
color
,
fontSize
:
14
}
})
}
{
showEfficiency
?
<
UpOutlined
style=
{
{
fontSize
:
10
}
}
/>
:
<
DownOutlined
style=
{
{
fontSize
:
10
}
}
/>
}
{
label
}
</
div
>
<
div
style=
{
{
fontSize
:
26
,
fontWeight
:
700
,
color
,
lineHeight
:
1
,
fontVariantNumeric
:
'
tabular-nums
'
}
}
>
{
value
}
<
span
style=
{
{
fontSize
:
12
,
fontWeight
:
400
,
marginLeft
:
3
,
color
:
'
#999
'
}
}
>
{
unit
}
</
span
>
</
div
>
</
div
>
</
div
>
))
}
<
div
onClick=
{
()
=>
setShowEfficiency
(
!
showEfficiency
)
}
style=
{
{
cursor
:
'
pointer
'
,
padding
:
'
12px 14px
'
,
borderRadius
:
12
,
background
:
showEfficiency
?
'
linear-gradient(135deg, #F0FFF5 0%, #D9F7E0 100%)
'
:
'
#f8f9fa
'
,
border
:
`1px solid ${showEfficiency ? '#b7eb8f' : '#e8e8e8'}`
,
display
:
'
flex
'
,
flexDirection
:
'
column
'
,
alignItems
:
'
center
'
,
justifyContent
:
'
center
'
,
gap
:
4
,
fontSize
:
12
,
color
:
showEfficiency
?
'
#2B9E6E
'
:
'
#8c8c8c
'
,
transition
:
'
all 0.2s
'
,
minWidth
:
56
,
}
}
>
<
DashboardOutlined
style=
{
{
fontSize
:
16
}
}
/>
<
span
style=
{
{
fontSize
:
11
}
}
>
效率
</
span
>
{
showEfficiency
?
<
UpOutlined
style=
{
{
fontSize
:
9
}
}
/>
:
<
DownOutlined
style=
{
{
fontSize
:
9
}
}
/>
}
</
div
>
</
div
>
</
div
>
</
div
>
{
/* 可折叠效率指标行 */
}
{
/* 可折叠效率指标行 */
}
{
showEfficiency
&&
efficiencyItems
.
length
>
0
&&
(
{
showEfficiency
&&
efficiencyItems
.
length
>
0
&&
(
<
div
style=
{
{
<
div
style=
{
{
display
:
'
flex
'
,
gap
:
8
,
marginTop
:
8
,
padding
:
'
8px 12
px
'
,
display
:
'
flex
'
,
gap
:
8
,
marginTop
:
8
,
padding
:
'
10px 16
px
'
,
background
:
'
#fafafa
'
,
borderRadius
:
10
,
border
:
'
1px solid #f0f0f0
'
,
background
:
'
linear-gradient(135deg, #fafbfc 0%, #f0f2f5 100%)
'
,
overflow
:
'
auto
'
,
borderRadius
:
12
,
border
:
'
1px solid #eee
'
,
}
}
>
}
}
>
{
efficiencyItems
.
map
(({
label
,
value
,
unit
,
icon
,
color
})
=>
(
{
efficiencyItems
.
map
(({
label
,
value
,
unit
,
icon
,
color
})
=>
(
<
div
<
div
key=
{
label
}
key=
{
label
}
style=
{
{
style=
{
{
flex
:
1
,
minWidth
:
90
,
textAlign
:
'
center
'
,
flex
:
1
,
minWidth
:
80
,
textAlign
:
'
center
'
,
padding
:
'
4px 8px
'
,
padding
:
'
6px 8px
'
,
borderRadius
:
8
,
background
:
'
rgba(255,255,255,0.7)
'
,
}
}
}
}
>
>
<
div
style=
{
{
fontSize
:
11
,
color
:
'
#8c8c8c
'
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
justifyContent
:
'
center
'
,
gap
:
3
,
marginBottom
:
2
}
}
>
<
div
style=
{
{
fontSize
:
11
,
color
:
'
#8c8c8c
'
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
justifyContent
:
'
center
'
,
gap
:
3
,
marginBottom
:
4
}
}
>
{
React
.
cloneElement
(
icon
as
React
.
ReactElement
,
{
style
:
{
color
,
fontSize
:
1
1
}
})
}
{
React
.
cloneElement
(
icon
as
React
.
ReactElement
,
{
style
:
{
color
,
fontSize
:
1
2
}
})
}
{
label
}
{
label
}
</
div
>
</
div
>
<
div
style=
{
{
fontSize
:
18
,
fontWeight
:
600
,
color
,
lineHeight
:
1.3
}
}
>
<
div
style=
{
{
fontSize
:
20
,
fontWeight
:
600
,
color
,
lineHeight
:
1.2
,
fontVariantNumeric
:
'
tabular-nums
'
}
}
>
{
value
}
{
value
}
<
span
style=
{
{
fontSize
:
10
,
fontWeight
:
400
,
marginLeft
:
1
}
}
>
{
unit
}
</
span
>
<
span
style=
{
{
fontSize
:
10
,
fontWeight
:
400
,
marginLeft
:
2
,
color
:
'
#999
'
}
}
>
{
unit
}
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
))
}
))
}
...
@@ -363,9 +381,9 @@ const ConsultPage: React.FC = () => {
...
@@ -363,9 +381,9 @@ const ConsultPage: React.FC = () => {
</
div
>
</
div
>
{
/* 主体三栏 */
}
{
/* 主体三栏 */
}
<
div
style=
{
{
flex
:
1
,
display
:
'
flex
'
,
gap
:
1
2
,
overflow
:
'
hidden
'
,
minHeight
:
0
}
}
>
<
div
style=
{
{
flex
:
1
,
display
:
'
flex
'
,
gap
:
1
0
,
overflow
:
'
hidden
'
,
minHeight
:
0
}
}
>
{
/* 左:患者列表 */
}
{
/* 左:患者列表 */
}
<
div
style=
{
{
width
:
2
72
,
flexShrink
:
0
}
}
>
<
div
style=
{
{
width
:
2
80
,
flexShrink
:
0
}
}
>
<
PatientList
<
PatientList
patients=
{
patientList
}
patients=
{
patientList
}
onSelectPatient=
{
handleSelectPatient
}
onSelectPatient=
{
handleSelectPatient
}
...
...
web/src/app/(main)/doctor/schedule/page.tsx
View file @
e26e5d42
'
use client
'
;
'
use client
'
;
import
React
,
{
useState
,
useEffect
}
from
'
react
'
;
import
React
,
{
useState
,
useEffect
}
from
'
react
'
;
import
{
Card
,
Calendar
,
Button
,
Modal
,
Form
,
TimePicker
,
InputNumber
,
Tag
,
Typography
,
Space
,
Row
,
Col
,
Badge
,
List
,
message
,
Spin
}
from
'
antd
'
;
import
{
Card
,
Calendar
,
Button
,
Modal
,
Form
,
TimePicker
,
InputNumber
,
Tag
,
Typography
,
Space
,
Row
,
Col
,
Badge
,
List
,
Spin
,
App
}
from
'
antd
'
;
import
{
DrawerForm
}
from
'
@ant-design/pro-components
'
;
import
{
DrawerForm
}
from
'
@ant-design/pro-components
'
;
import
{
PlusOutlined
,
DeleteOutlined
,
CalendarOutlined
}
from
'
@ant-design/icons
'
;
import
{
PlusOutlined
,
DeleteOutlined
,
CalendarOutlined
}
from
'
@ant-design/icons
'
;
import
dayjs
,
{
Dayjs
}
from
'
dayjs
'
;
import
dayjs
,
{
Dayjs
}
from
'
dayjs
'
;
...
@@ -10,6 +10,7 @@ import { doctorPortalApi, type ScheduleSlot } from '@/api/doctorPortal';
...
@@ -10,6 +10,7 @@ import { doctorPortalApi, type ScheduleSlot } from '@/api/doctorPortal';
const
{
Text
}
=
Typography
;
const
{
Text
}
=
Typography
;
const
DoctorSchedulePage
:
React
.
FC
=
()
=>
{
const
DoctorSchedulePage
:
React
.
FC
=
()
=>
{
const
{
message
,
modal
}
=
App
.
useApp
();
const
[
selectedDate
,
setSelectedDate
]
=
useState
<
Dayjs
>
(
dayjs
());
const
[
selectedDate
,
setSelectedDate
]
=
useState
<
Dayjs
>
(
dayjs
());
const
[
isModalOpen
,
setIsModalOpen
]
=
useState
(
false
);
const
[
isModalOpen
,
setIsModalOpen
]
=
useState
(
false
);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
loading
,
setLoading
]
=
useState
(
false
);
...
@@ -59,7 +60,7 @@ const DoctorSchedulePage: React.FC = () => {
...
@@ -59,7 +60,7 @@ const DoctorSchedulePage: React.FC = () => {
setIsModalOpen
(
true
);
setIsModalOpen
(
true
);
};
};
const
handleSaveSchedule
=
async
(
values
:
any
)
=>
{
const
handleSaveSchedule
=
async
(
values
:
any
)
:
Promise
<
boolean
>
=>
{
try
{
try
{
await
doctorPortalApi
.
createSchedule
([{
await
doctorPortalApi
.
createSchedule
([{
date
:
selectedDate
.
format
(
'
YYYY-MM-DD
'
),
date
:
selectedDate
.
format
(
'
YYYY-MM-DD
'
),
...
@@ -68,15 +69,16 @@ const DoctorSchedulePage: React.FC = () => {
...
@@ -68,15 +69,16 @@ const DoctorSchedulePage: React.FC = () => {
max_count
:
values
.
max_count
,
max_count
:
values
.
max_count
,
}]);
}]);
message
.
success
(
'
排班添加成功
'
);
message
.
success
(
'
排班添加成功
'
);
setIsModalOpen
(
false
);
fetchSchedule
();
fetchSchedule
();
return
true
;
}
catch
{
}
catch
{
message
.
error
(
'
添加排班失败
'
);
message
.
error
(
'
添加排班失败
'
);
return
false
;
}
}
};
};
const
handleDeleteSlot
=
(
slotId
:
string
)
=>
{
const
handleDeleteSlot
=
(
slotId
:
string
)
=>
{
M
odal
.
confirm
({
m
odal
.
confirm
({
title
:
'
确认删除
'
,
title
:
'
确认删除
'
,
content
:
'
确定要删除该排班吗?
'
,
content
:
'
确定要删除该排班吗?
'
,
onOk
:
async
()
=>
{
onOk
:
async
()
=>
{
...
@@ -159,10 +161,7 @@ const DoctorSchedulePage: React.FC = () => {
...
@@ -159,10 +161,7 @@ const DoctorSchedulePage: React.FC = () => {
title=
{
`添加排班 - ${selectedDate.format('YYYY-MM-DD')}`
}
title=
{
`添加排班 - ${selectedDate.format('YYYY-MM-DD')}`
}
open=
{
isModalOpen
}
open=
{
isModalOpen
}
onOpenChange=
{
setIsModalOpen
}
onOpenChange=
{
setIsModalOpen
}
onFinish=
{
async
(
values
:
any
)
=>
{
onFinish=
{
handleSaveSchedule
}
await
handleSaveSchedule
(
values
);
return
true
;
}
}
drawerProps=
{
{
placement
:
'
right
'
,
destroyOnClose
:
true
}
}
drawerProps=
{
{
placement
:
'
right
'
,
destroyOnClose
:
true
}
}
width=
{
480
}
width=
{
480
}
>
>
...
...
web/src/app/(main)/patient/chronic/page.tsx
View file @
e26e5d42
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
import
React
,
{
useState
}
from
'
react
'
;
import
React
,
{
useState
}
from
'
react
'
;
import
{
import
{
Card
,
Tabs
,
Button
,
Tag
,
Space
,
Form
,
Input
,
Select
,
App
,
Card
,
Button
,
Tag
,
Space
,
Form
,
Input
,
Select
,
App
,
DatePicker
,
Switch
,
Popconfirm
,
Typography
,
Row
,
Col
,
DatePicker
,
Switch
,
Popconfirm
,
Typography
,
Row
,
Col
,
TimePicker
,
Statistic
,
Empty
,
TimePicker
,
Statistic
,
Empty
,
}
from
'
antd
'
;
}
from
'
antd
'
;
...
@@ -566,17 +566,68 @@ const MetricsTab: React.FC = () => {
...
@@ -566,17 +566,68 @@ const MetricsTab: React.FC = () => {
// ========== 主页面 ==========
// ========== 主页面 ==========
const
PatientChronicPage
:
React
.
FC
=
()
=>
{
const
PatientChronicPage
:
React
.
FC
=
()
=>
{
const
tabItems
=
[
const
[
activeKey
,
setActiveKey
]
=
useState
(
'
records
'
);
{
key
:
'
records
'
,
label
:
<><
HeartOutlined
/>
慢病档案
</>,
children
:
<
ChronicRecordsTab
/>
},
{
key
:
'
renewals
'
,
label
:
<><
FileAddOutlined
/>
续方申请
</>,
children
:
<
RenewalsTab
/>
},
const
menuItems
=
[
{
key
:
'
reminders
'
,
label
:
<><
BellOutlined
/>
用药提醒
</>,
children
:
<
RemindersTab
/>
},
{
key
:
'
records
'
,
icon
:
<
HeartOutlined
/>,
label
:
'
慢病档案
'
},
{
key
:
'
metrics
'
,
label
:
<><
LineChartOutlined
/>
健康指标
</>,
children
:
<
MetricsTab
/>
},
{
key
:
'
renewals
'
,
icon
:
<
FileAddOutlined
/>,
label
:
'
续方申请
'
},
{
key
:
'
reminders
'
,
icon
:
<
BellOutlined
/>,
label
:
'
用药提醒
'
},
{
key
:
'
metrics
'
,
icon
:
<
LineChartOutlined
/>,
label
:
'
健康指标
'
},
];
];
const
contentMap
:
Record
<
string
,
React
.
ReactNode
>
=
{
records
:
<
ChronicRecordsTab
/>,
renewals
:
<
RenewalsTab
/>,
reminders
:
<
RemindersTab
/>,
metrics
:
<
MetricsTab
/>,
};
const
currentMenu
=
menuItems
.
find
(
m
=>
m
.
key
===
activeKey
);
return
(
return
(
<
div
style=
{
{
padding
:
'
12px 16px
'
,
display
:
'
flex
'
,
flexDirection
:
'
column
'
,
gap
:
16
}
}
>
<
div
style=
{
{
padding
:
'
12px 16px
'
,
display
:
'
flex
'
,
gap
:
16
,
minHeight
:
'
calc(100vh - 120px)
'
}
}
>
<
Card
style=
{
{
borderRadius
:
12
,
border
:
'
1px solid #E5E8EF
'
}
}
>
{
/* 左侧导航 */
}
<
Tabs
items=
{
tabItems
}
/>
<
Card
style=
{
{
width
:
200
,
flexShrink
:
0
,
borderRadius
:
12
,
border
:
'
1px solid #E5E8EF
'
}
}
styles=
{
{
body
:
{
padding
:
'
12px 8px
'
}
}
}
>
<
div
style=
{
{
padding
:
'
4px 12px 16px
'
,
marginBottom
:
8
,
borderBottom
:
'
1px solid #f0f0f0
'
}
}
>
<
Space
>
<
MedicineBoxOutlined
style=
{
{
color
:
'
#2B6DE8
'
,
fontSize
:
16
}
}
/>
<
Text
strong
style=
{
{
fontSize
:
15
}
}
>
慢病管理
</
Text
>
</
Space
>
</
div
>
{
menuItems
.
map
(
item
=>
(
<
div
key=
{
item
.
key
}
onClick=
{
()
=>
setActiveKey
(
item
.
key
)
}
style=
{
{
display
:
'
flex
'
,
alignItems
:
'
center
'
,
gap
:
10
,
padding
:
'
10px 16px
'
,
borderRadius
:
8
,
cursor
:
'
pointer
'
,
marginBottom
:
4
,
transition
:
'
all 0.2s
'
,
background
:
activeKey
===
item
.
key
?
'
#EBF1FF
'
:
'
transparent
'
,
color
:
activeKey
===
item
.
key
?
'
#2B6DE8
'
:
'
#595959
'
,
fontWeight
:
activeKey
===
item
.
key
?
600
:
400
,
}
}
>
<
span
style=
{
{
fontSize
:
16
}
}
>
{
item
.
icon
}
</
span
>
<
span
style=
{
{
fontSize
:
14
}
}
>
{
item
.
label
}
</
span
>
</
div
>
))
}
</
Card
>
{
/* 右侧内容 */
}
<
Card
title=
{
currentMenu
&&
(
<
Space
>
<
span
style=
{
{
color
:
'
#2B6DE8
'
}
}
>
{
currentMenu
.
icon
}
</
span
>
<
span
>
{
currentMenu
.
label
}
</
span
>
</
Space
>
)
}
style=
{
{
flex
:
1
,
borderRadius
:
12
,
border
:
'
1px solid #E5E8EF
'
}
}
styles=
{
{
body
:
{
padding
:
'
16px 20px
'
}
}
}
>
{
contentMap
[
activeKey
]
}
</
Card
>
</
Card
>
</
div
>
</
div
>
);
);
...
...
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