Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
performance-score
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tools
performance-score
Commits
559a85c1
Commit
559a85c1
authored
Sep 24, 2025
by
Performance System
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
1
parent
e1d6e3c2
Pipeline
#3236
passed with stage
in 31 seconds
Changes
5
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
462 additions
and
4 deletions
+462
-4
backend/routers/history.py
+0
-2
src/services/api.js
+19
-0
src/store/data.js
+99
-0
src/views/admin/AdminPanel.vue
+90
-2
test_manual_save.html
+254
-0
No files found.
backend/routers/history.py
View file @
559a85c1
...
...
@@ -355,5 +355,3 @@ async def cleanup_old_history(
src/services/api.js
View file @
559a85c1
...
...
@@ -484,6 +484,25 @@ export const historyApi = {
return
apiClient
.
post
(
'/api/history/cleanup'
,
{
keep_months
:
keepMonths
})
},
// 检查指定月份数据是否存在
async
checkMonthExists
(
month
)
{
try
{
const
response
=
await
apiClient
.
get
(
`/api/history/
${
month
}
`
)
return
true
}
catch
(
error
)
{
// 如果是404错误,说明数据不存在,返回false
if
(
error
.
message
&&
error
.
message
.
includes
(
'指定月份的历史记录不存在'
))
{
return
false
}
if
(
error
.
response
&&
error
.
response
.
status
===
404
)
{
return
false
}
// 其他错误继续抛出
console
.
error
(
`检查月份
${
month
}
存在性时出错:`
,
error
)
throw
error
}
},
}
...
...
src/store/data.js
View file @
559a85c1
...
...
@@ -801,6 +801,104 @@ export const useDataStore = defineStore('data', () => {
}
}
/**
* 手动保存当前月份的统计数据到历史记录
* @param {boolean} forceOverwrite - 是否强制覆盖已存在的数据
* @returns {Object} 保存结果 { success: boolean, message: string, isOverwrite: boolean }
*/
const
saveCurrentMonthStatsManually
=
async
(
forceOverwrite
=
false
)
=>
{
try
{
const
currentDate
=
new
Date
()
const
monthKey
=
`
${
currentDate
.
getFullYear
()}
-
${
String
(
currentDate
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)}
`
console
.
log
(
`📊 手动保存
${
monthKey
}
月份统计数据...`
)
// 检查当前月份数据是否已存在
console
.
log
(
`🔍 检查月份
${
monthKey
}
数据是否存在...`
)
const
monthExists
=
await
historyApi
.
checkMonthExists
(
monthKey
)
console
.
log
(
`📋 月份
${
monthKey
}
存在状态:
${
monthExists
}
`
)
if
(
monthExists
&&
!
forceOverwrite
)
{
console
.
log
(
`⚠️ 月份
${
monthKey
}
数据已存在,需要用户确认覆盖`
)
return
{
success
:
false
,
message
:
`
${
monthKey
}
月份的数据已存在,是否要覆盖?`
,
isOverwrite
:
true
,
monthKey
}
}
// 获取当前所有用户的统计数据
const
currentStats
=
users
.
value
.
filter
(
user
=>
user
.
role
===
'user'
)
.
map
(
user
=>
{
const
userInstitutions
=
getInstitutionsByUserId
(
user
.
id
)
return
{
userId
:
user
.
id
,
userName
:
user
.
name
,
institutionCount
:
userInstitutions
.
length
,
interactionScore
:
calculateInteractionScore
(
user
.
id
),
performanceScore
:
calculatePerformanceScore
(
user
.
id
),
institutions
:
userInstitutions
.
map
(
inst
=>
({
id
:
inst
.
id
,
name
:
inst
.
name
,
imageCount
:
inst
.
images
?
inst
.
images
.
length
:
0
}))
}
})
// 保存完整的机构和图片数据
const
institutionsWithImages
=
institutions
.
value
.
map
(
inst
=>
({
id
:
inst
.
id
,
institutionId
:
inst
.
institutionId
,
name
:
inst
.
name
,
ownerId
:
inst
.
ownerId
,
images
:
inst
.
images
||
[]
}))
const
historyData
=
{
month
:
monthKey
,
save_time
:
new
Date
().
toISOString
(),
total_users
:
currentStats
.
length
,
total_institutions
:
institutions
.
value
.
length
,
total_images
:
institutions
.
value
.
reduce
((
total
,
inst
)
=>
total
+
(
inst
.
images
?
inst
.
images
.
length
:
0
),
0
),
user_stats
:
currentStats
,
institutions_data
:
institutionsWithImages
}
// 保存到数据库
console
.
log
(
`💾 开始保存
${
monthKey
}
月份数据到数据库...`
)
console
.
log
(
'📊 保存的数据概览:'
,
{
month
:
monthKey
,
totalUsers
:
historyData
.
total_users
,
totalInstitutions
:
historyData
.
total_institutions
,
totalImages
:
historyData
.
total_images
,
userStatsCount
:
historyData
.
user_stats
.
length
})
await
historyApi
.
save
(
historyData
)
const
message
=
monthExists
?
`✅
${
monthKey
}
月份统计数据覆盖保存成功`
:
`✅
${
monthKey
}
月份统计数据保存成功`
console
.
log
(
message
)
return
{
success
:
true
,
message
,
isOverwrite
:
false
,
monthKey
}
}
catch
(
error
)
{
console
.
error
(
'手动保存历史统计数据失败:'
,
error
)
return
{
success
:
false
,
message
:
`保存失败:
${
error
.
message
||
'未知错误'
}
`
,
isOverwrite
:
false
}
}
}
// ========== 数据管理 ==========
/**
...
...
@@ -1006,6 +1104,7 @@ export const useDataStore = defineStore('data', () => {
// 历史数据
saveCurrentMonthStats
,
saveCurrentMonthStatsManually
,
getHistoryStats
,
getAvailableHistoryMonths
,
getMonthStats
,
...
...
src/views/admin/AdminPanel.vue
View file @
559a85c1
...
...
@@ -360,6 +360,15 @@
<p
class=
"history-subtitle"
>
查看历史月份各用户的绩效数据
</p>
</div>
<div
class=
"header-actions"
>
<el-button
type=
"success"
@
click=
"handleManualSave"
:loading=
"manualSaveLoading"
>
<el-icon><DocumentAdd
/></el-icon>
手动保存当前数据
</el-button>
<el-dropdown
@
command=
"handleHistoryExportCommand"
:disabled=
"!selectedMonthData"
...
...
@@ -941,6 +950,7 @@ import {
Calendar
,
InfoFilled
,
Document
,
DocumentAdd
,
Grid
,
List
,
Printer
...
...
@@ -1013,6 +1023,7 @@ const selectedMonthData = ref(null)
const
availableMonths
=
ref
([])
const
exportHistoryLoading
=
ref
(
false
)
const
manualSaveLoading
=
ref
(
false
)
...
...
@@ -2292,6 +2303,60 @@ const loadLastResetTime = async () => {
*/
/**
* 手动保存当前月份数据
*/
const
handleManualSave
=
async
()
=>
{
try
{
manualSaveLoading
.
value
=
true
const
result
=
await
dataStore
.
saveCurrentMonthStatsManually
()
if
(
result
.
success
)
{
ElMessage
.
success
(
result
.
message
)
// 刷新可用月份列表
await
loadAvailableMonths
()
// 注意:不立即加载刚保存的数据,避免时序问题
// 用户可以手动选择月份来查看数据
}
else
if
(
result
.
isOverwrite
)
{
// 需要用户确认是否覆盖
try
{
await
ElMessageBox
.
confirm
(
result
.
message
,
'数据已存在'
,
{
type
:
'warning'
,
confirmButtonText
:
'覆盖保存'
,
cancelButtonText
:
'取消'
}
)
// 用户确认覆盖,重新保存
const
overwriteResult
=
await
dataStore
.
saveCurrentMonthStatsManually
(
true
)
if
(
overwriteResult
.
success
)
{
ElMessage
.
success
(
overwriteResult
.
message
)
// 刷新可用月份列表
await
loadAvailableMonths
()
// 注意:不立即加载刚保存的数据,避免时序问题
// 用户可以手动选择月份来查看数据
}
else
{
ElMessage
.
error
(
overwriteResult
.
message
)
}
}
catch
{
// 用户取消覆盖
ElMessage
.
info
(
'已取消保存'
)
}
}
else
{
ElMessage
.
error
(
result
.
message
)
}
}
catch
(
error
)
{
console
.
error
(
'手动保存失败:'
,
error
)
ElMessage
.
error
(
'保存失败,请重试'
)
}
finally
{
manualSaveLoading
.
value
=
false
}
}
/**
* 加载可用的历史月份
*/
const
loadAvailableMonths
=
async
()
=>
{
...
...
@@ -2682,7 +2747,7 @@ const iconComponents = {
z-index
:
10
;
}
.header-actions
.el-button
{
.header-actions
.el-button
:not
(
.el-button--success
)
{
background
:
linear-gradient
(
135deg
,
#667eea
0%
,
#764ba2
100%
);
border
:
1px
solid
#667eea
;
color
:
white
;
...
...
@@ -2693,13 +2758,36 @@ const iconComponents = {
box-shadow
:
0
2px
8px
rgba
(
102
,
126
,
234
,
0.3
);
}
.header-actions
.el-button
:hover
{
.header-actions
.el-button
:
not
(
.el-button--success
)
:
hover
{
background
:
linear-gradient
(
135deg
,
#5a6fd8
0%
,
#6a4190
100%
);
border-color
:
#5a6fd8
;
transform
:
translateY
(
-2px
);
box-shadow
:
0
4px
15px
rgba
(
102
,
126
,
234
,
0.4
);
}
/* 确保成功类型按钮显示正确的绿色 - 使用更高优先级 */
.header-actions
.el-button.el-button--success
{
background
:
#67c23a
!important
;
background-color
:
#67c23a
!important
;
background-image
:
none
!important
;
border-color
:
#67c23a
!important
;
color
:
white
!important
;
font-weight
:
500
;
padding
:
10px
20px
;
border-radius
:
8px
;
transition
:
all
0.3s
ease
;
box-shadow
:
0
2px
8px
rgba
(
103
,
194
,
58
,
0.3
)
!important
;
}
.header-actions
.el-button.el-button--success
:hover
{
background
:
#5daf34
!important
;
background-color
:
#5daf34
!important
;
background-image
:
none
!important
;
border-color
:
#5daf34
!important
;
transform
:
translateY
(
-2px
);
box-shadow
:
0
4px
15px
rgba
(
103
,
194
,
58
,
0.4
)
!important
;
}
.stats-section
{
margin-bottom
:
20px
;
}
...
...
test_manual_save.html
0 → 100644
View file @
559a85c1
<!DOCTYPE html>
<html
lang=
"zh-CN"
>
<head>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
手动保存功能测试
</title>
<style>
body
{
font-family
:
Arial
,
sans-serif
;
max-width
:
800px
;
margin
:
0
auto
;
padding
:
20px
;
background-color
:
#f5f5f5
;
}
.container
{
background
:
white
;
padding
:
30px
;
border-radius
:
8px
;
box-shadow
:
0
2px
10px
rgba
(
0
,
0
,
0
,
0.1
);
}
h1
{
color
:
#333
;
text-align
:
center
;
margin-bottom
:
30px
;
}
.test-section
{
margin-bottom
:
30px
;
padding
:
20px
;
border
:
1px
solid
#ddd
;
border-radius
:
5px
;
background-color
:
#fafafa
;
}
.test-section
h2
{
color
:
#555
;
margin-top
:
0
;
}
.button
{
background-color
:
#409EFF
;
color
:
white
;
padding
:
10px
20px
;
border
:
none
;
border-radius
:
4px
;
cursor
:
pointer
;
margin
:
5px
;
font-size
:
14px
;
}
.button
:hover
{
background-color
:
#337ecc
;
}
.button
:disabled
{
background-color
:
#ccc
;
cursor
:
not-allowed
;
}
.result
{
margin-top
:
15px
;
padding
:
10px
;
border-radius
:
4px
;
font-family
:
monospace
;
white-space
:
pre-wrap
;
}
.success
{
background-color
:
#f0f9ff
;
border
:
1px
solid
#67c23a
;
color
:
#67c23a
;
}
.error
{
background-color
:
#fef0f0
;
border
:
1px
solid
#f56c6c
;
color
:
#f56c6c
;
}
.info
{
background-color
:
#f4f4f5
;
border
:
1px
solid
#909399
;
color
:
#606266
;
}
.loading
{
background-color
:
#fff7e6
;
border
:
1px
solid
#e6a23c
;
color
:
#e6a23c
;
}
</style>
</head>
<body>
<div
class=
"container"
>
<h1>
🧪 手动保存功能测试
</h1>
<div
class=
"test-section"
>
<h2>
📋 测试说明
</h2>
<p>
这个页面用于测试绩效计分系统的手动保存功能。请按照以下步骤进行测试:
</p>
<ol>
<li>
确保后端服务正在运行(http://localhost:8000)
</li>
<li>
确保前端服务正在运行(http://localhost:4001)
</li>
<li>
点击下面的测试按钮来验证各个功能
</li>
</ol>
</div>
<div
class=
"test-section"
>
<h2>
🔍 1. 检查当前月份数据是否存在
</h2>
<button
class=
"button"
onclick=
"testCheckMonthExists()"
>
检查 2025-09 月份数据
</button>
<div
id=
"checkResult"
class=
"result info"
>
点击按钮开始测试...
</div>
</div>
<div
class=
"test-section"
>
<h2>
💾 2. 测试手动保存功能
</h2>
<button
class=
"button"
onclick=
"testManualSave()"
>
手动保存当前数据
</button>
<div
id=
"saveResult"
class=
"result info"
>
点击按钮开始测试...
</div>
</div>
<div
class=
"test-section"
>
<h2>
📊 3. 验证保存的数据
</h2>
<button
class=
"button"
onclick=
"testGetSavedData()"
>
获取已保存的数据
</button>
<div
id=
"dataResult"
class=
"result info"
>
点击按钮开始测试...
</div>
</div>
</div>
<script>
const
API_BASE_URL
=
'http://localhost:8000'
;
// 获取当前月份
function
getCurrentMonth
()
{
const
now
=
new
Date
();
return
`
${
now
.
getFullYear
()}
-
${
String
(
now
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)}
`
;
}
// 显示结果
function
showResult
(
elementId
,
message
,
type
=
'info'
)
{
const
element
=
document
.
getElementById
(
elementId
);
element
.
textContent
=
message
;
element
.
className
=
`result
${
type
}
`
;
}
// 显示加载状态
function
showLoading
(
elementId
,
message
=
'正在处理...'
)
{
showResult
(
elementId
,
message
,
'loading'
);
}
// 测试检查月份数据是否存在
async
function
testCheckMonthExists
()
{
const
month
=
getCurrentMonth
();
showLoading
(
'checkResult'
,
`正在检查
${
month
}
月份数据...`
);
try
{
const
response
=
await
fetch
(
`
${
API_BASE_URL
}
/api/history/
${
month
}
`
,
{
method
:
'GET'
,
headers
:
{
'Content-Type'
:
'application/json'
,
}
});
if
(
response
.
ok
)
{
const
data
=
await
response
.
json
();
showResult
(
'checkResult'
,
`✅
${
month
}
月份数据存在\n响应:
${
JSON
.
stringify
(
data
,
null
,
2
)}
`
,
'success'
);
}
else
if
(
response
.
status
===
404
)
{
showResult
(
'checkResult'
,
`ℹ️
${
month
}
月份数据不存在(这是正常的,可以进行保存)`
,
'info'
);
}
else
{
const
errorData
=
await
response
.
json
();
showResult
(
'checkResult'
,
`❌ 检查失败:
${
errorData
.
detail
||
response
.
statusText
}
`
,
'error'
);
}
}
catch
(
error
)
{
showResult
(
'checkResult'
,
`❌ 网络错误:
${
error
.
message
}
`
,
'error'
);
}
}
// 测试手动保存功能
async
function
testManualSave
()
{
const
month
=
getCurrentMonth
();
showLoading
(
'saveResult'
,
`正在保存
${
month
}
月份数据...`
);
// 模拟保存数据
const
historyData
=
{
month
:
month
,
save_time
:
new
Date
().
toISOString
(),
total_users
:
2
,
total_institutions
:
4
,
total_images
:
6
,
user_stats
:
[
{
userId
:
'test-user-1'
,
userName
:
'测试用户1'
,
institutionCount
:
2
,
interactionScore
:
1.5
,
performanceScore
:
7.5
,
institutions
:
[]
},
{
userId
:
'test-user-2'
,
userName
:
'测试用户2'
,
institutionCount
:
2
,
interactionScore
:
2.0
,
performanceScore
:
10.0
,
institutions
:
[]
}
],
institutions_data
:
[]
};
try
{
const
response
=
await
fetch
(
`
${
API_BASE_URL
}
/api/history`
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
// 注意:实际使用时需要添加认证头
// 'Authorization': 'Bearer your-token-here'
},
body
:
JSON
.
stringify
(
historyData
)
});
if
(
response
.
ok
)
{
const
data
=
await
response
.
json
();
showResult
(
'saveResult'
,
`✅ 保存成功!\n响应:
${
JSON
.
stringify
(
data
,
null
,
2
)}
`
,
'success'
);
}
else
{
const
errorData
=
await
response
.
json
();
showResult
(
'saveResult'
,
`❌ 保存失败:
${
errorData
.
detail
||
response
.
statusText
}
`
,
'error'
);
}
}
catch
(
error
)
{
showResult
(
'saveResult'
,
`❌ 网络错误:
${
error
.
message
}
`
,
'error'
);
}
}
// 测试获取保存的数据
async
function
testGetSavedData
()
{
const
month
=
getCurrentMonth
();
showLoading
(
'dataResult'
,
`正在获取
${
month
}
月份数据...`
);
try
{
const
response
=
await
fetch
(
`
${
API_BASE_URL
}
/api/history/
${
month
}
`
,
{
method
:
'GET'
,
headers
:
{
'Content-Type'
:
'application/json'
,
}
});
if
(
response
.
ok
)
{
const
data
=
await
response
.
json
();
showResult
(
'dataResult'
,
`✅ 数据获取成功!\n
${
JSON
.
stringify
(
data
,
null
,
2
)}
`
,
'success'
);
}
else
if
(
response
.
status
===
404
)
{
showResult
(
'dataResult'
,
`ℹ️
${
month
}
月份数据不存在,请先保存数据`
,
'info'
);
}
else
{
const
errorData
=
await
response
.
json
();
showResult
(
'dataResult'
,
`❌ 获取失败:
${
errorData
.
detail
||
response
.
statusText
}
`
,
'error'
);
}
}
catch
(
error
)
{
showResult
(
'dataResult'
,
`❌ 网络错误:
${
error
.
message
}
`
,
'error'
);
}
}
// 页面加载完成后显示当前月份
document
.
addEventListener
(
'DOMContentLoaded'
,
function
()
{
const
month
=
getCurrentMonth
();
console
.
log
(
`当前测试月份:
${
month
}
`
);
});
</script>
</body>
</html>
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