@@ -2,168 +2,68 @@
< view class = "page" : style = "{ paddingTop: navBarHeight }" >
< CustomNavBar title = "管理中心" show -back / >
<!-- Stats summary card -- >
< view class = "stats-card-wrap " >
< view class = "stats-card" >
< view v-if = "statsLoading" class="stats-loading" >
< view v-for = "i in 3" :key="i" class="stat-skeleton" / >
< / view >
< template v-else >
< view class = "stat-block" >
< text class = "stat-num" > { { stats . todayBookings } } < / text >
< text class = "stat-sub" > 今日预约 < / text >
< / view >
< view class = "stat-sep" / >
< view class = "stat-block" >
< text class = "stat-num" > { { stats . totalOrders } } < / text >
< text class = "stat-sub" > 总订单 < / text >
< / view >
< view class = "stat-sep" / >
< view class = "stat-block" >
< text class = "stat-num" > { { stats . totalBookings } } < / text >
< text class = "stat-sub" > 总预约 < / text >
< / view >
< / template >
<!-- Section : 课务运营 -- >
< view class = "section-header " >
< text class = "section-title" > 课务运营 < / text >
< / view >
< view class = "list" >
< view class = "list-item" @tap ="navigate('/pages/admin/schedule')" >
< text class = "item-title" > 排课管理 < / text >
< text class = "arrow-text" > › < / text >
< / view >
< view class = "list-item" @tap ="navigate('/pages/admin/bookings')" >
< text class = "item-title" > 预约管理 < / text >
< text class = "arrow-text" > › < / text >
< / view >
< / view >
< view class = "section-header" > < text class = "section-title" > 教学报告 < / text > < / view >
<!-- Section : 教学复盘 -- >
< view class = "section-header" >
< text class = "section-title" > 教学复盘 < / text >
< / view >
< view class = "list" >
< view class = "list-item" @tap ="navigate('/pages/admin/analytics')" >
< view class = "item-left" >
< view class = "item-icon-wrap icon--subscribe" > < text class = "item-icon -text" > ▥ < / text > < / view >
< view class = "item-text-group" > < text class = "item-title" > 统计分析 < / text > < text class = "item-desc" > 月度课次 · 学员出勤 · 上课明细 < / text > < / view >
< / view >
< view class = "item-arrow" > < text class = "arrow-text" > › < / text > < / view >
< text class = "item-title" > 统计分析 < / text >
< text class = "arrow -text" > › < / text >
< / view >
< / view >
< view class = "list" > < view class = "list-item" @tap ="navigate('/pages/admin/reviews')" > < view class = "item-left" > < view class = "item-icon-wrap icon--subscribe" > < text class = "item-icon-text" > ☆ < / text > < / view > < view class = "item-text-group" > < text class = "item-title" > 课后评价 < / text > < text class = "item-desc" > 学员反馈 · 星级均分 · 推荐意愿趋势 < / text > < / view > < / view > < view class = "item-arrow" > < text class = "arrow-text" > › < / text > < / view > < / view > < / view >
<!-- Section header : 课程管理 -- >
< view class = "section-header" >
< text class = "section-title" > 课程管理 < / text >
< / view >
<!-- List : schedule -- >
< view class = "list" >
< view class = "list-item" @tap ="navigate('/pages/admin/bookings')" >
< view class = "item-left" >
< view class = "item-icon-wrap icon--bookings" >
< text class = "item-icon-text" > ▣ < / text >
< / view >
< view class = "item-text-group" >
< text class = "item-title" > 预约管理 < / text >
< text class = "item-desc" > 查看 / 确认 / 核销学员预约 < / text >
< / view >
< / view >
< view class = "item-arrow" >
< text class = "arrow-text" > › < / text >
< / view >
< / view >
< view class = "list-item" @tap ="navigate('/pages/admin/schedule')" >
< view class = "item-left" >
< view class = "item-icon-wrap icon--schedule" >
< text class = "item-icon-text" > ◇ < / text >
< / view >
< view class = "item-text-group" >
< text class = "item-title" > 排课管理 < / text >
< text class = "item-desc" > 管理每周课程时段 < / text >
< / view >
< / view >
< view class = "item-arrow" >
< text class = "arrow-text" > › < / text >
< / view >
< view class = "list-item" @tap ="navigate('/pages/admin/reviews')" >
< text class = "item-title" > 课后评价 < / text >
< text class = "arrow-text" > › < / text >
< / view >
< / view >
<!-- Section header : 会员与订单 -- >
<!-- Section : 会员与订单 -- >
< view class = "section-header" >
< text class = "section-title" > 会员与订单 < / text >
< / view >
<!-- List : members & orders -- >
< view class = "list" >
< view class = "list-item" @tap ="navigate('/pages/admin/members')" >
< view class = "item-left" >
< view class = "item-icon-wrap icon--members" >
< text class = "item-icon-text" > ◎ < / text >
< / view >
< view class = "item-text-group" >
< text class = "item-title" > 会员管理 < / text >
< text class = "item-desc" > 查看所有会员信息 < / text >
< / view >
< / view >
< view class = "item-arrow" >
< text class = "arrow-text" > › < / text >
< / view >
< text class = "item-title" > 会员管理 < / text >
< text class = "arrow-text" > › < / text >
< / view >
< view class = "list-item" @tap ="navigate('/pages/admin/orders')" >
< view class = "item-left" >
< view class = "item-icon-wrap icon--orders" >
< text class = "item-icon-text" > ▣ < / text >
< / view >
< view class = "item-text-group" >
< text class = "item-title" > 订单管理 < / text >
< text class = "item-desc" > 查看所有订单记录 < / text >
< / view >
< / view >
< view class = "item-arrow" >
< text class = "arrow-text" > › < / text >
< / view >
< / view >
< view class = "list-item" @tap ="navigate('/pages/admin/card-types')" >
< view class = "item-left" >
< view class = "item-icon-wrap icon--card" >
< text class = "item-icon-text" > ▤ < / text >
< / view >
< view class = "item-text-group" >
< text class = "item-title" > 卡种管理 < / text >
< text class = "item-desc" > 设置会员卡类型 < / text >
< / view >
< / view >
< view class = "item-arrow" >
< text class = "arrow-text" > › < / text >
< / view >
< text class = "item-title" > 订单管理 < / text >
< text class = "arrow-text" > › < / text >
< / view >
< / view >
<!-- Section header : 系统 -- >
<!-- Section : 系统设置 -- >
< view class = "section-header" >
< text class = "section-title" > 系统 < / text >
< text class = "section-title" > 系统设置 < / text >
< / view >
<!-- List : settings -- >
< view class = "list" >
< view class = "list-item" @tap ="navigate('/pages/admin/studio ')" >
< view class = "item-left" >
< view class = "item-icon-wrap icon--studio" >
< text class = "item-icon-text" > ◉ < / text >
< / view >
< view class = "item-text-group" >
< text class = "item-title " > 工作室设置 < / text >
< text class = "item-desc" > 工作室信息与配置 < / text >
< / view >
< / view >
< view class = "item-arrow" >
< text class = "arrow-text" > › < / text >
< / view >
< view class = "list-item" @tap ="navigate('/pages/admin/card-types ')" >
< text class = "item-title" > 卡种管理 < / text >
< text class = "arrow-text" > › < / text >
< / view >
< view class = "list-item" @tap ="navigate('/pages/admin/studio')" >
< text class = "item-title" > 工作室设置 < / text >
< text class = "arrow-text " > › < / text >
< / view >
< view class = "list-item" @tap ="handleIncreaseSubscriptionCount" >
< view class = "item-left" >
< view class = "item-icon-wrap icon--subscribe" >
< text class = "item-icon -text" > ✦ < / text >
< / view >
< view class = "item-text-group" >
< text class = "item-title" > 增加订阅次数 < / text >
< text class = "item-desc" > 当前剩余 { { user ? . adminBookingSubscriptionCount ? ? 0 } } 次 < / text >
< / view >
< / view >
< view class = "item-arrow" >
< text class = "arrow-text" > { { adminSubscribeLoading ? '...' : '› ' } } < / text >
< / view >
< text class = "item-title" > 增加订阅次数 < / text >
< text class = "item-extra" > 剩余 { { user ? . adminBookingSubscriptionCount ? ? 0 } } 次 < / text >
< text class = "arrow -text" > { { adminSubscribeLoading ? '...' : '› ' } } < / text >
< / view >
< / view >
@@ -176,37 +76,21 @@ import { ref, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import CustomNavBar from '../../components/CustomNavBar.vue'
import { getSystemLayout } from '../../utils/system'
import { useAdminStore } from './stores/admin'
import { useUserStore } from '../../stores/user'
import type { AdminStats } from './stores/admin'
import { requestAdminBookingSubscriptionCount } from '../../utils/wechat-subscription'
import { getErrorMessage } from '../../utils/auth'
const navBarHeight = ref ( '64px' )
const adminStore = useAdminStore ( )
const userStore = useUserStore ( )
const { user } = storeToRefs ( userStore )
const statsLoading = ref ( false )
const stats = ref < AdminStats > ( { todayBookings : 0 , totalOrders : 0 , totalBookings : 0 } )
const adminSubscribeLoading = ref ( false )
function navigate ( path : string ) {
uni . navigateTo ( { url : path } )
}
async function loadStats ( ) {
statsLoading . value = true
try {
stats . value = await adminStore . fetchDashboardStats ( )
} catch {
// fail silently — stats are non-critical
} finally {
statsLoading . value = false
}
}
async function handleIncreaseSubscriptionCount ( ) {
if ( adminSubscribeLoading . value ) {
return
@@ -231,78 +115,18 @@ async function handleIncreaseSubscriptionCount() {
onMounted ( ( ) => {
navBarHeight . value = ` ${ getSystemLayout ( ) . navBarHeight } px `
loadStats ( )
userStore . fetchProfile ( )
} )
< / script >
< style lang = "scss" scoped >
/* ── Page ───────────────────────────────────── */
/* ── Page ───────────────────────── */
. page {
min - height : 100 vh ;
padding - bottom : 40 rpx ;
}
/* ── Stats card ────────────── ───────────────── */
. stats - card - wrap {
padding : 24 rpx 24 rpx 8 rpx ;
}
. stats - card {
background : # FFFFFF ;
border - radius : 20 rpx ;
padding : 32 rpx 24 rpx ;
display : flex ;
align - items : center ;
box - shadow : 0 4 rpx 20 rpx rgba ( 180 , 160 , 130 , 0.10 ) ;
border : 1 rpx solid rgba ( 180 , 160 , 130 , 0.12 ) ;
}
. stats - loading {
width : 100 % ;
display : flex ;
align - items : center ;
justify - content : space - around ;
}
. stat - skeleton {
width : 100 rpx ;
height : 64 rpx ;
border - radius : 12 rpx ;
background : linear - gradient ( 90 deg , $primary - border 25 % , $primary - light 50 % , $primary - border 75 % ) ;
background - size : 400 % 100 % ;
animation : shimmer 1.6 s ease infinite ;
}
. stat - block {
flex : 1 ;
display : flex ;
flex - direction : column ;
align - items : center ;
gap : 8 rpx ;
}
. stat - num {
font - size : 44 rpx ;
font - weight : 700 ;
color : # 4 A4035 ;
line - height : 1 ;
font - family : 'DIN Alternate' , 'Helvetica Neue' , Arial , sans - serif ;
}
. stat - sub {
font - size : 22 rpx ;
color : # A09080 ;
letter - spacing : 0.5 rpx ;
}
. stat - sep {
width : 1 rpx ;
height : 56 rpx ;
background : rgba ( 180 , 160 , 130 , 0.2 ) ;
}
/* ── Section header ─────────────────────────── */
/* ── Section header ───────────────── */
. section - header {
padding : 32 rpx 24 rpx 12 rpx ;
}
@@ -315,7 +139,7 @@ onMounted(() => {
text - transform : uppercase ;
}
/* ── List ───────────────────────────────────── */
/* ── List ───────────────────────── */
. list {
background : # FFFFFF ;
margin : 0 24 rpx ;
@@ -328,8 +152,8 @@ onMounted(() => {
. list - item {
display : flex ;
align - items : center ;
justify - content : space - between ;
padding : 28 rpx 24 rpx ;
gap : 16 rpx ;
padding : 3 2rpx 28 rpx ;
border - bottom : 1 rpx solid rgba ( 180 , 160 , 130 , 0.1 ) ;
transition : background 0.15 s ease ;
@@ -342,63 +166,23 @@ onMounted(() => {
}
}
. item - left {
display : flex ;
align - items : center ;
gap : 20 rpx ;
}
. item - icon - wrap {
width : 72 rpx ;
height : 72 rpx ;
border - radius : 18 rpx ;
display : flex ;
align - items : center ;
justify - content : center ;
flex - shrink : 0 ;
}
. item - icon - text {
font - size : 32 rpx ;
color : # FFFFFF ;
line - height : 1 ;
}
/* Icon variants — warm muted tones */
. icon -- bookings { background : linear - gradient ( 135 deg , # C4A87E , # B49868 ) ; }
. icon -- schedule { background : linear - gradient ( 135 deg , # 8 B9E7E , # 7 A8E6E ) ; }
. icon -- template { background : linear - gradient ( 135 deg , # A090C0 , # 9080 B0 ) ; }
. icon -- members { background : linear - gradient ( 135 deg , $primary - color , $primary - dark ) ; }
. icon -- orders { background : linear - gradient ( 135 deg , # 7 E9EC4 , # 6 E8EB4 ) ; }
. icon -- card { background : linear - gradient ( 135 deg , # C48E7E , # B47E6E ) ; }
. icon -- studio { background : linear - gradient ( 135 deg , # 9 E9E7E , # 8 E8E6E ) ; }
. icon -- subscribe { background : linear - gradient ( 135 deg , # 5 D8C8A , # 476 D72 ) ; }
. item - text - group {
display : flex ;
flex - direction : column ;
gap : 4 rpx ;
}
. item - title {
flex : 1 ;
font - size : 30 rpx ;
font - weight : 6 00;
font - weight : 5 00;
color : # 4 A4035 ;
letter - spacing : 0.5 rpx ;
}
. item - desc {
. item - extra {
font - size : 24 rpx ;
color : # A09080 ;
}
. item - arrow {
flex - shrink : 0 ;
padding - left : 16 rpx ;
}
. arrow - text {
font - size : 40 rpx ;
flex - shrink : 0 ;
font - size : 36 rpx ;
color : rgba ( 180 , 160 , 130 , 0.5 ) ;
font - weight : 300 ;
line - height : 1 ;