@@ -1,889 +0,0 @@
<!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 >
* { box-sizing : border-box ; margin : 0 ; padding : 0 ; }
body { font-family : - apple-system , BlinkMacSystemFont , 'PingFang SC' , 'Microsoft YaHei' , sans-serif ; background : #F5F7FA ; color : #333 ; min-width : 1280 px ; }
: root {
--primary : #1890FF ; --primary-light : #E6F7FF ; --success : #52C41A ; --warning : #FAAD14 ; --danger : #FF4D4F ;
--text-primary : #333 ; --text-secondary : #666 ; --text-placeholder : #999 ; --border : #E8E8E8 ; --background : #F5F7FA ;
--font-xs : 11 px ; --font-sm : 13 px ; --font-base : 15 px ; --font-lg : 17 px ;
--radius-sm : 4 px ; --radius-md : 8 px ; --radius-lg : 12 px ;
--shadow-sm : 0 1 px 4 px rgba ( 0 , 0 , 0 , 0.08 ) ; --shadow-md : 0 4 px 12 px rgba ( 0 , 0 , 0 , 0.10 ) ;
}
/* 顶部导航栏 */
. top-bar { display : flex ; align-items : center ; height : 56 px ; background : #001529 ; padding : 0 24 px ; position : fixed ; top : 0 ; left : 0 ; right : 0 ; z-index : 100 ; }
. top-bar__logo { display : flex ; align-items : center ; gap : 8 px ; color : #fff ; font-size : var ( - - font - lg ) ; font-weight : 600 ; white-space : nowrap ; width : 240 px ; flex-shrink : 0 ; }
. top-bar__logo svg { width : 28 px ; height : 28 px ; }
. top-bar__nav { display : flex ; align-items : center ; gap : 4 px ; flex : 1 ; }
. top-bar__nav a { color : rgba ( 255 , 255 , 255 , 0.65 ) ; text-decoration : none ; padding : 6 px 16 px ; border-radius : var ( - - radius - sm ) ; font-size : var ( - - font - sm ) ; transition : all .2 s ; }
. top-bar__nav a : hover { color : #fff ; background : rgba ( 255 , 255 , 255 , 0.08 ) ; }
. top-bar__nav a . active { color : #fff ; background : var ( - - primary ) ; }
. top-bar__right { display : flex ; align-items : center ; gap : 16 px ; color : rgba ( 255 , 255 , 255 , 0.65 ) ; font-size : var ( - - font - sm ) ; }
. btn-back { color : rgba ( 255 , 255 , 255 , 0.65 ) ; cursor : pointer ; display : flex ; align-items : center ; gap : 4 px ; text-decoration : none ; }
. btn-back : hover { color : #fff ; }
. avatar { width : 32 px ; height : 32 px ; border-radius : 50 % ; background : #1890FF ; display : flex ; align-items : center ; justify-content : center ; color : #fff ; font-size : var ( - - font - sm ) ; cursor : pointer ; }
/* 侧边栏 */
. sidebar { position : fixed ; top : 56 px ; left : 0 ; bottom : 0 ; width : 240 px ; background : #001529 ; overflow-y : auto ; z-index : 90 ; padding-top : 8 px ; }
. sidebar-menu__title { color : rgba ( 255 , 255 , 255 , 0.35 ) ; font-size : var ( - - font - xs ) ; padding : 16 px 24 px 8 px ; letter-spacing : .5 px ; }
. sidebar-item { display : flex ; align-items : center ; gap : 8 px ; padding : 10 px 24 px ; color : rgba ( 255 , 255 , 255 , 0.65 ) ; font-size : var ( - - font - sm ) ; cursor : pointer ; transition : all .15 s ; text-decoration : none ; }
. sidebar-item : hover { color : #fff ; background : rgba ( 255 , 255 , 255 , 0.06 ) ; }
. sidebar-item . active { color : #fff ; background : var ( - - primary ) ; }
/* 主内容区 */
. main { margin-left : 240 px ; margin-top : 56 px ; padding : 24 px ; min-height : calc ( 100 vh - 56 px ) ; }
/* 页面标题 */
. page-header { margin-bottom : 16 px ; }
. page-header__breadcrumb { font-size : var ( - - font - sm ) ; color : var ( - - text - placeholder ) ; }
. page-header__breadcrumb span { color : var ( - - text - primary ) ; }
/* 搜索下拉 */
. search-select { position : relative ; width : 100 % ; }
. search-select__input { width : 100 % ; height : 40 px ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius - sm ) ; padding : 0 36 px 0 12 px ; font-size : var ( - - font - sm ) ; outline : none ; transition : border-color .2 s ; box-sizing : border-box ; background : #fff url ( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='7'%3E%3Cpath d='M0 0l6 7 6-7z' fill='%23999'/%3E%3C/svg%3E" ) no-repeat right 12 px center ; }
. search-select__input : focus { border-color : var ( - - primary ) ; }
. search-select__input :: placeholder { color : var ( - - text - placeholder ) ; }
. search-select__panel { position : absolute ; top : 100 % ; left : 0 ; right : 0 ; margin-top : 4 px ; background : #fff ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius - md ) ; box-shadow : var ( - - shadow - md ) ; z-index : 500 ; display : none ; max-height : 280 px ; overflow-y : auto ; }
. search-select__panel . show { display : block ; }
. search-select__option { padding : 12 px 16 px ; font-size : var ( - - font - sm ) ; cursor : pointer ; transition : background .1 s ; border-bottom : 1 px solid #F5F5F5 ; }
. search-select__option : last-child { border-bottom : none ; }
. search-select__option : hover { background : #F5F7FA ; }
. search-select__option . selected { color : var ( - - primary ) ; background : var ( - - primary - light ) ; }
. search-select__option . ss-org { font-size : var ( - - font - xs ) ; color : var ( - - text - placeholder ) ; margin-top : 2 px ; }
. search-select__empty { padding : 20 px ; text-align : center ; color : var ( - - text - placeholder ) ; font-size : var ( - - font - sm ) ; }
. search-select__clear { position : absolute ; right : 8 px ; top : 50 % ; transform : translateY ( -50 % ) ; width : 22 px ; height : 22 px ; border : none ; background : none ; cursor : pointer ; color : var ( - - text - placeholder ) ; font-size : 16 px ; display : none ; align-items : center ; justify-content : center ; border-radius : 50 % ; line-height : 1 ; padding : 0 ; z-index : 1 ; }
. search-select__clear : hover { color : var ( - - text - primary ) ; background : #F0F0F0 ; }
. search-select__clear . show { display : flex ; }
. search-select--selected . search-select__input { background-image : none ; padding-right : 32 px ; }
/* 筛选栏 */
. filter-bar { background : #fff ; padding : 16 px 24 px ; display : flex ; align-items : center ; gap : 12 px ; flex-wrap : wrap ; border-radius : var ( - - radius - md ) var ( - - radius - md ) 0 0 ; box-shadow : var ( - - shadow - sm ) ; }
. filter-item { display : flex ; align-items : center ; gap : 8 px ; }
. filter-item label { font-size : var ( - - font - xs ) ; color : var ( - - text - secondary ) ; white-space : nowrap ; }
. filter-input { height : 32 px ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius - sm ) ; padding : 0 10 px ; font-size : var ( - - font - xs ) ; outline : none ; transition : border-color .2 s ; box-sizing : border-box ; }
. filter-input : focus { border-color : var ( - - primary ) ; }
. filter-date { height : 32 px ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius - sm ) ; padding : 0 10 px ; font-size : var ( - - font - xs ) ; outline : none ; transition : border-color .2 s ; box-sizing : border-box ; color : var ( - - text - primary ) ; }
. filter-date : focus { border-color : var ( - - primary ) ; }
. filter-divider { color : var ( - - text - placeholder ) ; font-size : var ( - - font - xs ) ; margin : 0 2 px ; }
/* 单位部门双列面板 */
. org-select-wrap { position : relative ; display : inline-block ; }
. org-trigger { height : 32 px ; min-width : 160 px ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius - sm ) ; padding : 0 28 px 0 10 px ; font-size : var ( - - font - xs ) ; color : var ( - - text - primary ) ; background : #fff url ( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E" ) no-repeat right 8 px center ; cursor : pointer ; display : flex ; align-items : center ; user-select : none ; white-space : nowrap ; }
. org-trigger . open , . org-trigger : hover { border-color : var ( - - primary ) ; }
. org-panel { display : none ; position : absolute ; top : 36 px ; left : 0 ; z-index : 300 ; background : #fff ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius - sm ) ; box-shadow : var ( - - shadow - md ) ; width : 420 px ; }
. org-panel . open { display : flex ; }
. org-col { flex : 1 ; overflow-y : auto ; max-height : 240 px ; border-right : 1 px solid var ( - - border ) ; }
. org-col : last-child { border-right : none ; }
. org-col__title { padding : 8 px 14 px ; font-size : var ( - - font - xs ) ; color : var ( - - text - placeholder ) ; background : #FAFAFA ; border-bottom : 1 px solid var ( - - border ) ; font-weight : 500 ; }
. org-item { padding : 9 px 14 px ; font-size : var ( - - font - sm ) ; color : var ( - - text - primary ) ; cursor : pointer ; display : flex ; align-items : center ; justify-content : space-between ; gap : 4 px ; }
. org-item : hover { background : #F0F8FF ; color : var ( - - primary ) ; }
. org-item . active { background : var ( - - primary - light ) ; color : var ( - - primary ) ; font-weight : 500 ; }
/* 按钮 */
. btn { height : 32 px ; padding : 0 16 px ; border-radius : var ( - - radius - sm ) ; font-size : var ( - - font - xs ) ; cursor : pointer ; border : none ; transition : all .2 s ; display : inline-flex ; align-items : center ; gap : 6 px ; white-space : nowrap ; }
. btn-primary { background : var ( - - primary ) ; color : #fff ; } . btn-primary : hover { background : #40a9ff ; }
. btn-default { background : #fff ; color : var ( - - text - primary ) ; border : 1 px solid var ( - - border ) ; } . btn-default : hover { color : var ( - - primary ) ; border-color : var ( - - primary ) ; }
. btn-export { background : #fff ; color : var ( - - text - primary ) ; border : 1 px solid var ( - - border ) ; } . btn-export : hover { color : var ( - - primary ) ; border-color : var ( - - primary ) ; }
/* 操作栏 */
. action-bar { display : flex ; align-items : center ; gap : 8 px ; padding : 16 px 24 px ; background : #fff ; border-top : 1 px solid var ( - - border ) ; }
/* 表格卡片 */
. table-card { background : #fff ; border-radius : 0 0 var ( - - radius - md ) var ( - - radius - md ) ; box-shadow : var ( - - shadow - sm ) ; overflow : hidden ; }
. table-card__content { overflow-x : auto ; -webkit- overflow-scrolling : touch ; }
. table-card__content table { min-width : 1600 px ; white-space : nowrap ; }
table { width : 100 % ; border-collapse : collapse ; }
/* 表头 - 两行结构 */
thead th { background : #FAFAFA ; padding : 8 px 4 px ; text-align : center ; font-size : var ( - - font - xs ) ; color : var ( - - text - secondary ) ; font-weight : 500 ; border-bottom : 1 px solid var ( - - border ) ; white-space : nowrap ; }
thead th . th-parent { background : #F0F2F5 ; font-weight : 600 ; color : var ( - - text - primary ) ; border-bottom : 1 px solid var ( - - border ) ; }
thead th . th-divider { border-right : 2 px solid var ( - - border ) ; }
tbody td { padding : 10 px 4 px ; text-align : center ; font-size : var ( - - font - xs ) ; color : var ( - - text - primary ) ; border-bottom : 1 px solid #F0F0F0 ; white-space : nowrap ; }
tbody tr : last-child td { border-bottom : none ; }
tbody tr : hover { background : #F8FBFF ; }
/* 左侧固定列:干预计划、姓名(序号列不固定) */
thead th : nth-child ( 2 ) , tbody td : nth-child ( 2 ) { position : sticky ; left : 0 ; z-index : 6 ; background : #fff ; width : 200 px ; min-width : 200 px ; max-width : 200 px ; overflow : hidden ; text-overflow : ellipsis ; }
thead th : nth-child ( 2 ) { z-index : 9 ; background : #FAFAFA ; }
thead th : nth-child ( 3 ) , tbody td : nth-child ( 3 ) { position : sticky ; left : 200 px ; z-index : 5 ; background : #fff ; width : 60 px ; min-width : 60 px ; max-width : 60 px ; }
thead th : nth-child ( 3 ) { z-index : 8 ; background : #FAFAFA ; }
thead th : nth-child ( 4 ) , tbody td : nth-child ( 4 ) { position : sticky ; left : 260 px ; z-index : 4 ; background : #fff ; width : 88 px ; min-width : 88 px ; max-width : 88 px ; }
thead th : nth-child ( 4 ) { z-index : 7 ; background : #FAFAFA ; }
tbody tr : hover td : nth-child ( 2 ) ,
tbody tr : hover td : nth-child ( 3 ) ,
tbody tr : hover td : nth-child ( 4 ) { background : #F8FBFF ; }
/* 右侧固定列:体重报告(操作列) */
thead tr : first-child th : last-child , tbody td : last-child { position : sticky ; right : 0 ; z-index : 2 ; background : #fff ; box-shadow : -2 px 0 4 px rgba ( 0 , 0 , 0 , 0.04 ) ; width : 72 px ; min-width : 72 px ; max-width : 72 px ; }
thead tr : first-child th : last-child { z-index : 4 ; background : #FAFAFA ; }
thead tr : nth-child ( 2 ) th : last-child { background : #FAFAFA ; }
tbody tr : hover td : last-child { background : #F8FBFF ; }
/* 饮食、运动、体重子列分隔线 */
. col-diet-last { border-right : 2 px solid var ( - - border ) !important ; }
. col-exercise-last { border-right : 2 px solid var ( - - border ) !important ; }
. col-weight-last { border-right : 2 px solid var ( - - border ) !important ; }
. td-empty { text-align : center ; padding : 48 px 0 ; color : var ( - - text - placeholder ) ; font-size : var ( - - font - sm ) ; }
/* 变化值颜色 */
. change-up { color : var ( - - danger ) ; }
. change-down { color : var ( - - success ) ; }
. change-zero { color : var ( - - text - placeholder ) ; }
/* 分页 */
. pagination { display : flex ; align-items : center ; justify-content : flex-end ; padding : 16 px 24 px ; gap : 8 px ; border-top : 1 px solid var ( - - border ) ; }
. pagination span { font-size : var ( - - font - sm ) ; color : var ( - - text - secondary ) ; }
. page-btn { width : 32 px ; height : 32 px ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius - sm ) ; background : #fff ; cursor : pointer ; font-size : var ( - - font - sm ) ; display : inline-flex ; align-items : center ; justify-content : center ; transition : all .2 s ; }
. page-btn : hover , . page-btn . active { background : var ( - - primary ) ; color : #fff ; border-color : var ( - - primary ) ; }
/* 链接 */
. link { color : var ( - - primary ) ; cursor : pointer ; font-size : var ( - - font - xs ) ; text-decoration : none ; } . link : hover { color : #40a9ff ; }
/* Toast */
. toast { position : fixed ; top : 80 px ; left : 50 % ; transform : translateX ( -50 % ) ; background : #333 ; color : #fff ; padding : 10 px 24 px ; border-radius : var ( - - radius - sm ) ; font-size : var ( - - font - sm ) ; z-index : 9999 ; opacity : 0 ; transition : opacity .2 s ; pointer-events : none ; }
. toast . show { opacity : 1 ; }
/* ===== 体重报告弹窗 ===== */
. report-overlay { display : none ; position : fixed ; inset : 0 ; z-index : 1000 ; background : rgba ( 0 , 0 , 0 , 0.45 ) ; justify-content : center ; align-items : flex-start ; padding-top : 40 px ; }
. report-overlay . show { display : flex ; }
. report-modal { background : #fff ; border-radius : var ( - - radius - lg ) ; width : 720 px ; max-height : calc ( 100 vh - 80 px ) ; display : flex ; flex-direction : column ; overflow : hidden ; box-shadow : 0 8 px 40 px rgba ( 0 , 0 , 0 , 0.18 ) ; }
. report-modal__header { display : flex ; align-items : center ; justify-content : space-between ; padding : 16 px 24 px ; border-bottom : 1 px solid var ( - - border ) ; flex-shrink : 0 ; }
. report-modal__title { font-size : var ( - - font - lg ) ; font-weight : 700 ; color : var ( - - text - primary ) ; }
. report-modal__close { width : 32 px ; height : 32 px ; border : none ; background : none ; cursor : pointer ; display : flex ; align-items : center ; justify-content : center ; border-radius : var ( - - radius - sm ) ; color : var ( - - text - placeholder ) ; font-size : 20 px ; transition : all .15 s ; }
. report-modal__close : hover { background : #F0F0F0 ; color : var ( - - text - primary ) ; }
. report-modal__body { flex : 1 ; overflow-y : auto ; padding : 0 24 px 24 px ; display : flex ; flex-direction : column ; }
. report-modal__body :: -webkit-scrollbar { width : 6 px ; }
. report-modal__body :: -webkit-scrollbar-thumb { background : #D9D9D9 ; border-radius : 3 px ; }
/* 总览卡片(固定在弹窗顶部,不随列表滚动) */
. report-overview { background : linear-gradient ( 135 deg , #36cfc9 0 % , #2ec4b6 100 % ) ; border-radius : var ( - - radius - md ) ; padding : 20 px 24 px ; color : #fff ; position : relative ; overflow : hidden ; flex-shrink : 0 ; margin : 16 px 24 px 0 ; }
. report-overview :: before { content : '' ; position : absolute ; top : -40 px ; right : -40 px ; width : 120 px ; height : 120 px ; border-radius : 50 % ; background : rgba ( 255 , 255 , 255 , 0.08 ) ; pointer-events : none ; }
. report-overview :: after { content : '' ; position : absolute ; bottom : -50 px ; left : -30 px ; width : 140 px ; height : 140 px ; border-radius : 50 % ; background : rgba ( 255 , 255 , 255 , 0.05 ) ; pointer-events : none ; }
. report-overview__title { font-size : 18 px ; font-weight : 800 ; margin-bottom : 12 px ; position : relative ; z-index : 1 ; letter-spacing : 1 px ; }
. report-overview__row { display : flex ; align-items : center ; gap : 32 px ; position : relative ; z-index : 1 ; font-size : 13 px ; opacity : 0.9 ; margin-bottom : 8 px ; }
. report-overview__rank { position : relative ; z-index : 1 ; font-size : 13 px ; opacity : 0.9 ; margin-bottom : 14 px ; line-height : 1.8 ; }
. report-overview__metrics { display : flex ; gap : 16 px ; position : relative ; z-index : 1 ; }
. report-overview__metric { flex : 1 ; background : rgba ( 255 , 255 , 255 , 0.15 ) ; border-radius : 10 px ; padding : 14 px 18 px ; backdrop-filter : blur ( 4 px ) ; }
. report-overview__metric-label { font-size : 12 px ; opacity : 0.8 ; margin-bottom : 6 px ; }
. report-overview__metric-val { font-size : 28 px ; font-weight : 800 ; }
. report-overview__metric-unit { font-size : 13 px ; font-weight : 500 ; opacity : 0.8 ; margin-left : 2 px ; }
/* 记录表格 */
. report-table-wrap { overflow-x : auto ; -webkit- overflow-scrolling : touch ; }
. report-table { width : 100 % ; border-collapse : collapse ; }
. report-table thead { position : sticky ; top : 0 ; z-index : 5 ; }
. report-table thead th { background : #FAFAFA ; padding : 8 px 10 px ; text-align : center ; font-size : var ( - - font - xs ) ; color : var ( - - text - secondary ) ; font-weight : 500 ; border-bottom : 1 px solid var ( - - border ) ; white-space : nowrap ; }
. report-table tbody td { padding : 9 px 10 px ; text-align : center ; font-size : var ( - - font - xs ) ; color : var ( - - text - primary ) ; border-bottom : 1 px solid #F0F0F0 ; white-space : nowrap ; }
. report-table tbody tr : hover { background : #F8FBFF ; }
. report-table tbody tr : last-child td { border-bottom : none ; }
. report-table-empty { text-align : center ; padding : 40 px 20 px ; color : var ( - - text - placeholder ) ; font-size : var ( - - font - sm ) ; }
/* 弹窗底部 */
. report-modal__footer { display : flex ; align-items : center ; justify-content : flex-end ; padding : 12 px 24 px ; border-top : 1 px solid var ( - - border ) ; flex-shrink : 0 ; gap : 8 px ; }
< / style >
< / head >
< body >
<!-- 顶部导航栏 -->
< div class = "top-bar" >
< div class = "top-bar__logo" > < svg viewBox = "0 0 28 28" fill = "none" > < rect width = "28" height = "28" rx = "6" fill = "#1890FF" / > < path d = "M7 14h14M14 7v14" stroke = "#fff" stroke-width = "2.5" stroke-linecap = "round" / > < / svg > 体重管理< / div >
< nav class = "top-bar__nav" >
< a href = "archive-personal-weight-data.html" > 档案< / a >
< a href = "plan-list.html" > 计划管理< / a >
< a href = "registration-tracking.html" > 报名管理< / a >
< a href = "action-record.html" class = "active" > 过程管理< / a >
< a href = "history-plan-list.html" > 历史干预< / a >
< a href = "personal-target-data.html" > 个人体重管理< / a >
< a href = "scale-device.html" > 设备管理< / a >
< a href = "exception-system-dirty.html" > 异常事件< / a >
< / nav >
< div class = "top-bar__right" >
< a class = "btn-back" href = "../../index.html" > < svg width = "16" height = "16" viewBox = "0 0 16 16" fill = "none" > < path d = "M10 3L5 8l5 5" stroke = "currentColor" stroke-width = "1.5" stroke-linecap = "round" / > < / svg > 返回首页< / a >
< div class = "avatar" > 管< / div >
< span style = "color:rgba(255,255,255,0.65);" > 系统管理员 ▾< / span >
< / div >
< / div >
<!-- 侧边栏 -->
< aside class = "sidebar" >
< div class = "sidebar-menu__title" > 过程管理< / div >
< a href = "action-record.html" class = "sidebar-item" > 行动记录< / a >
< a href = "detail-data.html" class = "sidebar-item active" > 详细数据< / a >
< a href = "weight-ranking.html" class = "sidebar-item" > 体重排名< / a >
< a href = "action-overview.html" class = "sidebar-item" > 活动总览< / a >
< a href = "activity-statistics.html" class = "sidebar-item" > 活动统计< / a >
< a href = "recipe-record.html" class = "sidebar-item" > 食堂食谱< / a >
< / aside >
<!-- 主内容 -->
< main class = "main" >
<!-- 页面标题 -->
< div class = "page-header" >
< div class = "page-header__breadcrumb" > 体重管理 / 过程管理 / < span > 详细数据< / span > < / div >
< / div >
<!-- [交互] 筛选栏 — 日期范围(默认当日)、单位部门、姓名、员工编号 -->
< div class = "filter-bar" >
<!-- [交互] 干预计划下拉搜索 -->
< div class = "filter-item" >
< label > 干预计划< / label >
< div class = "search-select" id = "planSelect" style = "width:280px;" >
< input class = "search-select__input" id = "planSearchInput" placeholder = "搜索计划名称或组织机构" oninput = "searchPlan()" onfocus = "showPlanPanel()" autocomplete = "off" >
< button class = "search-select__clear" id = "planClearBtn" onclick = "clearPlan()" title = "清空计划" > × < / button >
< div class = "search-select__panel" id = "planPanel" > < / div >
< / div >
< / div >
<!-- [交互] 日期范围选择,默认当日 -->
< div class = "filter-item" >
< label > 日期范围< / label >
< input class = "filter-date" type = "date" id = "filterDateFrom" onchange = "applyFilter()" >
< span class = "filter-divider" > ~< / span >
< input class = "filter-date" type = "date" id = "filterDateTo" onchange = "applyFilter()" >
< / div >
<!-- [交互] 双列面板:左选单位→右选部门 -->
< div class = "filter-item" >
< label > 单位部门< / label >
< div class = "org-select-wrap" id = "orgWrap" >
< div class = "org-trigger" id = "orgTrigger" onclick = "toggleOrg()" > 全部单位/部门< / div >
< div class = "org-panel" id = "orgPanel" >
< div class = "org-col" id = "orgUnitCol" >
< div class = "org-col__title" > 单位< / div >
< div class = "org-item active" data-unit = "" onclick = "selectUnit(this,'全部单位/部门','')" > 全部单位/部门< / div >
< div class = "org-item" data-unit = "第四采油厂" onclick = "selectUnit(this,'第四采油厂','第四采油厂')" > 第四采油厂< / div >
< div class = "org-item" data-unit = "采油一厂" onclick = "selectUnit(this,'采油一厂','采油一厂')" > 采油一厂< / div >
< div class = "org-item" data-unit = "采油二厂" onclick = "selectUnit(this,'采油二厂','采油二厂')" > 采油二厂< / div >
< div class = "org-item" data-unit = "采气一厂" onclick = "selectUnit(this,'采气一厂','采气一厂')" > 采气一厂< / div >
< / div >
< div class = "org-col" id = "orgDeptCol" >
< div class = "org-col__title" > 部门< / div >
< div class = "org-item active" data-dept = "" onclick = "selectDept(this,'全部部门','')" > 全部部门< / div >
< / div >
< / div >
< / div >
< / div >
< div class = "filter-item" >
< label > 姓名< / label >
< input class = "filter-input" id = "filterName" placeholder = "请输入姓名" style = "width:100px;" >
< / div >
< div class = "filter-item" >
< label > 员工编号< / label >
< input class = "filter-input" id = "filterEmpId" placeholder = "请输入编号" style = "width:110px;" >
< / div >
<!-- [交互] 点击查询按钮筛选列表 -->
< button class = "btn btn-primary" onclick = "applyFilter()" >
< svg width = "14" height = "14" viewBox = "0 0 14 14" fill = "none" > < circle cx = "6" cy = "6" r = "4.5" stroke = "#fff" stroke-width = "1.3" / > < path d = "M9.5 9.5L13 13" stroke = "#fff" stroke-width = "1.3" stroke-linecap = "round" / > < / svg > 查询
< / button >
< button class = "btn btn-default" onclick = "resetFilter()" > 重置< / button >
< / div >
<!-- [交互] 操作按钮区 -->
< div class = "action-bar" >
< button class = "btn btn-export" onclick = "showToast('导出列表数据功能开发中')" >
< svg width = "14" height = "14" viewBox = "0 0 14 14" fill = "none" > < path d = "M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke = "currentColor" stroke-width = "1.4" stroke-linecap = "round" stroke-linejoin = "round" / > < / svg > 导出列表数据
< / button >
<!-- [交互] 点击打开导出任务列表弹窗 -->
< button class = "btn btn-default" onclick = "showToast('导出任务列表开发中')" >
< svg width = "14" height = "14" viewBox = "0 0 14 14" fill = "none" > < rect x = "2" y = "2" width = "10" height = "10" rx = "1.5" stroke = "currentColor" stroke-width = "1.4" / > < path d = "M4 5h6M4 7.5h4" stroke = "currentColor" stroke-width = "1.2" stroke-linecap = "round" / > < / svg > 查看导出任务
< / button >
< / div >
<!-- 数据表格 -->
< div class = "table-card" >
< div class = "table-card__content" >
< table >
< thead >
<!-- [交互] 表头第一行:父级分类(饮食摄入、运动消耗、体重为合并单元格) -->
< tr >
< th rowspan = "2" > 序号< / th >
< th rowspan = "2" > 干预计划< / th >
< th rowspan = "2" > 姓名< / th >
< th rowspan = "2" > 员工编号< / th >
< th rowspan = "2" > 性别< / th >
< th rowspan = "2" > 年龄< / th >
< th rowspan = "2" > 单位< / th >
< th rowspan = "2" > 部门< / th >
< th rowspan = "2" > 日期< / th >
< th rowspan = "2" > 身高< br > (cm)< / th >
< th class = "th-parent" colspan = "4" > 饮食摄入(千卡)< / th >
< th class = "th-parent" colspan = "2" > 运动消耗(千卡)< / th >
< th class = "th-parent" colspan = "3" > 体重记录< / th >
< th rowspan = "2" > 体重报告< / th >
< / tr >
<!-- [交互] 表头第二行:饮食摄入、运动消耗、体重的子列 -->
< tr >
< th > 早餐< / th >
< th > 午餐< / th >
< th > 晚餐< / th >
< th class = "col-diet-last" > 加餐< / th >
< th > 基础消耗< / th >
< th class = "col-exercise-last" > 其他消耗< / th >
< th > 体重< / th >
< th > 体脂< / th >
< th class = "col-weight-last" > 腰围< / th >
< / tr >
< / thead >
< tbody id = "tableBody" > < / tbody >
< / table >
< / div >
< div class = "pagination" >
< span id = "pageInfo" > 显示 0 条记录< / span >
< button class = "page-btn" onclick = "goPage(-1)" > ‹ < / button >
< span id = "pageNum" style = "font-size:var(--font-sm);color:var(--text-secondary);" > 第 1 页< / span >
< button class = "page-btn" onclick = "goPage(1)" > › < / button >
< / div >
< / div >
< / main >
<!-- [交互] 体重报告弹窗 — 点击列表"查看"按钮打开 -->
< div class = "report-overlay" id = "reportOverlay" onclick = "if(event.target===this)closeReport()" >
< div class = "report-modal" >
< div class = "report-modal__header" >
< span class = "report-modal__title" id = "reportModalTitle" > 体重报告< / span >
<!-- [交互] 点击关闭弹窗 -->
< button class = "report-modal__close" onclick = "closeReport()" > ✕< / button >
< / div >
<!-- 总览卡片(固定不滚动) -->
< div class = "report-overview" >
< div class = "report-overview__title" id = "rpOverviewTitle" > 体重报告< / div >
< div class = "report-overview__row" >
< span > 截止至 < strong id = "rpCutoff" > -< / strong > < / span >
< span > 第 < strong id = "rpDay" > -< / strong > 天(共 < strong id = "rpTotal" > -< / strong > 天)< / span >
< / div >
< div class = "report-overview__rank" id = "rpRank" >
目前的减重量排名 < strong id = "rpRankWeight" > -< / strong > ,减重率排名 < strong id = "rpRankRate" > -< / strong > ,结合个人打卡情况,综合分排名 < strong id = "rpRankScore" > -< / strong >
< / div >
< div class = "report-overview__metrics" >
< div class = "report-overview__metric" >
< div class = "report-overview__metric-label" > 已减重< / div >
< div class = "report-overview__metric-val" id = "rpLoss" > -< span class = "report-overview__metric-unit" > kg< / span > < / div >
< / div >
< div class = "report-overview__metric" >
< div class = "report-overview__metric-label" > 减重率< / div >
< div class = "report-overview__metric-val" id = "rpRate" > -< span class = "report-overview__metric-unit" > %< / span > < / div >
< / div >
< / div >
< / div >
<!-- 记录表格(可滚动) -->
< div class = "report-modal__body" >
< div class = "report-table-wrap" >
< table class = "report-table" >
< thead >
< tr >
< th > 日期< / th >
< th > 体重(kg)< / th >
< th > BMI< / th >
< th > 体脂率(%)< / th >
< th > 腰围(cm)< / th >
< th > 摄入(kcal)< / th >
< th > 消耗(kcal)< / th >
< / tr >
< / thead >
< tbody id = "reportTableBody" > < / tbody >
< / table >
< / div >
< / div >
< div class = "report-modal__footer" >
< button class = "btn btn-default" onclick = "closeReport()" > 关闭< / button >
< / div >
< / div >
< / div >
<!-- Toast -->
< div class = "toast" id = "toast" > < / div >
< script >
/* ===== 侧边栏当前页高亮 ===== */
( function ( ) {
var current = location . pathname . split ( '/' ) . pop ( ) ;
document . querySelectorAll ( '.sidebar-item' ) . forEach ( function ( item ) {
var href = item . getAttribute ( 'href' ) ;
item . classList . toggle ( 'active' , href === current ) ;
} ) ;
} ) ( ) ;
/* ===== 干预计划数据 ===== */
var plans = [
{ id : 1 , name : '第四采油厂2026年超重健康干预计划' , org : '第四采油厂' , dateFrom : '2026-03-01' , dateTo : '2026-08-31' , totalCount : 234 , actualCount : 218 } ,
{ id : 2 , name : '采油一厂2026年体重管理专项行动' , org : '采油一厂' , dateFrom : '2026-04-01' , dateTo : '2026-09-30' , totalCount : 186 , actualCount : 180 } ,
{ id : 3 , name : '采油二厂2026年健康减重计划' , org : '采油二厂' , dateFrom : '2026-05-15' , dateTo : '2026-11-15' , totalCount : 312 , actualCount : 295 } ,
{ id : 4 , name : '采气一厂2025年度体重管理计划' , org : '采气一厂' , dateFrom : '2025-06-01' , dateTo : '2025-12-01' , totalCount : 158 , actualCount : 142 } ,
{ id : 5 , name : '第四采油厂2025年超重健康干预计划' , org : '第四采油厂' , dateFrom : '2025-03-01' , dateTo : '2025-08-31' , totalCount : 267 , actualCount : 251 } ,
{ id : 6 , name : '采油一厂2025年员工健康体重计划' , org : '采油一厂' , dateFrom : '2025-04-15' , dateTo : '2025-10-15' , totalCount : 420 , actualCount : 398 } ,
{ id : 7 , name : '采气一厂2026下半年体重干预计划' , org : '采气一厂' , dateFrom : '2026-09-01' , dateTo : '2026-12-31' , totalCount : 89 , actualCount : 72 } ,
{ id : 8 , name : '采油二厂2024年体重控制专项计划' , org : '采油二厂' , dateFrom : '2024-07-01' , dateTo : '2024-12-31' , totalCount : 145 , actualCount : 130 }
] ;
/* ===== 基础员工数据 ===== */
var baseEmployees = [
{ org : '第四采油厂' , dept : '健康管理部' , name : '张伟' , empId : 'CQ001234' , gender : '男' , age : 42 , height : 172 , weight : 85 , bmi : 28.7 , waist : 96 , fat : 28.5 } ,
{ org : '第四采油厂' , dept : '健康管理部' , name : '李娜' , empId : 'CQ001235' , gender : '女' , age : 35 , height : 160 , weight : 72 , bmi : 28.1 , waist : 84 , fat : 35.2 } ,
{ org : '第四采油厂' , dept : '健康管理部' , name : '王磊' , empId : 'CQ001236' , gender : '男' , age : 28 , height : 178 , weight : 90 , bmi : 28.4 , waist : 98 , fat : 26.8 } ,
{ org : '第四采油厂' , dept : '安全环保科' , name : '赵敏' , empId : 'CQ001237' , gender : '女' , age : 39 , height : 163 , weight : 75 , bmi : 28.2 , waist : 86 , fat : 36.5 } ,
{ org : '第四采油厂' , dept : '安全环保科' , name : '刘洋' , empId : 'CQ001238' , gender : '男' , age : 45 , height : 175 , weight : 88 , bmi : 28.7 , waist : 100 , fat : 29.2 } ,
{ org : '第四采油厂' , dept : '安全环保科' , name : '陈晨' , empId : 'CQ001239' , gender : '男' , age : 31 , height : 170 , weight : 82 , bmi : 28.4 , waist : 94 , fat : 27.5 } ,
{ org : '第四采油厂' , dept : '工会办公室' , name : '周杰' , empId : 'CQ001240' , gender : '男' , age : 50 , height : 168 , weight : 78 , bmi : 27.6 , waist : 92 , fat : 28.1 } ,
{ org : '第四采油厂' , dept : '工会办公室' , name : '吴芳' , empId : 'CQ001241' , gender : '女' , age : 33 , height : 158 , weight : 70 , bmi : 28.0 , waist : 82 , fat : 34.8 } ,
{ org : '第四采油厂' , dept : '生产技术部' , name : '孙丽' , empId : 'CQ001242' , gender : '女' , age : 40 , height : 165 , weight : 76 , bmi : 27.9 , waist : 88 , fat : 37.0 } ,
{ org : '第四采油厂' , dept : '生产技术部' , name : '郑浩' , empId : 'CQ001243' , gender : '男' , age : 36 , height : 180 , weight : 95 , bmi : 29.3 , waist : 104 , fat : 30.5 } ,
{ org : '第四采油厂' , dept : '生产技术部' , name : '黄磊' , empId : 'CQ001244' , gender : '男' , age : 27 , height : 176 , weight : 88 , bmi : 28.4 , waist : 97 , fat : 27.0 } ,
{ org : '第四采油厂' , dept : '生产技术部' , name : '林燕' , empId : 'CQ001245' , gender : '女' , age : 29 , height : 162 , weight : 74 , bmi : 28.2 , waist : 85 , fat : 35.8 } ,
{ org : '采油一厂' , dept : '健康管理部' , name : '杨帆' , empId : 'CQ001246' , gender : '男' , age : 38 , height : 170 , weight : 88 , bmi : 30.4 , waist : 102 , fat : 31.2 } ,
{ org : '采油一厂' , dept : '安全环保科' , name : '孙强' , empId : 'CQ001247' , gender : '男' , age : 44 , height : 173 , weight : 86 , bmi : 28.7 , waist : 99 , fat : 28.8 } ,
{ org : '采油一厂' , dept : '工会办公室' , name : '周强' , empId : 'CQ001248' , gender : '男' , age : 50 , height : 168 , weight : 78 , bmi : 27.6 , waist : 93 , fat : 27.6 } ,
{ org : '采油一厂' , dept : '生产技术部' , name : '吴敏' , empId : 'CQ001249' , gender : '女' , age : 29 , height : 162 , weight : 56 , bmi : 21.3 , waist : 72 , fat : 26.5 } ,
{ org : '采油二厂' , dept : '健康管理部' , name : '郑浩' , empId : 'CQ001250' , gender : '男' , age : 33 , height : 176 , weight : 82 , bmi : 26.5 , waist : 91 , fat : 25.8 } ,
{ org : '采油二厂' , dept : '安全环保科' , name : '黄丽' , empId : 'CQ001251' , gender : '女' , age : 27 , height : 162 , weight : 74 , bmi : 28.2 , waist : 86 , fat : 36.0 } ,
{ org : '采油二厂' , dept : '财务部' , name : '林涛' , empId : 'CQ001252' , gender : '男' , age : 41 , height : 175 , weight : 92 , bmi : 30.0 , waist : 105 , fat : 31.5 } ,
{ org : '采气一厂' , dept : '生产技术部' , name : '何婷' , empId : 'CQ001253' , gender : '女' , age : 34 , height : 160 , weight : 70 , bmi : 27.3 , waist : 84 , fat : 35.3 } ,
{ org : '采气一厂' , dept : '信息中心' , name : '马超' , empId : 'CQ001254' , gender : '男' , age : 36 , height : 179 , weight : 95 , bmi : 29.6 , waist : 107 , fat : 32.0 }
] ;
/* ===== 每日明细Mock数据 ===== */
function buildDailyData ( ) {
var data = [ ] ;
var today = new Date ( ) ;
// 生成最近10天日期列表
var dateList = [ ] ;
for ( var d = 9 ; d >= 0 ; d -- ) {
var dt = new Date ( today ) ;
dt . setDate ( today . getDate ( ) - d ) ;
dateList . push ( formatDate ( dt ) ) ;
}
baseEmployees . forEach ( function ( e , empIdx ) {
var planIdx = empIdx % plans . length ;
var p = plans [ planIdx ] ;
// 目标指标(比初始降低约15%)
var targetWeight = Math . round ( e . weight * 0.85 ) ;
var targetBmi = + ( e . bmi * 0.85 ) . toFixed ( 1 ) ;
var targetWaist = Math . round ( e . waist * 0.88 ) ;
var targetFat = + ( e . fat * 0.80 ) . toFixed ( 1 ) ;
// 第一个员工的进度基准
var progressBase = ( empIdx % 5 ) / 4 ;
dateList . forEach ( function ( dateStr , dateIdx ) {
// 模拟每日波动 + 整体趋势向目标靠近
var dayProgress = progressBase - ( 9 - dateIdx ) * 0.015 ;
var curWeight = + ( e . weight - ( e . weight - targetWeight ) * Math . max ( 0 , dayProgress ) + ( Math . random ( ) - 0.5 ) * 0.6 ) . toFixed ( 1 ) ;
var curBmi = + ( curWeight / ( ( e . height / 100 ) * ( e . height / 100 ) ) ) . toFixed ( 1 ) ;
var curWaist = + ( e . waist - ( e . waist - targetWaist ) * Math . max ( 0 , dayProgress ) + ( Math . random ( ) - 0.5 ) * 0.4 ) . toFixed ( 1 ) ;
var curFat = + ( e . fat - ( e . fat - targetFat ) * Math . max ( 0 , dayProgress ) + ( Math . random ( ) - 0.5 ) * 0.3 ) . toFixed ( 1 ) ;
var weightChange = + ( curWeight - e . weight ) . toFixed ( 1 ) ;
var bmiChange = + ( curBmi - e . bmi ) . toFixed ( 1 ) ;
var fatChange = + ( curFat - e . fat ) . toFixed ( 1 ) ;
var waistChange = + ( curWaist - e . waist ) . toFixed ( 1 ) ;
// 饮食摄入 - 三餐 + 加餐
var breakfast = Math . floor ( Math . random ( ) * 200 ) + 300 ;
var lunch = Math . floor ( Math . random ( ) * 300 ) + 400 ;
var dinner = Math . floor ( Math . random ( ) * 250 ) + 350 ;
var snack = Math . floor ( Math . random ( ) * 150 ) ;
// 运动消耗
var exercise = Math . floor ( Math . random ( ) * 300 ) + 150 ;
var baseExercise = Math . floor ( exercise * 0.6 ) ;
var otherExercise = exercise - baseExercise ;
// 最新体重指标
var latestWeight = + ( curWeight + ( Math . random ( ) - 0.3 ) * 0.5 ) . toFixed ( 1 ) ;
var latestFat = + ( curFat + ( Math . random ( ) - 0.5 ) * 0.4 ) . toFixed ( 1 ) ;
var latestWaist = Math . round ( curWaist + ( Math . random ( ) - 0.5 ) * 1 ) ;
data . push ( {
planId : p . id , planName : p . name ,
org : e . org , dept : e . dept ,
name : e . name , empId : e . empId ,
gender : e . gender , age : e . age ,
date : dateStr ,
height : e . height ,
initWeight : e . weight , initBmi : e . bmi , initFat : e . fat , initWaist : e . waist ,
targetWeight : targetWeight , targetBmi : targetBmi , targetFat : targetFat , targetWaist : targetWaist ,
curWeight : curWeight , curBmi : curBmi , curFat : curFat , curWaist : curWaist ,
weightChange : weightChange , bmiChange : bmiChange , fatChange : fatChange , waistChange : waistChange ,
breakfast : breakfast , lunch : lunch , dinner : dinner , snack : snack ,
exercise : exercise , baseExercise : baseExercise , otherExercise : otherExercise ,
latestWeight : latestWeight , latestFat : latestFat , latestWaist : latestWaist
} ) ;
} ) ;
} ) ;
return data ;
}
var dailyData = buildDailyData ( ) ;
var selectedPlanId = null ;
var pageSize = 10 ;
var currentPage = 0 ;
var filteredData = dailyData . slice ( ) ;
/* ===== 计划搜索选择 ===== */
function searchPlan ( ) {
var keyword = document . getElementById ( 'planSearchInput' ) . value . trim ( ) . toLowerCase ( ) ;
var panel = document . getElementById ( 'planPanel' ) ;
var filtered = plans ;
if ( keyword ) {
filtered = plans . filter ( function ( p ) {
return p . name . toLowerCase ( ) . indexOf ( keyword ) > - 1 || p . org . toLowerCase ( ) . indexOf ( keyword ) > - 1 ;
} ) ;
}
if ( filtered . length === 0 ) {
panel . innerHTML = '<div class="search-select__empty">无匹配计划</div>' ;
} else {
var html = '' ;
filtered . forEach ( function ( p ) {
var sel = selectedPlanId === p . id ? ' selected' : '' ;
html += '<div class="search-select__option' + sel + '" onclick="selectPlan(' + p . id + ')">' ;
html += '<div style="font-weight:500;">' + esc ( p . name ) + '</div>' ;
html += '<div class="ss-org">' + esc ( p . org ) + ' | ' + p . dateFrom + ' ~ ' + p . dateTo + '</div>' ;
html += '</div>' ;
} ) ;
panel . innerHTML = html ;
}
}
function showPlanPanel ( ) {
searchPlan ( ) ;
document . getElementById ( 'planPanel' ) . classList . add ( 'show' ) ;
}
function selectPlan ( planId ) {
selectedPlanId = planId ;
var plan = plans . find ( function ( p ) { return p . id === planId ; } ) ;
if ( ! plan ) return ;
document . getElementById ( 'planSearchInput' ) . value = plan . name ;
document . getElementById ( 'planPanel' ) . classList . remove ( 'show' ) ;
document . getElementById ( 'planClearBtn' ) . classList . add ( 'show' ) ;
document . getElementById ( 'planSelect' ) . classList . add ( 'search-select--selected' ) ;
applyFilter ( ) ;
}
/* [交互] 清空已选干预计划,恢复未选择状态 */
function clearPlan ( ) {
selectedPlanId = null ;
document . getElementById ( 'planSearchInput' ) . value = '' ;
document . getElementById ( 'planClearBtn' ) . classList . remove ( 'show' ) ;
document . getElementById ( 'planSelect' ) . classList . remove ( 'search-select--selected' ) ;
document . getElementById ( 'planPanel' ) . classList . remove ( 'show' ) ;
applyFilter ( ) ;
}
/* ===== 单位部门双列面板 ===== */
var deptMap = {
'第四采油厂' : [ '健康管理部' , '安全环保科' , '工会办公室' , '生产技术部' , '财务部' , '信息中心' ] ,
'采油一厂' : [ '健康管理部' , '安全环保科' , '工会办公室' , '生产技术部' ] ,
'采油二厂' : [ '健康管理部' , '安全环保科' , '财务部' ] ,
'采气一厂' : [ '生产技术部' , '信息中心' ]
} ;
var selectedUnitName = '' ;
var selectedUnitCode = '' ;
var selectedDeptName = '' ;
var selectedDeptCode = '' ;
function toggleOrg ( ) {
var panel = document . getElementById ( 'orgPanel' ) ;
var trigger = document . getElementById ( 'orgTrigger' ) ;
panel . classList . toggle ( 'open' ) ;
trigger . classList . toggle ( 'open' ) ;
}
function selectUnit ( elem , unitName , unitCode ) {
document . querySelectorAll ( '#orgUnitCol .org-item' ) . forEach ( function ( item ) { item . classList . remove ( 'active' ) ; } ) ;
elem . classList . add ( 'active' ) ;
selectedUnitName = unitName ;
selectedUnitCode = unitCode ;
var depts = deptMap [ unitCode ] || [ ] ;
var html = '<div class="org-col__title">部门</div>' ;
html += '<div class="org-item active" data-dept="" onclick="selectDept(this,\'全部部门\',\'\')">全部部门</div>' ;
depts . forEach ( function ( d ) {
html += '<div class="org-item" data-dept="' + d + '" onclick="selectDept(this,\'' + d + '\',\'' + d + '\')">' + d + '</div>' ;
} ) ;
document . getElementById ( 'orgDeptCol' ) . innerHTML = html ;
selectedDeptName = '' ;
selectedDeptCode = '' ;
}
function selectDept ( elem , deptName , deptCode ) {
document . querySelectorAll ( '#orgDeptCol .org-item' ) . forEach ( function ( item ) { item . classList . remove ( 'active' ) ; } ) ;
elem . classList . add ( 'active' ) ;
selectedDeptName = deptName ;
selectedDeptCode = deptCode ;
var label = selectedUnitCode ? selectedUnitName : '全部单位' ;
label += deptCode ? ' / ' + deptName : ' / 全部部门' ;
document . getElementById ( 'orgTrigger' ) . textContent = label ;
}
/* ===== 筛选 ===== */
function applyFilter ( ) {
var dateFrom = document . getElementById ( 'filterDateFrom' ) . value ;
var dateTo = document . getElementById ( 'filterDateTo' ) . value ;
var unit = selectedUnitCode ;
var dept = selectedDeptCode ;
var name = document . getElementById ( 'filterName' ) . value . trim ( ) . toLowerCase ( ) ;
var empId = document . getElementById ( 'filterEmpId' ) . value . trim ( ) . toLowerCase ( ) ;
var data = dailyData . slice ( ) ;
if ( selectedPlanId ) { data = data . filter ( function ( d ) { return d . planId === selectedPlanId ; } ) ; }
filteredData = data . filter ( function ( d ) {
if ( dateFrom && d . date < dateFrom ) return false ;
if ( dateTo && d . date > dateTo ) return false ;
if ( unit && d . org !== unit ) return false ;
if ( dept && d . dept !== dept ) return false ;
if ( name && d . name . toLowerCase ( ) . indexOf ( name ) === - 1 ) return false ;
if ( empId && d . empId . toLowerCase ( ) . indexOf ( empId ) === - 1 ) return false ;
return true ;
} ) ;
currentPage = 0 ;
renderTable ( ) ;
}
function resetFilter ( ) {
// 恢复日期默认当日
var today = formatDate ( new Date ( ) ) ;
document . getElementById ( 'filterDateFrom' ) . value = today ;
document . getElementById ( 'filterDateTo' ) . value = today ;
selectedUnitName = '' ;
selectedUnitCode = '' ;
selectedDeptName = '' ;
selectedDeptCode = '' ;
document . getElementById ( 'orgTrigger' ) . textContent = '全部单位/部门' ;
document . querySelectorAll ( '#orgUnitCol .org-item' ) . forEach ( function ( item ) { item . classList . remove ( 'active' ) ; } ) ;
document . querySelectorAll ( '#orgUnitCol .org-item' ) . item ( 0 ) . classList . add ( 'active' ) ;
document . getElementById ( 'orgDeptCol' ) . innerHTML = '<div class="org-col__title">部门</div><div class="org-item active" data-dept="" onclick="selectDept(this,\'全部部门\',\'\')">全部部门</div>' ;
document . getElementById ( 'filterName' ) . value = '' ;
document . getElementById ( 'filterEmpId' ) . value = '' ;
applyFilter ( ) ;
}
/* ===== 渲染表格 ===== */
function renderTable ( ) {
var tbody = document . getElementById ( 'tableBody' ) ;
var start = currentPage * pageSize ;
var pageData = filteredData . slice ( start , start + pageSize ) ;
var totalPages = Math . ceil ( filteredData . length / pageSize ) || 1 ;
document . getElementById ( 'pageInfo' ) . textContent = '显示 ' + pageData . length + ' 条,共 ' + filteredData . length + ' 条记录' ;
document . getElementById ( 'pageNum' ) . textContent = '第 ' + ( currentPage + 1 ) + '/' + totalPages + ' 页' ;
if ( filteredData . length === 0 ) {
tbody . innerHTML = '<tr><td colspan="20" class="td-empty">暂无匹配数据,请选择干预计划后查看</td></tr>' ;
return ;
}
var html = '' ;
pageData . forEach ( function ( d , i ) {
html += '<tr>' ;
html += '<td>' + ( start + i + 1 ) + '</td>' ;
html += '<td>' + esc ( d . planName ) + '</td>' ;
html += '<td>' + esc ( d . name ) + '</td>' ;
html += '<td>' + esc ( d . empId ) + '</td>' ;
html += '<td>' + d . gender + '</td>' ;
html += '<td>' + d . age + '</td>' ;
html += '<td>' + esc ( d . org ) + '</td>' ;
html += '<td>' + esc ( d . dept ) + '</td>' ;
html += '<td>' + d . date + '</td>' ;
html += '<td>' + d . height + '</td>' ;
// 饮食摄入(千卡):早餐、午餐、晚餐、加餐
html += '<td>' + d . breakfast + '</td>' ;
html += '<td>' + d . lunch + '</td>' ;
html += '<td>' + d . dinner + '</td>' ;
html += '<td class="col-diet-last">' + d . snack + '</td>' ;
// 运动消耗(千卡):基础消耗、其他消耗
html += '<td>' + d . baseExercise + '</td>' ;
html += '<td class="col-exercise-last">' + d . otherExercise + '</td>' ;
// 体重记录:体重、体脂、腰围
html += '<td>' + d . latestWeight + '</td>' ;
html += '<td>' + d . latestFat . toFixed ( 1 ) + '</td>' ;
html += '<td class="col-weight-last">' + d . latestWaist + '</td>' ;
// 体重报告
html += '<td><a class="link" href="javascript:void(0)" onclick="viewWeightReport(\'' + esc ( d . empId ) + '\',\'' + esc ( d . name ) + '\',\'' + d . date + '\')">查看</a></td>' ;
html += '</tr>' ;
} ) ;
tbody . innerHTML = html ;
}
/* ===== 翻页 ===== */
function goPage ( delta ) {
var totalPages = Math . ceil ( filteredData . length / pageSize ) ;
var newPage = currentPage + delta ;
if ( newPage < 0 || newPage >= totalPages ) return ;
currentPage = newPage ;
renderTable ( ) ;
}
/* ===== 体重报告弹窗 ===== */
var reportEmpId = null ;
var reportEmpName = '' ;
var reportDate = '' ;
var reportRecords = [ ] ;
/* 生成指定员工的体重报告Mock数据 */
function generateReportRecords ( empId , empName , targetDate ) {
var endDate = parseDate ( targetDate ) ;
var records = [ ] ;
var emp = null ;
for ( var i = 0 ; i < baseEmployees . length ; i ++ ) {
if ( baseEmployees [ i ] . empId === empId ) { emp = baseEmployees [ i ] ; break ; }
}
if ( ! emp ) emp = baseEmployees [ 0 ] ;
var initWeight = emp . weight ;
var height = emp . height ;
var currentWeight = initWeight - ( Math . random ( ) * 8 + 4 ) ;
var startWeight = currentWeight + ( Math . random ( ) * 6 + 6 ) ;
for ( var d = 60 ; d >= 0 ; d -= ( Math . random ( ) > 0.5 ? 2 : 3 ) ) {
var dt = new Date ( endDate ) ;
dt . setDate ( dt . getDate ( ) - d ) ;
if ( dt > endDate ) continue ;
var progress = ( 60 - d ) / 60 ;
var w = + ( startWeight - ( startWeight - currentWeight ) * progress + ( Math . random ( ) - 0.5 ) * 0.4 ) . toFixed ( 1 ) ;
if ( d === 0 ) w = currentWeight ;
var bmi = + ( w / ( ( height / 100 ) * ( height / 100 ) ) ) . toFixed ( 1 ) ;
var fat = + ( 22 + ( initWeight - w ) * 0.3 + ( Math . random ( ) - 0.5 ) * 0.5 ) . toFixed ( 1 ) ;
var waist = + ( 80 + ( initWeight - w ) * 1.2 + ( Math . random ( ) - 0.5 ) * 0.3 ) . toFixed ( 1 ) ;
var intake = Math . floor ( Math . random ( ) * 600 ) + 1000 ;
var consume = Math . floor ( Math . random ( ) * 500 ) + 800 ;
records . push ( {
date : formatDate ( dt ) ,
weight : w , bmi : bmi , bodyFat : fat , waist : waist ,
intake : intake , consume : consume
} ) ;
if ( d === 0 ) break ;
}
if ( records . length === 0 || records [ records . length - 1 ] . date !== targetDate ) {
var w = currentWeight ;
var bmi = + ( w / ( ( height / 100 ) * ( height / 100 ) ) ) . toFixed ( 1 ) ;
var fat = + ( 22 + ( initWeight - w ) * 0.3 ) . toFixed ( 1 ) ;
var waist = + ( 80 + ( initWeight - w ) * 1.2 ) . toFixed ( 1 ) ;
records . push ( {
date : targetDate ,
weight : w , bmi : bmi , bodyFat : fat , waist : waist ,
intake : Math . floor ( Math . random ( ) * 600 ) + 1000 ,
consume : Math . floor ( Math . random ( ) * 500 ) + 800
} ) ;
}
return records ;
}
function viewWeightReport ( empId , name , date ) {
reportEmpId = empId ;
reportEmpName = name ;
reportDate = date ;
reportRecords = generateReportRecords ( empId , name , date ) ;
updateReportOverview ( ) ;
renderReportRecords ( ) ;
document . getElementById ( 'reportOverlay' ) . classList . add ( 'show' ) ;
document . body . style . overflow = 'hidden' ;
}
function closeReport ( ) {
document . getElementById ( 'reportOverlay' ) . classList . remove ( 'show' ) ;
document . body . style . overflow = '' ;
}
function updateReportOverview ( ) {
var recs = reportRecords ;
if ( recs . length === 0 ) return ;
var firstRec = recs [ 0 ] ;
var lastRec = recs [ recs . length - 1 ] ;
var startW = firstRec . weight ;
var curW = lastRec . weight ;
var loss = startW - curW ;
var rate = startW > 0 ? ( loss / startW ) * 100 : 0 ;
var firstDate = parseDate ( firstRec . date ) ;
var lastDate = parseDate ( lastRec . date ) ;
var days = Math . floor ( ( lastDate - firstDate ) / ( 1000 * 60 * 60 * 24 ) ) + 1 ;
document . getElementById ( 'reportModalTitle' ) . textContent = reportEmpName + ' - 体重报告' ;
document . getElementById ( 'rpOverviewTitle' ) . textContent = reportEmpName + ' 的体重报告' ;
document . getElementById ( 'rpCutoff' ) . textContent = lastRec . date ;
document . getElementById ( 'rpDay' ) . textContent = days ;
document . getElementById ( 'rpTotal' ) . textContent = days ;
document . getElementById ( 'rpLoss' ) . innerHTML = loss . toFixed ( 1 ) + '<span class="report-overview__metric-unit">kg</span>' ;
document . getElementById ( 'rpRate' ) . innerHTML = rate . toFixed ( 1 ) + '<span class="report-overview__metric-unit">%</span>' ;
// 模拟排名(基于empId的hash保证同一员工每次排名一致)
var hash = 0 ;
for ( var i = 0 ; i < reportEmpId . length ; i ++ ) { hash = ( ( hash << 5 ) - hash ) + reportEmpId . charCodeAt ( i ) ; hash |= 0 ; }
var rankWeight = Math . abs ( hash % 80 ) + 1 ;
var rankRate = Math . abs ( ( hash * 7 + 13 ) % 75 ) + 1 ;
var rankScore = Math . abs ( ( hash * 3 + 47 ) % 60 ) + 1 ;
document . getElementById ( 'rpRankWeight' ) . textContent = '第 ' + rankWeight + ' 名' ;
document . getElementById ( 'rpRankRate' ) . textContent = '第 ' + rankRate + ' 名' ;
document . getElementById ( 'rpRankScore' ) . textContent = '第 ' + rankScore + ' 名' ;
}
function renderReportRecords ( ) {
var tbody = document . getElementById ( 'reportTableBody' ) ;
if ( reportRecords . length === 0 ) {
tbody . innerHTML = '<tr><td colspan="7" class="report-table-empty">暂无记录</td></tr>' ;
return ;
}
var html = '' ;
for ( var i = 0 ; i < reportRecords . length ; i ++ ) {
var r = reportRecords [ i ] ;
html += '<tr>' ;
html += '<td>' + r . date + '</td>' ;
html += '<td>' + r . weight . toFixed ( 1 ) + '</td>' ;
html += '<td>' + r . bmi . toFixed ( 1 ) + '</td>' ;
html += '<td>' + r . bodyFat . toFixed ( 1 ) + '</td>' ;
html += '<td>' + r . waist . toFixed ( 1 ) + '</td>' ;
html += '<td>' + r . intake + '</td>' ;
html += '<td>' + r . consume + '</td>' ;
html += '</tr>' ;
}
tbody . innerHTML = html ;
}
function parseDate ( s ) { var p = s . split ( '-' ) ; return new Date ( + p [ 0 ] , + p [ 1 ] - 1 , + p [ 2 ] ) ; }
/* ===== 工具函数 ===== */
function formatDate ( d ) {
var y = d . getFullYear ( ) ;
var m = ( d . getMonth ( ) + 1 ) ;
var day = d . getDate ( ) ;
return y + '-' + ( m < 10 ? '0' + m : m ) + '-' + ( day < 10 ? '0' + day : day ) ;
}
function calcPlanDays ( dateFrom , dateTo ) {
var today = new Date ( ) ;
var from = new Date ( dateFrom ) ;
var to = new Date ( dateTo ) ;
var end = to < today ? to : today ;
if ( end < from ) return '未开始' ;
return Math . ceil ( ( end - from ) / ( 1000 * 60 * 60 * 24 ) ) + 1 ;
}
function esc ( s ) { return String ( s || '' ) . replace ( /&/g , '&' ) . replace ( /"/g , '"' ) . replace ( /</g , '<' ) . replace ( />/g , '>' ) ; }
function showToast ( msg ) {
var t = document . getElementById ( 'toast' ) ; t . textContent = msg ; t . classList . add ( 'show' ) ;
setTimeout ( function ( ) { t . classList . remove ( 'show' ) ; } , 1500 ) ;
}
/* 点击外部关闭下拉 */
document . addEventListener ( 'click' , function ( e ) {
if ( ! e . target . closest ( '#planSelect' ) ) {
document . getElementById ( 'planPanel' ) . classList . remove ( 'show' ) ;
}
var orgWrap = document . getElementById ( 'orgWrap' ) ;
if ( orgWrap && ! orgWrap . contains ( e . target ) ) {
document . getElementById ( 'orgPanel' ) . classList . remove ( 'open' ) ;
document . getElementById ( 'orgTrigger' ) . classList . remove ( 'open' ) ;
}
} ) ;
/* ===== 初始化 ===== */
( function init ( ) {
// 初始化日期范围为当日
var today = formatDate ( new Date ( ) ) ;
document . getElementById ( 'filterDateFrom' ) . value = today ;
document . getElementById ( 'filterDateTo' ) . value = today ;
// 预填充计划下拉数据
var html = '' ;
plans . forEach ( function ( p ) {
html += '<div class="search-select__option" onclick="selectPlan(' + p . id + ')">' ;
html += '<div style="font-weight:500;">' + esc ( p . name ) + '</div>' ;
html += '<div class="ss-org">' + esc ( p . org ) + ' | ' + p . dateFrom + ' ~ ' + p . dateTo + '</div>' ;
html += '</div>' ;
} ) ;
document . getElementById ( 'planPanel' ) . innerHTML = html ;
renderTable ( ) ;
} ) ( ) ;
< / script >
< / body >
< / html >