edu/uniapp/src/pages/my_recruitment/my_recruitment.vue

399 lines
11 KiB
Vue
Raw Normal View History

<template>
<page-meta :page-style="$theme.pageStyle">
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
</page-meta>
<view class="my-recruitment min-h-full pb-[env(safe-area-inset-bottom)]">
<view class="px-[30rpx] py-[20rpx]">
<view class="filter-bar flex items-center justify-between">
<view class="filter-item" @click="showTimeFilter = true">
<text :class="timeFilter ? 'text-blue-500' : 'text-gray-600'">
{{ timeFilterText }}
</text>
<u-icon
name="arrow-down"
size="24"
:color="timeFilter ? '#3B82F6' : '#999'"
></u-icon>
</view>
<view class="filter-item" @click="showRegionFilter = true">
<text :class="regionFilter ? 'text-blue-500' : 'text-gray-600'">
{{ regionFilterText }}
</text>
<u-icon
name="arrow-down"
size="24"
:color="regionFilter ? '#3B82F6' : '#999'"
></u-icon>
</view>
<view class="filter-item" @click="toggleSort">
<text :class="sortBy ? 'text-blue-500' : 'text-gray-600'">
{{ sortText }}
</text>
<u-icon
:name="sortOrder === 'desc' ? 'arrow-down' : 'arrow-up'"
size="24"
:color="sortBy ? '#3B82F6' : '#999'"
></u-icon>
</view>
</view>
</view>
<view class="px-[30rpx]">
<view class="text-sm text-gray-500 mb-[20rpx]"> {{ total }} 条记录 </view>
<view v-if="loading" class="flex justify-center py-[100rpx]">
<u-loading mode="circle"></u-loading>
</view>
<view
v-else-if="list.length === 0"
class="empty-state flex flex-col items-center py-[100rpx]"
>
<u-icon name="inbox" size="120" color="#E5E7EB"></u-icon>
<view class="text-gray-400 mt-[30rpx]">暂无招生数据</view>
</view>
<view v-else class="list">
<view
v-for="(item, index) in list"
:key="index"
class="list-item bg-white rounded-lg p-[30rpx] mb-[20rpx]"
>
<view class="flex items-center justify-between mb-[20rpx]">
<view class="flex items-center">
<view
class="avatar bg-blue-100 text-blue-500 w-[80rpx] h-[80rpx] rounded-full flex items-center justify-center text-xl font-medium"
>
{{ item.name ? item.name.charAt(0) : '?' }}
</view>
<view class="ml-[20rpx]">
<view class="text-lg font-medium">{{ item.name }}</view>
<view class="text-sm text-gray-500 mt-[5rpx]">{{
item.gender === 1 ? '男' : '女'
}}</view>
</view>
</view>
<view class="text-right">
<view class="text-sm text-gray-500">{{ item.majorName }}</view>
<view class="text-xs text-gray-400 mt-[5rpx]">{{
item.createTime
}}</view>
</view>
</view>
<view class="info-grid grid grid-cols-2 gap-[20rpx]">
<view class="info-item">
<view class="text-xs text-gray-400">身份证号</view>
<view class="text-sm mt-[5rpx]">{{ formatIdCard(item.idCard) }}</view>
</view>
<view class="info-item">
<view class="text-xs text-gray-400">毕业学校</view>
<view class="text-sm mt-[5rpx]">{{ item.previousSchool }}</view>
</view>
<view class="info-item">
<view class="text-xs text-gray-400">中考成绩</view>
<view class="text-sm mt-[5rpx]">{{ item.highSchoolScore }}</view>
</view>
<view class="info-item">
<view class="text-xs text-gray-400">身高/体重</view>
<view class="text-sm mt-[5rpx]"
>{{ item.height }}cm / {{ item.weight }}kg</view
>
</view>
</view>
<view class="status-bar mt-[20rpx] flex items-center justify-between">
<view class="flex items-center">
<view class="status-dot" :class="getStatusClass(item.status)"></view>
<view
class="text-sm ml-[10rpx]"
:class="getStatusTextClass(item.status)"
>
{{ getStatusText(item.status) }}
</view>
</view>
<view class="text-xs text-gray-400"> 鞋码: {{ item.shoeSize }} </view>
</view>
</view>
</view>
</view>
<u-action-sheet
v-model="showTimeFilter"
:list="timeFilterList"
@click="handleTimeFilter"
></u-action-sheet>
<u-action-sheet
v-model="showRegionFilter"
:list="regionFilterList"
@click="handleRegionFilter"
></u-action-sheet>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getRecruitmentList } from '@/api/app'
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
const list = ref<any[]>([])
const loading = ref(false)
const total = ref(0)
const showTimeFilter = ref(false)
const showRegionFilter = ref(false)
const timeFilter = ref('')
const regionFilter = ref('')
const sortBy = ref('createTime')
const sortOrder = ref<'asc' | 'desc'>('desc')
const timeFilterText = computed(() => {
const map: Record<string, string> = {
'': '全部时间',
today: '今天',
week: '本周',
month: '本月',
year: '今年'
}
return map[timeFilter.value] || '全部时间'
})
const regionFilterText = computed(() => {
const map: Record<string, string> = {
'': '全部地区',
north: '北部地区',
south: '南部地区',
east: '东部地区',
west: '西部地区'
}
return map[regionFilter.value] || '全部地区'
})
const sortText = computed(() => {
const map: Record<string, string> = {
createTime: '时间',
highSchoolScore: '成绩',
height: '身高'
}
return map[sortBy.value] || '排序'
})
const timeFilterList = [
{ text: '全部时间' },
{ text: '今天' },
{ text: '本周' },
{ text: '本月' },
{ text: '今年' }
]
const regionFilterList = [
{ text: '全部地区' },
{ text: '北部地区' },
{ text: '南部地区' },
{ text: '东部地区' },
{ text: '西部地区' }
]
const formatIdCard = (idCard: string) => {
if (!idCard) return ''
return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2')
}
const getStatusClass = (status: number) => {
const map: Record<number, string> = {
0: 'status-pending',
1: 'status-approved',
2: 'status-rejected'
}
return map[status] || 'status-pending'
}
const getStatusTextClass = (status: number) => {
const map: Record<number, string> = {
0: 'text-yellow-500',
1: 'text-green-500',
2: 'text-red-500'
}
return map[status] || 'text-yellow-500'
}
const getStatusText = (status: number) => {
const map: Record<number, string> = {
0: '待审核',
1: '已通过',
2: '已拒绝'
}
return map[status] || '待审核'
}
const handleTimeFilter = (index: number) => {
const valueMap = ['', 'today', 'week', 'month', 'year']
timeFilter.value = valueMap[index] || ''
loadList()
}
const handleRegionFilter = (index: number) => {
const valueMap = ['', 'north', 'south', 'east', 'west']
regionFilter.value = valueMap[index] || ''
loadList()
}
const toggleSort = () => {
if (sortBy.value === 'createTime') {
sortBy.value = 'highSchoolScore'
} else if (sortBy.value === 'highSchoolScore') {
sortBy.value = 'height'
} else {
sortBy.value = 'createTime'
}
loadList()
}
const loadList = async () => {
if (!userInfo.value || !userInfo.value.teacherId) {
return
}
loading.value = true
try {
const res = await getRecruitmentList({
teacherId: userInfo.value.teacherId,
timeFilter: timeFilter.value,
regionFilter: regionFilter.value,
sortBy: sortBy.value,
sortOrder: sortOrder.value
})
if (res.code === 1 && res.data) {
list.value = res.data.list || []
total.value = res.data.total || 0
}
} catch (error) {
console.error('获取招生列表失败:', error)
uni.$u.toast('获取数据失败')
} finally {
loading.value = false
}
}
onMounted(() => {
loadList()
})
onLoad(() => {
loadList()
})
</script>
<style lang="scss" scoped>
.my-recruitment {
background-color: #f5f5f5;
min-height: 100vh;
}
.filter-bar {
background: white;
padding: 20rpx;
border-radius: 12rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.filter-item {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 28rpx;
padding: 10rpx 20rpx;
border-radius: 8rpx;
transition: all 0.2s ease;
&:active {
background: #f5f5f5;
}
}
.list-item {
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}
.avatar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.info-grid {
padding: 20rpx 0;
border-top: 1rpx solid #f0f0f0;
border-bottom: 1rpx solid #f0f0f0;
}
.info-item {
margin-bottom: 10rpx;
&:last-child {
margin-bottom: 0;
}
}
.status-bar {
padding-top: 10rpx;
}
.status-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
&.status-pending {
background-color: #f59e0b;
}
&.status-approved {
background-color: #10b981;
}
&.status-rejected {
background-color: #ef4444;
}
}
.empty-state {
background: white;
border-radius: 12rpx;
margin-top: 20rpx;
}
.text-blue-500 {
color: #3b82f6;
}
.text-gray-600 {
color: #666;
}
.text-gray-400 {
color: #999;
}
.text-gray-500 {
color: #888;
}
.text-yellow-500 {
color: #f59e0b;
}
.text-green-500 {
color: #10b981;
}
.text-red-500 {
color: #ef4444;
}
</style>