Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
CM2305 Group 17 Project
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Ewan Crowle
CM2305 Group 17 Project
Commits
7b896e3a
Commit
7b896e3a
authored
3 months ago
by
Yeying
Browse files
Options
Downloads
Patches
Plain Diff
Refactored code for a complete and functional web interface
parent
e7118186
No related branches found
No related tags found
1 merge request
!1
Merge Lei's Flask Website into main Branch
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
flask-test/static/css/style.css
+30
-10
30 additions, 10 deletions
flask-test/static/css/style.css
flask-test/static/script.js
+74
-246
74 additions, 246 deletions
flask-test/static/script.js
flask-test/templates/index.html
+1
-4
1 addition, 4 deletions
flask-test/templates/index.html
with
105 additions
and
260 deletions
flask-test/static/css/style.css
+
30
−
10
View file @
7b896e3a
...
...
@@ -50,7 +50,7 @@ body {
box-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.1
);
}
/*
头部区域样式
*/
/*
Header section styles
*/
.header-section
{
display
:
flex
;
justify-content
:
space-between
;
...
...
@@ -65,7 +65,7 @@ body {
font-weight
:
bold
;
}
/*
刷新控制样式
*/
/*
Refresh control styles
*/
.refresh-controls
{
display
:
flex
;
align-items
:
center
;
...
...
@@ -91,7 +91,16 @@ body {
font-size
:
0.9rem
;
}
/* 统计摘要样式 */
/* Filter and Statistics Section */
.filter-section
{
margin-bottom
:
2rem
;
}
.date-filter
{
margin-bottom
:
2.5rem
;
/* Increase space between filter and stats */
}
/* Statistics summary styles */
.stats-summary
{
display
:
grid
;
grid-template-columns
:
repeat
(
auto-fit
,
minmax
(
200px
,
1
fr
));
...
...
@@ -101,10 +110,16 @@ body {
.stat-box
{
background-color
:
white
;
padding
:
1rem
;
padding
:
1
.5
rem
;
border-radius
:
8px
;
box-shadow
:
0
2
px
4
px
rgba
(
0
,
0
,
0
,
0.
05
);
box-shadow
:
0
4
px
12
px
rgba
(
0
,
0
,
0
,
0.
1
);
text-align
:
center
;
transition
:
transform
0.2s
ease
;
}
.stat-box
:hover
{
transform
:
translateY
(
-2px
);
box-shadow
:
0
6px
15px
rgba
(
0
,
0
,
0
,
0.15
);
}
.stat-box
h3
{
...
...
@@ -120,7 +135,7 @@ body {
margin
:
0
;
}
/*
图表区域样式
*/
/*
Chart area styles
*/
.charts-container
{
margin-top
:
20px
;
width
:
100%
;
...
...
@@ -133,11 +148,16 @@ body {
.chart-container
{
background
:
white
;
border-radius
:
8
px
;
padding
:
2
0
px
;
box-shadow
:
0
2
px
4
px
rgba
(
0
,
0
,
0
,
0.1
);
border-radius
:
12
px
;
padding
:
2
5
px
;
box-shadow
:
0
4
px
15
px
rgba
(
0
,
0
,
0
,
0.1
);
width
:
100%
;
height
:
400px
;
transition
:
box-shadow
0.3s
ease
;
}
.chart-container
:hover
{
box-shadow
:
0
6px
20px
rgba
(
0
,
0
,
0
,
0.15
);
}
.chart-container
h3
{
...
...
@@ -151,7 +171,7 @@ body {
height
:
300px
!important
;
}
/*
详细记录列表样式
*/
/*
Detailed records list styles
*/
.returns-list-container
{
margin-top
:
2rem
;
}
...
...
This diff is collapsed.
Click to expand it.
flask-test/static/script.js
+
74
−
246
View file @
7b896e3a
// 页面加载完成后执行
document
.
addEventListener
(
'
DOMContentLoaded
'
,
function
()
{
// 获取所有产品图片
// Global variables
let
productChart
,
hourlyChart
;
let
currentFilter
=
'
year
'
;
// Execute when page is loaded
document
.
addEventListener
(
'
DOMContentLoaded
'
,
async
function
()
{
await
initCharts
();
await
loadDefaultData
();
setupEventListeners
();
});
// Initialize all event listeners
function
setupEventListeners
()
{
// Add refresh button event listener
document
.
getElementById
(
'
refreshBtn
'
).
addEventListener
(
'
click
'
,
function
()
{
loadDefaultData
();
});
// Add date filter change listener
document
.
getElementById
(
'
dateFilter
'
).
addEventListener
(
'
change
'
,
function
(
e
)
{
currentFilter
=
e
.
target
.
value
;
filterByDate
(
currentFilter
);
});
// Initialize product images error handling
const
productImages
=
document
.
querySelectorAll
(
'
.product-image
'
);
// 为每个图片添加错误处理
productImages
.
forEach
(
img
=>
{
img
.
onerror
=
function
()
{
this
.
src
=
'
/static/products/default.jpg
'
;
};
});
}
initCharts
();
filterByDate
(
'
today
'
);
// 默认显示今日数据
// 添加刷新按钮事件监听
document
.
getElementById
(
'
refreshBtn
'
).
addEventListener
(
'
click
'
,
function
()
{
refreshData
();
// 重置倒计时
countdown
=
30
*
60
;
});
});
// 设置倒计时刷新
let
countdown
=
30
*
60
;
// 30分钟
let
currentFilter
=
'
today
'
;
// 保存当前的筛选条件
// Load default year data
async
function
loadDefaultData
()
{
const
dateFilter
=
document
.
getElementById
(
'
dateFilter
'
);
dateFilter
.
value
=
'
year
'
;
await
filterByDate
(
'
year
'
);
}
function
updateCountdown
()
{
const
minutes
=
Math
.
floor
(
countdown
/
60
);
const
seconds
=
countdown
%
60
;
const
timeString
=
`
${
minutes
.
toString
().
padStart
(
2
,
'
0
'
)}
:
${
seconds
.
toString
().
padStart
(
2
,
'
0
'
)}
`
;
const
countdownElements
=
document
.
querySelectorAll
(
'
#countdown
'
);
countdownElements
.
forEach
(
element
=>
{
element
.
textContent
=
timeString
;
});
countdown
--
;
// Date filtering functions
async
function
filterByDate
(
value
)
{
const
today
=
new
Date
();
let
startDate
,
endDate
;
if
(
countdown
<
0
)
{
countdown
=
30
*
60
;
// 重置为30分钟
refreshData
();
// 刷新数据
switch
(
value
)
{
case
'
year
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
0
,
1
);
endDate
=
new
Date
(
today
.
getFullYear
(),
11
,
31
,
23
,
59
,
59
);
break
;
case
'
today
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
today
.
getDate
());
endDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
today
.
getDate
(),
23
,
59
,
59
);
break
;
case
'
yesterday
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
today
.
getDate
()
-
1
);
endDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
today
.
getDate
()
-
1
,
23
,
59
,
59
);
break
;
case
'
week
'
:
startDate
=
new
Date
(
today
.
setDate
(
today
.
getDate
()
-
today
.
getDay
()));
endDate
=
new
Date
(
today
.
setDate
(
today
.
getDate
()
+
6
));
endDate
.
setHours
(
23
,
59
,
59
);
break
;
case
'
month
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
1
);
endDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
()
+
1
,
0
,
23
,
59
,
59
);
break
;
}
}
// 每秒更新倒计时
setInterval
(
updateCountdown
,
1000
);
let
productChart
,
hourlyChart
;
await
fetchData
(
startDate
,
endDate
);
}
function
initCharts
()
{
async
function
initCharts
()
{
const
productCtx
=
document
.
getElementById
(
'
productChart
'
).
getContext
(
'
2d
'
);
const
hourlyCtx
=
document
.
getElementById
(
'
hourlyChart
'
).
getContext
(
'
2d
'
);
...
...
@@ -105,14 +127,14 @@ function initCharts() {
});
}
//
更新统计数据
//
Update stats data
function
updateStats
(
data
)
{
document
.
getElementById
(
'
totalReturns
'
).
textContent
=
data
.
total_returns
;
document
.
getElementById
(
'
totalPoints
'
).
textContent
=
data
.
total_points
;
document
.
getElementById
(
'
activeUsers
'
).
textContent
=
data
.
active_users
;
}
//
更新图表数据
//
Update chart data
function
updateCharts
(
data
)
{
// Update product chart
const
productLabels
=
Object
.
keys
(
data
.
product_stats
);
...
...
@@ -123,10 +145,10 @@ function updateCharts(data) {
productChart
.
update
();
// Update weekly trend chart
const
weeklyData
=
[
0
,
0
,
0
,
0
,
0
,
0
,
0
];
// Initialize array for 7 days
const
weeklyData
=
[
0
,
0
,
0
,
0
,
0
,
0
,
0
];
data
.
returns
.
forEach
(
return_item
=>
{
const
date
=
new
Date
(
return_item
.
return_date
);
const
dayOfWeek
=
date
.
getDay
();
// 0 = Sunday, 1 = Monday, etc.
const
dayOfWeek
=
date
.
getDay
();
weeklyData
[
dayOfWeek
]
++
;
});
...
...
@@ -134,119 +156,16 @@ function updateCharts(data) {
hourlyChart
.
update
();
}
// Date filtering functions
function
filterByDate
(
value
)
{
const
today
=
new
Date
();
let
startDate
,
endDate
;
switch
(
value
)
{
case
'
today
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
today
.
getDate
());
endDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
today
.
getDate
(),
23
,
59
,
59
);
break
;
case
'
yesterday
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
today
.
getDate
()
-
1
);
endDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
today
.
getDate
()
-
1
,
23
,
59
,
59
);
break
;
case
'
week
'
:
const
weekStart
=
new
Date
(
today
);
weekStart
.
setDate
(
today
.
getDate
()
-
today
.
getDay
());
startDate
=
new
Date
(
weekStart
.
getFullYear
(),
weekStart
.
getMonth
(),
weekStart
.
getDate
());
endDate
=
new
Date
(
weekStart
.
getFullYear
(),
weekStart
.
getMonth
(),
weekStart
.
getDate
()
+
6
,
23
,
59
,
59
);
break
;
case
'
month
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
1
);
endDate
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
()
+
1
,
0
,
23
,
59
,
59
);
break
;
case
'
quarter1
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
0
,
1
);
endDate
=
new
Date
(
today
.
getFullYear
(),
2
,
31
,
23
,
59
,
59
);
break
;
case
'
quarter2
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
3
,
1
);
endDate
=
new
Date
(
today
.
getFullYear
(),
5
,
30
,
23
,
59
,
59
);
break
;
case
'
quarter3
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
6
,
1
);
endDate
=
new
Date
(
today
.
getFullYear
(),
8
,
30
,
23
,
59
,
59
);
break
;
case
'
quarter4
'
:
startDate
=
new
Date
(
today
.
getFullYear
(),
9
,
1
);
endDate
=
new
Date
(
today
.
getFullYear
(),
11
,
31
,
23
,
59
,
59
);
break
;
async
function
fetchData
(
startDate
,
endDate
)
{
try
{
const
response
=
await
fetch
(
`/returns?start_date=
${
startDate
.
toISOString
()}
&end_date=
${
endDate
.
toISOString
()}
`
);
const
data
=
await
response
.
json
();
updateStats
(
data
);
updateCharts
(
data
);
updateTable
(
data
.
returns
);
}
catch
(
error
)
{
console
.
error
(
'
Error fetching data:
'
,
error
);
}
fetchData
(
startDate
,
endDate
);
}
function
filterByCustomRange
()
{
const
startDate
=
new
Date
(
document
.
getElementById
(
'
startDate
'
).
value
);
const
endDate
=
new
Date
(
document
.
getElementById
(
'
endDate
'
).
value
);
fetchData
(
startDate
,
endDate
);
}
function
fetchData
(
startDate
,
endDate
)
{
fetch
(
`/returns?start_date=
${
startDate
.
toISOString
()}
&end_date=
${
endDate
.
toISOString
()}
`
)
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
updateStats
(
data
);
updateCharts
(
data
);
updateTable
(
data
.
returns
);
})
.
catch
(
error
=>
console
.
error
(
'
Error:
'
,
error
));
}
// Date picker modal functions
function
showDatePickerModal
()
{
const
modal
=
document
.
getElementById
(
'
datePickerModal
'
);
modal
.
style
.
display
=
'
block
'
;
// Set default dates
const
today
=
new
Date
();
const
lastMonth
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
()
-
1
,
today
.
getDate
());
document
.
getElementById
(
'
modalStartDate
'
).
value
=
lastMonth
.
toISOString
().
split
(
'
T
'
)[
0
];
document
.
getElementById
(
'
modalEndDate
'
).
value
=
today
.
toISOString
().
split
(
'
T
'
)[
0
];
}
function
closeDatePickerModal
()
{
const
modal
=
document
.
getElementById
(
'
datePickerModal
'
);
modal
.
style
.
display
=
'
none
'
;
}
function
selectPresetRange
(
preset
)
{
const
today
=
new
Date
();
let
startDate
=
new
Date
();
switch
(
preset
)
{
case
'
last7days
'
:
startDate
.
setDate
(
today
.
getDate
()
-
7
);
break
;
case
'
last30days
'
:
startDate
.
setDate
(
today
.
getDate
()
-
30
);
break
;
case
'
last90days
'
:
startDate
.
setDate
(
today
.
getDate
()
-
90
);
break
;
case
'
lastYear
'
:
startDate
.
setFullYear
(
today
.
getFullYear
()
-
1
);
break
;
}
document
.
getElementById
(
'
modalStartDate
'
).
value
=
startDate
.
toISOString
().
split
(
'
T
'
)[
0
];
document
.
getElementById
(
'
modalEndDate
'
).
value
=
today
.
toISOString
().
split
(
'
T
'
)[
0
];
}
function
applyDateRange
()
{
const
startDate
=
new
Date
(
document
.
getElementById
(
'
modalStartDate
'
).
value
);
const
endDate
=
new
Date
(
document
.
getElementById
(
'
modalEndDate
'
).
value
);
// Set time to start and end of day
startDate
.
setHours
(
0
,
0
,
0
,
0
);
endDate
.
setHours
(
23
,
59
,
59
,
999
);
fetchData
(
startDate
,
endDate
);
closeDatePickerModal
();
}
// Update table with data
...
...
@@ -273,98 +192,7 @@ function updateTable(returns) {
<td>
${
return_item
.
product_id
}
</td>
<td>
${
return_item
.
reward_value
}
</td>
<td>
${
return_item
.
return_date
}
</td>
<td>
<button class="action-btn view-btn" onclick="toggleDetails(this)">View</button>
<button class="action-btn details-btn" onclick="showDetailsModal(this)" style="display: none;">Details</button>
</td>
`
;
tbody
.
appendChild
(
row
);
const
detailsRow
=
document
.
createElement
(
'
tr
'
);
detailsRow
.
className
=
'
details-row
'
;
detailsRow
.
style
.
display
=
'
none
'
;
detailsRow
.
innerHTML
=
`
<td colspan="6">
<div class="details-content">
<p><strong>ID:</strong>
${
return_item
.
id
}
</p>
<p><strong>Customer ID:</strong>
${
return_item
.
customer_id
}
</p>
<p><strong>Product ID:</strong>
${
return_item
.
product_id
}
</p>
<p><strong>Reward Value:</strong>
${
return_item
.
reward_value
}
</p>
<p><strong>Return Date:</strong>
${
return_item
.
return_date
}
</p>
</div>
</td>
`
;
tbody
.
appendChild
(
detailsRow
);
});
}
// 刷新数据
function
refreshData
()
{
// 根据当前的筛选条件重新获取数据
switch
(
currentFilter
)
{
case
'
today
'
:
filterByDate
(
'
today
'
);
break
;
case
'
week
'
:
filterByDate
(
'
week
'
);
break
;
case
'
month
'
:
filterByDate
(
'
month
'
);
break
;
case
'
custom
'
:
const
customDate
=
document
.
getElementById
(
'
customDate
'
).
value
;
if
(
customDate
)
{
filterByDate
(
customDate
);
}
break
;
}
}
// Table interaction functions
function
toggleDetails
(
button
)
{
const
row
=
button
.
closest
(
'
tr
'
);
const
detailsRow
=
row
.
nextElementSibling
;
const
detailsBtn
=
row
.
querySelector
(
'
.details-btn
'
);
if
(
detailsRow
.
style
.
display
===
'
none
'
)
{
detailsRow
.
style
.
display
=
'
table-row
'
;
detailsBtn
.
style
.
display
=
'
inline-block
'
;
button
.
textContent
=
'
Hide
'
;
}
else
{
detailsRow
.
style
.
display
=
'
none
'
;
detailsBtn
.
style
.
display
=
'
none
'
;
button
.
textContent
=
'
View
'
;
}
}
// Modal functions
const
modal
=
document
.
getElementById
(
'
detailsModal
'
);
const
closeBtn
=
document
.
querySelector
(
'
.close
'
);
function
showDetailsModal
(
button
)
{
const
row
=
button
.
closest
(
'
tr
'
);
const
detailsContent
=
row
.
nextElementSibling
.
querySelector
(
'
.details-content
'
).
innerHTML
;
document
.
getElementById
(
'
modalContent
'
).
innerHTML
=
detailsContent
;
modal
.
style
.
display
=
'
block
'
;
}
// Close modal when clicking the close button
closeBtn
.
onclick
=
function
()
{
modal
.
style
.
display
=
'
none
'
;
}
// Close modal when clicking outside
window
.
onclick
=
function
(
event
)
{
if
(
event
.
target
==
modal
)
{
modal
.
style
.
display
=
'
none
'
;
}
}
// Add double-click handler for rows
document
.
querySelectorAll
(
'
.returns-table tbody tr
'
).
forEach
(
row
=>
{
row
.
addEventListener
(
'
dblclick
'
,
function
()
{
const
detailsContent
=
this
.
nextElementSibling
.
querySelector
(
'
.details-content
'
).
innerHTML
;
document
.
getElementById
(
'
modalContent
'
).
innerHTML
=
detailsContent
;
modal
.
style
.
display
=
'
block
'
;
});
});
This diff is collapsed.
Click to expand it.
flask-test/templates/index.html
+
1
−
4
View file @
7b896e3a
...
...
@@ -31,14 +31,11 @@
<div
class=
"filter-group"
>
<label>
Date Range:
</label>
<select
id=
"dateFilter"
onchange=
"filterByDate(this.value)"
>
<option
value=
"year"
>
This Year
</option>
<option
value=
"today"
>
Current Day
</option>
<option
value=
"yesterday"
>
Yesterday
</option>
<option
value=
"week"
>
This Week
</option>
<option
value=
"month"
>
This Month
</option>
<option
value=
"quarter1"
>
Q1 (Jan-Mar)
</option>
<option
value=
"quarter2"
>
Q2 (Apr-Jun)
</option>
<option
value=
"quarter3"
>
Q3 (Jul-Sep)
</option>
<option
value=
"quarter4"
>
Q4 (Oct-Dec)
</option>
</select>
</div>
<div
class=
"filter-group"
>
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment