This commit is contained in:
qsh
2026-02-03 09:50:16 +08:00
parent a5977aa41f
commit 20ab62b473
3 changed files with 113 additions and 322 deletions

9
src/api/student/index.js Normal file
View File

@@ -0,0 +1,9 @@
import request from '@/utils/request';
export const getStudentList = async params => {
return await request({
url: '/applet/xunjia/user/pageList',
method: 'GET',
params: params
});
};

View File

@@ -6,34 +6,30 @@
<view class="back-icon"></view> <view class="back-icon"></view>
</view> </view>
<view class="header-title">学员列表管理</view> <view class="header-title">学员列表管理</view>
<view class="header-right" @click="addStudent"> <view class="header-right"></view>
<view class="add-btn">添加</view>
</view>
</view> </view>
<!-- 筛选条件 --> <!-- 筛选条件 -->
<view class="filter-section"> <view class="filter-section">
<view class="filter-row"> <view class="filter-row">
<view class="filter-item"> <view class="filter-item">
<view class="filter-label">状态</view> <view class="filter-label">姓名</view>
<view class="filter-control"> <view class="filter-control">
<picker <input
:range="statusOptions" v-model="searchName"
:value="statusIndex" class="search-input"
@change="onStatusChange" placeholder="请输入姓名"
class="picker" @input="onSearch"
> />
<view class="picker-text">{{ statusOptions[statusIndex] }}</view>
</picker>
</view> </view>
</view> </view>
<view class="filter-item"> <view class="filter-item">
<view class="filter-label">搜索</view> <view class="filter-label">手机号</view>
<view class="filter-control"> <view class="filter-control">
<input <input
v-model="searchKeyword" v-model="searchPhone"
class="search-input" class="search-input"
placeholder="请输入姓名或手机号" placeholder="请输入手机号"
@input="onSearch" @input="onSearch"
/> />
</view> </view>
@@ -49,25 +45,19 @@
v-for="(student, index) in studentList" v-for="(student, index) in studentList"
:key="index" :key="index"
class="student-item" class="student-item"
@click="goToStudentDetail(student.id)" @click="goToStudentDetail(student.userId)"
> >
<view class="student-avatar"> <view class="student-avatar">
<view class="avatar-placeholder">{{ getInitials(student.name) }}</view> <view class="avatar-placeholder">{{ getInitials(student.userName) }}</view>
</view> </view>
<view class="student-info"> <view class="student-info">
<view class="student-name">{{ student.name }}</view> <view class="student-name flex jc">{{ student.userName }}</view>
<view class="student-meta"> <view class="student-meta">
<view class="meta-item">{{ student.phone }}</view> <view class="meta-item">{{ student.phone }}</view>
<view class="meta-item">{{ student.school }}</view>
<view class="meta-item status-{{ student.statusClass }}">{{ student.status }}</view>
</view> </view>
</view> <view class="student-meta" style="justify-content: space-between;">
<view class="student-actions"> <view class="meta-item">上次登录{{ formatDate(student.rencentlyLoginTime) }}</view>
<view class="action-btn edit-btn" @click.stop="editStudent(student.id)"> <view class="meta-item">注册时间{{ formatDate(student.createTime) }}</view>
编辑
</view>
<view class="action-btn delete-btn" @click.stop="deleteStudent(student.id)">
删除
</view> </view>
</view> </view>
</view> </view>
@@ -83,20 +73,20 @@
<!-- 分页 --> <!-- 分页 -->
<view v-if="studentList.length > 0" class="pagination"> <view v-if="studentList.length > 0" class="pagination">
<view class="page-info"> <view class="page-info">
{{ totalStudents }} 当前第 {{ currentPage }} {{ totalStudents }} 当前第 {{ pageNo }}
</view> </view>
<view class="page-controls"> <view class="page-controls">
<view <view
class="page-btn" class="page-btn"
:class="{ disabled: currentPage === 1 }" :class="{ disabled: pageNo === 1 }"
@click="goToPage(currentPage - 1)" @click="goToPage(pageNo - 1)"
> >
上一页 上一页
</view> </view>
<view <view
class="page-btn" class="page-btn"
:class="{ disabled: currentPage === totalPages }" :class="{ disabled: pageNo === totalPages }"
@click="goToPage(currentPage + 1)" @click="goToPage(pageNo + 1)"
> >
下一页 下一页
</view> </view>
@@ -107,50 +97,17 @@
<script setup> <script setup>
import { ref, computed, onMounted } from "vue" import { ref, computed, onMounted } from "vue"
import { getStudentList } from "@/api/student"
// 筛选条件 // 筛选条件
const statusOptions = ['全部状态', '正常', '禁用'] const searchName = ref('')
const statusIndex = ref(0) const searchPhone = ref('')
const searchKeyword = ref('')
// 学员列表数据 // 学员列表数据
const studentList = ref([ const studentList = ref([])
{
id: 1,
name: '张三',
phone: '13800138001',
school: '北京大学',
status: '正常',
statusClass: 'active'
},
{
id: 2,
name: '李四',
phone: '13800138002',
school: '清华大学',
status: '正常',
statusClass: 'active'
},
{
id: 3,
name: '王五',
phone: '13800138003',
school: '复旦大学',
status: '禁用',
statusClass: 'inactive'
},
{
id: 4,
name: '赵六',
phone: '13800138004',
school: '上海交通大学',
status: '正常',
statusClass: 'active'
}
])
// 分页信息 // 分页信息
const currentPage = ref(1) const pageNo = ref(1)
const totalStudents = ref(100) const totalStudents = ref(100)
const pageSize = ref(10) const pageSize = ref(10)
@@ -161,7 +118,7 @@
onMounted(() => { onMounted(() => {
// 实际项目中应从接口获取学员列表 // 实际项目中应从接口获取学员列表
// loadStudentList() searchStudentList()
}) })
// 返回上一页 // 返回上一页
@@ -169,62 +126,31 @@
uni.navigateBack({ delta: 1 }) uni.navigateBack({ delta: 1 })
} }
// 添加学员 const searchStudentList = async () => {
function addStudent() { const res = await getStudentList({
uni.showToast({ pageNo: pageNo.value,
title: '添加学员功能开发中', pageSize: pageSize.value,
icon: 'none' name: searchName.value,
phone: searchPhone.value
}) })
if (res.code == '0000') {
studentList.value = res.data?.list || []
totalStudents.value = res.data.total || 0
}
} }
// 跳转到学员详情 // 跳转到学员详情
function goToStudentDetail(id) { function goToStudentDetail(userId) {
uni.navigateTo({ uni.navigateTo({
url: `/pages/student/detail?id=${id}` url: `/pages/student/detail?userId=${userId}`
}) })
} }
// 编辑学员
function editStudent(id) {
uni.showToast({
title: '编辑学员功能开发中',
icon: 'none'
})
}
// 删除学员
function deleteStudent(id) {
uni.showModal({
title: '删除学员',
content: '确定要删除该学员吗?',
success: function(res) {
if (res.confirm) {
// 实际项目中应调用接口删除学员
const index = studentList.value.findIndex(item => item.id === id)
if (index !== -1) {
studentList.value.splice(index, 1)
}
uni.showToast({
title: '删除成功',
icon: 'success'
})
}
}
})
}
// 状态变更
function onStatusChange(e) {
const value = e.detail.value
statusIndex.value = value
// 实际项目中应根据状态筛选学员
// filterStudentList()
}
// 搜索 // 搜索
function onSearch() { function onSearch() {
// 实际项目中应根据关键词搜索学员 // 实际项目中应根据关键词搜索学员
// searchStudentList() searchStudentList()
} }
// 跳转到指定页码 // 跳转到指定页码
@@ -232,15 +158,25 @@
if (page < 1 || page > totalPages.value) { if (page < 1 || page > totalPages.value) {
return return
} }
currentPage.value = page pageNo.value = page
// 实际项目中应加载指定页码的学员 // 实际项目中应加载指定页码的学员
// loadStudentPage(page) searchStudentList()
} }
// 获取姓名首字母 // 获取姓名首字母
function getInitials(name) { function getInitials(name) {
return name.charAt(0).toUpperCase() return name.charAt(0).toUpperCase()
} }
// 格式化日期为年月日
function formatDate(dateStr) {
if (!dateStr) return '暂无'
const date = new Date(dateStr)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -298,12 +234,7 @@
text-align: right; text-align: right;
} }
.add-btn {
font-size: 24rpx;
color: #409eff;
font-weight: 600;
cursor: pointer;
}
/* 筛选条件 */ /* 筛选条件 */
.filter-section { .filter-section {
@@ -336,16 +267,7 @@
padding: 16rpx; padding: 16rpx;
} }
.picker {
display: flex;
justify-content: space-between;
align-items: center;
}
.picker-text {
font-size: 24rpx;
color: #303133;
}
.search-input { .search-input {
font-size: 24rpx; font-size: 24rpx;
@@ -423,36 +345,7 @@
color: #606266; color: #606266;
} }
.meta-item.status-active {
color: #67c23a;
}
.meta-item.status-inactive {
color: #909399;
}
.student-actions {
display: flex;
gap: 16rpx;
}
.action-btn {
padding: 8rpx 16rpx;
border-radius: 8rpx;
font-size: 16rpx;
font-weight: 600;
cursor: pointer;
}
.edit-btn {
background-color: #ecf5ff;
color: #409eff;
}
.delete-btn {
background-color: #fef0f0;
color: #f56c6c;
}
/* 空状态 */ /* 空状态 */
.empty-state { .empty-state {
@@ -509,113 +402,53 @@
cursor: not-allowed; cursor: not-allowed;
} }
/* 平板和大屏响应式 */ /* 大屏平板响应式 */
@media screen and (min-width: 768px) {
.student-list-container {
max-width: 1000px;
margin: 0 auto;
width: 100%;
}
.filter-section {
margin: 0 32rpx 24rpx;
padding: 32rpx;
}
.student-list-section {
padding: 0 32rpx;
}
.pagination {
margin: 0 32rpx 32rpx;
padding: 32rpx;
}
.section-title {
font-size: 36rpx;
margin-bottom: 32rpx;
}
.filter-label {
font-size: 26rpx;
}
.picker-text,
.search-input {
font-size: 26rpx;
}
.filter-control {
padding: 20rpx;
}
.student-item {
padding: 32rpx;
}
.student-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50rpx;
margin-right: 32rpx;
}
.avatar-placeholder {
font-size: 36rpx;
}
.student-name {
font-size: 32rpx;
}
.student-meta {
gap: 24rpx;
}
.meta-item {
font-size: 22rpx;
}
.action-btn {
font-size: 18rpx;
padding: 12rpx 24rpx;
}
.page-info {
font-size: 22rpx;
}
.page-btn {
font-size: 22rpx;
padding: 12rpx 24rpx;
}
.add-btn {
font-size: 28rpx;
}
}
/* 大屏设备响应式 */
@media screen and (min-width: 1024px) { @media screen and (min-width: 1024px) {
.student-list-container { .student-list-container {
max-width: 1200px; max-width: 1200px;
} }
.header-title {
.filter-label { font-size: 48rpx;
font-size: 28rpx; }
.back-icon {
font-size: 60rpx;
}
.filter-section {
padding: 40rpx;
}
.filter-label {
font-size: 48rpx;
} }
.picker-text,
.search-input { .search-input {
font-size: 28rpx; font-size: 48rpx;
}
.student-item {
padding: 40rpx;
}
.student-avatar {
width: 200rpx;
height: 200rpx;
border-radius: 100rpx;
margin-right: 40rpx;
}
.avatar-placeholder {
font-size: 80rpx;
} }
.section-title { .section-title {
font-size: 40rpx; margin-top: 40rpx;
font-size: 60rpx;
} }
.student-name { .student-name {
font-size: 34rpx; font-size: 56rpx;
} }
.student-meta { .student-meta {
@@ -623,11 +456,21 @@
} }
.meta-item { .meta-item {
font-size: 24rpx; font-size: 44rpx;
} }
.action-btn { .pagination {
font-size: 20rpx; padding: 40rpx;
.page-info {
font-size: 44rpx;
}
.page-btn {
padding: 16rpx 32rpx;
border-radius: 16rpx;
font-size: 44rpx;
}
} }
} }
</style> </style>

View File

@@ -119,16 +119,6 @@
</view> </view>
</view> </view>
<view v-if="checkPermi(['work:student:info'])" class="feature-card" @click="goToStudentDetail">
<view class="feature-icon notice-icon">
<view class="icon-text">📈</view>
</view>
<view class="feature-info">
<view class="feature-title">学情详情查看</view>
<view class="feature-desc">查看学员学习数据AI充值状态</view>
</view>
</view>
<view v-if="checkPermi(['work:student:analysis'])" class="feature-card" @click="goToLearningAnalysis"> <view v-if="checkPermi(['work:student:analysis'])" class="feature-card" @click="goToLearningAnalysis">
<view class="feature-icon log-icon"> <view class="feature-icon log-icon">
<view class="icon-text">📊</view> <view class="icon-text">📊</view>
@@ -603,57 +593,6 @@
line-height: 1.4; line-height: 1.4;
} }
/* 平板响应式 */
@media screen and (min-width: 768px) {
.work-container {
max-width: 900px;
margin: 0 auto;
padding: 40rpx;
width: 100%;
}
.section {
margin-bottom: 32rpx;
}
.section-title {
font-size: 32rpx;
margin-bottom: 24rpx;
padding-left: 12rpx;
border-left: 8rpx solid #409eff;
}
.feature-row {
flex-direction: row;
flex-wrap: wrap;
gap: 20rpx;
}
.feature-card {
flex: 0 0 calc(50% - 10rpx);
max-width: calc(50% - 10rpx);
padding: 28rpx;
}
.feature-icon {
width: 80rpx;
height: 80rpx;
margin-right: 20rpx;
}
.icon-text {
font-size: 36rpx;
}
.feature-title {
font-size: 28rpx;
}
.feature-desc {
font-size: 22rpx;
}
}
/* 大屏设备响应式 */ /* 大屏设备响应式 */
@media screen and (min-width: 1024px) { @media screen and (min-width: 1024px) {
.work-container { .work-container {