Compare commits

..

No commits in common. "b6e46ba21eb04e6dc06732887a25fb21bed80aa2" and "0777d2901ab589c9c2ce9baaeeaf33b9230053b2" have entirely different histories.

5 changed files with 477 additions and 190 deletions

View File

@ -28,8 +28,17 @@
<!-- 操作栏 --> <!-- 操作栏 -->
<div class="action-bar"> <div class="action-bar">
<div class="search-box"> <div class="search-box">
<a-input-search v-model:value="searchKeyword" placeholder="搜索班级名称" style="width: 250px;" @search="handleSearch" <a-input-search
allow-clear /> v-model:value="searchKeyword"
placeholder="搜索班级名称"
style="width: 250px;"
@search="handleSearch"
allow-clear
>
<template #prefix>
<SearchOutlined style="color: #B2BEC3;" />
</template>
</a-input-search>
</div> </div>
<a-button type="primary" class="add-btn" @click="showAddModal"> <a-button type="primary" class="add-btn" @click="showAddModal">
<PlusOutlined class="btn-icon" /> <PlusOutlined class="btn-icon" />
@ -39,7 +48,12 @@
<!-- 班级卡片网格 --> <!-- 班级卡片网格 -->
<div class="class-grid" v-if="!loading && classes.length > 0"> <div class="class-grid" v-if="!loading && classes.length > 0">
<div v-for="cls in classes" :key="cls.id" class="class-card" :class="'grade-' + getGradeKey(cls.grade)"> <div
v-for="cls in classes"
:key="cls.id"
class="class-card"
:class="'grade-' + getGradeKey(cls.grade)"
>
<div class="card-header"> <div class="card-header">
<div class="class-icon"> <div class="class-icon">
<component :is="getGradeIcon(cls.grade)" class="icon-component" /> <component :is="getGradeIcon(cls.grade)" class="icon-component" />
@ -108,7 +122,11 @@
<EditOutlined /> <EditOutlined />
编辑 编辑
</a-button> </a-button>
<a-popconfirm title="确定要删除这个班级吗?" :disabled="cls.studentCount > 0" @confirm="handleDelete(cls.id)"> <a-popconfirm
title="确定要删除这个班级吗?"
:disabled="cls.studentCount > 0"
@confirm="handleDelete(cls.id)"
>
<a-tooltip v-if="cls.studentCount > 0" title="班级内有学生,无法删除"> <a-tooltip v-if="cls.studentCount > 0" title="班级内有学生,无法删除">
<a-button type="link" size="small" disabled class="disabled-btn"> <a-button type="link" size="small" disabled class="disabled-btn">
<DeleteOutlined /> <DeleteOutlined />
@ -142,9 +160,21 @@
</div> </div>
<!-- 添加/编辑班级模态框 --> <!-- 添加/编辑班级模态框 -->
<a-modal v-model:open="modalVisible" :title="isEdit ? modalEditTitle : modalAddTitle" @ok="handleModalOk" <a-modal
@cancel="handleModalCancel" :confirm-loading="submitting" :width="480"> v-model:open="modalVisible"
<a-form ref="formRef" :model="formState" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }"> :title="isEdit ? modalEditTitle : modalAddTitle"
@ok="handleModalOk"
@cancel="handleModalCancel"
:confirm-loading="submitting"
:width="480"
>
<a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
>
<a-form-item label="班级名称" name="name"> <a-form-item label="班级名称" name="name">
<a-input v-model:value="formState.name" placeholder="请输入班级名称"> <a-input v-model:value="formState.name" placeholder="请输入班级名称">
<template #prefix> <template #prefix>
@ -175,7 +205,12 @@
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label="班主任" name="teacherId"> <a-form-item label="班主任" name="teacherId">
<a-select v-model:value="formState.teacherId" placeholder="请选择班主任" :loading="teachersLoading" allow-clear> <a-select
v-model:value="formState.teacherId"
placeholder="请选择班主任"
:loading="teachersLoading"
allow-clear
>
<a-select-option v-for="teacher in teachers" :key="teacher.id" :value="teacher.id"> <a-select-option v-for="teacher in teachers" :key="teacher.id" :value="teacher.id">
{{ teacher.name }} {{ teacher.name }}
</a-select-option> </a-select-option>
@ -185,7 +220,12 @@
</a-modal> </a-modal>
<!-- 班级学生列表模态框 --> <!-- 班级学生列表模态框 -->
<a-modal v-model:open="studentsModalVisible" :title="studentsModalTitle" :footer="null" width="700px"> <a-modal
v-model:open="studentsModalVisible"
:title="studentsModalTitle"
:footer="null"
width="700px"
>
<div class="students-modal-content"> <div class="students-modal-content">
<div class="class-info-header"> <div class="class-info-header">
<div class="class-emoji"> <div class="class-emoji">
@ -204,7 +244,11 @@
</div> </div>
<div class="students-list" v-if="!studentsLoading && classStudents.length > 0"> <div class="students-list" v-if="!studentsLoading && classStudents.length > 0">
<div v-for="student in classStudents" :key="student.id" class="student-item"> <div
v-for="student in classStudents"
:key="student.id"
class="student-item"
>
<div class="student-avatar" :class="student.gender === '男' ? 'boy' : 'girl'"> <div class="student-avatar" :class="student.gender === '男' ? 'boy' : 'girl'">
<BoyOutlined v-if="student.gender === ''" class="student-gender-icon" /> <BoyOutlined v-if="student.gender === ''" class="student-gender-icon" />
<GirlOutlined v-else class="student-gender-icon" /> <GirlOutlined v-else class="student-gender-icon" />
@ -232,22 +276,35 @@
</div> </div>
<div class="pagination-wrapper" v-if="studentsPagination.total > studentsPagination.pageSize"> <div class="pagination-wrapper" v-if="studentsPagination.total > studentsPagination.pageSize">
<a-pagination v-model:current="studentsPagination.current" v-model:pageSize="studentsPagination.pageSize" <a-pagination
:total="studentsPagination.total" :show-total="(total: number) => `共 ${total} 条`" size="small" v-model:current="studentsPagination.current"
@change="handleStudentsPageChange" /> v-model:pageSize="studentsPagination.pageSize"
:total="studentsPagination.total"
:show-total="(total: number) => `共 ${total} 条`"
size="small"
@change="handleStudentsPageChange"
/>
</div> </div>
</div> </div>
</a-modal> </a-modal>
<!-- 班级教师管理模态框 --> <!-- 班级教师管理模态框 -->
<a-modal v-model:open="teachersModalVisible" :title="`管理 ${currentClass?.name || ''} 教师团队`" :footer="null" <a-modal
width="600px"> v-model:open="teachersModalVisible"
:title="`管理 ${currentClass?.name || ''} 教师团队`"
:footer="null"
width="600px"
>
<div class="teachers-modal-content"> <div class="teachers-modal-content">
<!-- 添加教师表单 --> <!-- 添加教师表单 -->
<div class="add-teacher-form"> <div class="add-teacher-form">
<div class="form-row"> <div class="form-row">
<a-select v-model:value="teacherFormState.teacherId" placeholder="选择教师" style="width: 150px;" <a-select
:loading="teachersLoading"> v-model:value="teacherFormState.teacherId"
placeholder="选择教师"
style="width: 150px;"
:loading="teachersLoading"
>
<a-select-option v-for="teacher in teachers" :key="teacher.id" :value="teacher.id"> <a-select-option v-for="teacher in teachers" :key="teacher.id" :value="teacher.id">
{{ teacher.name }} {{ teacher.name }}
</a-select-option> </a-select-option>
@ -269,14 +326,20 @@
<div v-for="teacher in classTeachers" :key="teacher.teacherId" class="teacher-item"> <div v-for="teacher in classTeachers" :key="teacher.teacherId" class="teacher-item">
<div class="teacher-info"> <div class="teacher-info">
<span class="teacher-name">{{ teacher.teacherName }}</span> <span class="teacher-name">{{ teacher.teacherName }}</span>
<a-select v-model:value="teacher.role" size="small" style="width: 80px; margin-left: 8px;" <a-select
@change="handleUpdateTeacherRole(teacher)"> v-model:value="teacher.role"
size="small"
style="width: 80px; margin-left: 8px;"
@change="handleUpdateTeacherRole(teacher)"
>
<a-select-option value="MAIN">主班</a-select-option> <a-select-option value="MAIN">主班</a-select-option>
<a-select-option value="ASSIST">配班</a-select-option> <a-select-option value="ASSIST">配班</a-select-option>
<a-select-option value="CARE">保育员</a-select-option> <a-select-option value="CARE">保育员</a-select-option>
</a-select> </a-select>
<a-checkbox v-model:checked="teacher.isPrimary" <a-checkbox
@change="handleUpdateTeacherRole(teacher)">班主任</a-checkbox> v-model:checked="teacher.isPrimary"
@change="handleUpdateTeacherRole(teacher)"
>班主任</a-checkbox>
</div> </div>
<a-button type="link" danger size="small" @click="handleRemoveTeacher(teacher.teacherId)"> <a-button type="link" danger size="small" @click="handleRemoveTeacher(teacher.teacherId)">
移除 移除
@ -298,6 +361,7 @@ import {
HomeOutlined, HomeOutlined,
BankOutlined, BankOutlined,
PlusOutlined, PlusOutlined,
SearchOutlined,
EditOutlined, EditOutlined,
DeleteOutlined, DeleteOutlined,
TeamOutlined, TeamOutlined,
@ -769,6 +833,10 @@ onMounted(() => {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
} }
.search-box :deep(.ant-input-affix-wrapper) {
border-radius: 12px;
border: 2px solid #F0F0F0;
}
.search-box :deep(.ant-input-affix-wrapper:hover) { .search-box :deep(.ant-input-affix-wrapper:hover) {
border-color: #4facfe; border-color: #4facfe;
@ -813,17 +881,9 @@ onMounted(() => {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
} }
.class-card.grade-small { .class-card.grade-small { border-top: 4px solid #43e97b; }
border-top: 4px solid #43e97b; .class-card.grade-middle { border-top: 4px solid #4facfe; }
} .class-card.grade-big { border-top: 4px solid #FF8C42; }
.class-card.grade-middle {
border-top: 4px solid #4facfe;
}
.class-card.grade-big {
border-top: 4px solid #FF8C42;
}
.class-card .card-header { .class-card .card-header {
display: flex; display: flex;
@ -842,17 +902,9 @@ onMounted(() => {
justify-content: center; justify-content: center;
} }
.class-card.grade-small .class-icon { .class-card.grade-small .class-icon { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); .class-card.grade-middle .class-icon { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
} .class-card.grade-big .class-icon { background: linear-gradient(135deg, #FF8C42 0%, #FFB347 100%); }
.class-card.grade-middle .class-icon {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.class-card.grade-big .class-icon {
background: linear-gradient(135deg, #FF8C42 0%, #FFB347 100%);
}
.icon-component { .icon-component {
font-size: 24px; font-size: 24px;
@ -880,20 +932,9 @@ onMounted(() => {
font-weight: 500; font-weight: 500;
} }
.grade-badge.grade-small { .grade-badge.grade-small { background: #E8F5E9; color: #43A047; }
background: #E8F5E9; .grade-badge.grade-middle { background: #E3F2FD; color: #1976D2; }
color: #43A047; .grade-badge.grade-big { background: #FFF8F0; color: #FF8C42; }
}
.grade-badge.grade-middle {
background: #E3F2FD;
color: #1976D2;
}
.grade-badge.grade-big {
background: #FFF8F0;
color: #FF8C42;
}
.card-body { .card-body {
padding: 16px 20px; padding: 16px 20px;

View File

@ -30,16 +30,30 @@
<div class="grade-tabs"> <div class="grade-tabs">
<span class="tab-label">年级筛选</span> <span class="tab-label">年级筛选</span>
<div class="tab-buttons"> <div class="tab-buttons">
<div v-for="grade in gradeOptions" :key="grade.value" class="grade-tab" <div
:class="{ active: selectedGrade === grade.value }" @click="selectedGrade = grade.value"> v-for="grade in gradeOptions"
:key="grade.value"
class="grade-tab"
:class="{ active: selectedGrade === grade.value }"
@click="selectedGrade = grade.value"
>
{{ grade.label }} {{ grade.label }}
</div> </div>
</div> </div>
</div> </div>
<div class="action-row"> <div class="action-row">
<div class="search-box"> <div class="search-box">
<a-input-search v-model:value="searchKeyword" placeholder="搜索课程名称" style="width: 280px;" <a-input-search
@search="handleSearch" allow-clear /> v-model:value="searchKeyword"
placeholder="搜索课程名称"
style="width: 280px;"
@search="handleSearch"
allow-clear
>
<template #prefix>
<SearchOutlined style="color: #B2BEC3;" />
</template>
</a-input-search>
</div> </div>
<a-button type="primary" class="auth-btn" @click="showAuthModal"> <a-button type="primary" class="auth-btn" @click="showAuthModal">
<StarFilled class="btn-icon" /> <StarFilled class="btn-icon" />
@ -50,8 +64,12 @@
<!-- 课程卡片网格 --> <!-- 课程卡片网格 -->
<div class="course-grid" v-if="!loading && filteredCourses.length > 0"> <div class="course-grid" v-if="!loading && filteredCourses.length > 0">
<div v-for="course in filteredCourses" :key="course.id" class="course-card" <div
:class="{ 'unauthorized': !course.authorized }"> v-for="course in filteredCourses"
:key="course.id"
class="course-card"
:class="{ 'unauthorized': !course.authorized }"
>
<div class="card-cover"> <div class="card-cover">
<div class="cover-placeholder" v-if="!course.pictureUrl"> <div class="cover-placeholder" v-if="!course.pictureUrl">
<ReadOutlined class="cover-icon" /> <ReadOutlined class="cover-icon" />
@ -69,12 +87,20 @@
<p class="course-book">{{ course.pictureBookName }}</p> <p class="course-book">{{ course.pictureBookName }}</p>
<div class="course-tags"> <div class="course-tags">
<span v-for="tag in course.gradeTags.slice(0, 2)" :key="tag" class="tag grade" <span
:style="getGradeTagStyle(translateGradeTag(tag))"> v-for="tag in course.gradeTags.slice(0, 2)"
:key="tag"
class="tag grade"
:style="getGradeTagStyle(translateGradeTag(tag))"
>
{{ translateGradeTag(tag) }} {{ translateGradeTag(tag) }}
</span> </span>
<span v-for="tag in course.domainTags.slice(0, 2)" :key="tag" class="tag domain" <span
:style="getDomainTagStyle(translateDomainTag(tag))"> v-for="tag in course.domainTags.slice(0, 2)"
:key="tag"
class="tag domain"
:style="getDomainTagStyle(translateDomainTag(tag))"
>
{{ translateDomainTag(tag) }} {{ translateDomainTag(tag) }}
</span> </span>
</div> </div>
@ -96,12 +122,21 @@
<FileTextOutlined /> <FileTextOutlined />
详情 详情
</a-button> </a-button>
<a-button v-if="!course.authorized" type="link" size="small" class="auth-action" <a-button
@click="handleAuthorize(course)"> v-if="!course.authorized"
type="link"
size="small"
class="auth-action"
@click="handleAuthorize(course)"
>
<StarFilled /> <StarFilled />
授权 授权
</a-button> </a-button>
<a-popconfirm v-else title="确定要取消授权吗?" @confirm="handleRevoke(course)"> <a-popconfirm
v-else
title="确定要取消授权吗?"
@confirm="handleRevoke(course)"
>
<a-button type="link" size="small" danger> <a-button type="link" size="small" danger>
<StopOutlined /> <StopOutlined />
取消 取消
@ -129,8 +164,13 @@
</div> </div>
<!-- 授权课程模态框 --> <!-- 授权课程模态框 -->
<a-modal v-model:open="authModalVisible" width="800px" class="auth-modal" @ok="handleAuthModalOk" <a-modal
@cancel="authModalVisible = false"> v-model:open="authModalVisible"
width="800px"
class="auth-modal"
@ok="handleAuthModalOk"
@cancel="authModalVisible = false"
>
<template #title> <template #title>
<span class="modal-title"> <span class="modal-title">
<StarFilled class="modal-title-icon" /> <StarFilled class="modal-title-icon" />
@ -139,12 +179,26 @@
</template> </template>
<div class="auth-content"> <div class="auth-content">
<div class="auth-search"> <div class="auth-search">
<a-input-search v-model:value="searchKeyword" placeholder="输入课程名称搜索..." @search="searchCourses" size="large" /> <a-input-search
v-model:value="searchKeyword"
placeholder="输入课程名称搜索..."
@search="searchCourses"
size="large"
>
<template #prefix>
<SearchOutlined />
</template>
</a-input-search>
</div> </div>
<div class="available-courses" v-if="!authLoading && availableCourses.length > 0"> <div class="available-courses" v-if="!authLoading && availableCourses.length > 0">
<div v-for="course in availableCourses" :key="course.id" class="available-course-item" <div
:class="{ 'selected': selectedCourseIds.includes(course.id) }" @click="toggleCourseSelection(course.id)"> v-for="course in availableCourses"
:key="course.id"
class="available-course-item"
:class="{ 'selected': selectedCourseIds.includes(course.id) }"
@click="toggleCourseSelection(course.id)"
>
<div class="course-checkbox"> <div class="course-checkbox">
<CheckCircleOutlined v-if="selectedCourseIds.includes(course.id)" class="checkbox-check" /> <CheckCircleOutlined v-if="selectedCourseIds.includes(course.id)" class="checkbox-check" />
</div> </div>
@ -186,6 +240,7 @@
import { ref, reactive, computed, onMounted } from 'vue'; import { ref, reactive, computed, onMounted } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { import {
SearchOutlined,
BookOutlined, BookOutlined,
ReadOutlined, ReadOutlined,
StarFilled, StarFilled,
@ -574,6 +629,10 @@ onMounted(() => {
gap: 12px; gap: 12px;
} }
.search-box :deep(.ant-input-affix-wrapper) {
border-radius: 12px;
border: 2px solid #F0F0F0;
}
.search-box :deep(.ant-input-affix-wrapper:hover) { .search-box :deep(.ant-input-affix-wrapper:hover) {
border-color: #43e97b; border-color: #43e97b;
@ -798,6 +857,9 @@ onMounted(() => {
gap: 20px; gap: 20px;
} }
.auth-search :deep(.ant-input-affix-wrapper) {
border-radius: 12px;
}
.available-courses { .available-courses {
display: grid; display: grid;

View File

@ -28,8 +28,17 @@
<!-- 操作栏 --> <!-- 操作栏 -->
<div class="action-bar"> <div class="action-bar">
<div class="search-box"> <div class="search-box">
<a-input-search v-model:value="searchKeyword" placeholder="搜索家长姓名/手机号/账号" style="width: 280px;" <a-input-search
@search="handleSearch" allow-clear /> v-model:value="searchKeyword"
placeholder="搜索家长姓名/手机号/账号"
style="width: 280px;"
@search="handleSearch"
allow-clear
>
<template #prefix>
<SearchOutlined style="color: #B2BEC3;" />
</template>
</a-input-search>
</div> </div>
<a-button type="primary" class="add-btn" @click="showAddModal"> <a-button type="primary" class="add-btn" @click="showAddModal">
<PlusOutlined class="btn-icon" /> <PlusOutlined class="btn-icon" />
@ -39,8 +48,12 @@
<!-- 家长卡片列表 --> <!-- 家长卡片列表 -->
<div class="parent-grid" v-if="!loading && parents.length > 0"> <div class="parent-grid" v-if="!loading && parents.length > 0">
<div v-for="parent in parents" :key="parent.id" class="parent-card" <div
:class="{ 'inactive': parent.status !== 'ACTIVE' }"> v-for="parent in parents"
:key="parent.id"
class="parent-card"
:class="{ 'inactive': parent.status !== 'ACTIVE' }"
>
<div class="card-header"> <div class="card-header">
<div class="parent-avatar"> <div class="parent-avatar">
<IdcardOutlined class="avatar-icon" /> <IdcardOutlined class="avatar-icon" />
@ -84,7 +97,10 @@
<a-button type="link" size="small" @click="handleResetPassword(parent)"> <a-button type="link" size="small" @click="handleResetPassword(parent)">
<KeyOutlined /> 重置 <KeyOutlined /> 重置
</a-button> </a-button>
<a-popconfirm title="确定要删除这位家长吗?" @confirm="handleDelete(parent.id)"> <a-popconfirm
title="确定要删除这位家长吗?"
@confirm="handleDelete(parent.id)"
>
<a-button type="link" size="small" danger> <a-button type="link" size="small" danger>
<DeleteOutlined /> 删除 <DeleteOutlined /> 删除
</a-button> </a-button>
@ -110,14 +126,26 @@
<!-- 分页 --> <!-- 分页 -->
<div class="pagination-wrapper" v-if="parents.length > 0"> <div class="pagination-wrapper" v-if="parents.length > 0">
<a-pagination v-model:current="pagination.current" v-model:pageSize="pagination.pageSize" <a-pagination
:total="pagination.total" :show-size-changer="true" :show-total="(total: number) => `共 ${total} 条`" v-model:current="pagination.current"
@change="handlePageChange" /> v-model:pageSize="pagination.pageSize"
:total="pagination.total"
:show-size-changer="true"
:show-total="(total: number) => `共 ${total} 条`"
@change="handlePageChange"
/>
</div> </div>
<!-- 添加/编辑家长模态框 --> <!-- 添加/编辑家长模态框 -->
<a-modal v-model:open="modalVisible" :title="isEdit ? '编辑家长' : '添加家长'" @ok="handleModalOk" <a-modal
@cancel="handleModalCancel" :confirm-loading="submitting" :width="520" class="parent-modal"> v-model:open="modalVisible"
:title="isEdit ? '编辑家长' : '添加家长'"
@ok="handleModalOk"
@cancel="handleModalCancel"
:confirm-loading="submitting"
:width="520"
class="parent-modal"
>
<template #title> <template #title>
<span class="modal-title"> <span class="modal-title">
<EditOutlined v-if="isEdit" class="modal-title-icon" /> <EditOutlined v-if="isEdit" class="modal-title-icon" />
@ -125,47 +153,53 @@
{{ isEdit ? '编辑家长' : '添加家长' }} {{ isEdit ? '编辑家长' : '添加家长' }}
</span> </span>
</template> </template>
<a-form ref="formRef" :model="formState" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }"> <a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
>
<a-form-item label="姓名" name="name"> <a-form-item label="姓名" name="name">
<a-input v-model:value="formState.name" placeholder="请输入家长姓名"> <a-input v-model:value="formState.name" placeholder="请输入家长姓名">
<template #prefix> <template #prefix><UserOutlined style="color: #B2BEC3;" /></template>
<UserOutlined style="color: #B2BEC3;" />
</template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item label="手机号" name="phone"> <a-form-item label="手机号" name="phone">
<a-input v-model:value="formState.phone" placeholder="请输入手机号"> <a-input v-model:value="formState.phone" placeholder="请输入手机号">
<template #prefix> <template #prefix><PhoneOutlined style="color: #B2BEC3;" /></template>
<PhoneOutlined style="color: #B2BEC3;" />
</template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item label="邮箱" name="email"> <a-form-item label="邮箱" name="email">
<a-input v-model:value="formState.email" placeholder="请输入邮箱(可选)"> <a-input v-model:value="formState.email" placeholder="请输入邮箱(可选)">
<template #prefix> <template #prefix><MailOutlined style="color: #B2BEC3;" /></template>
<MailOutlined style="color: #B2BEC3;" />
</template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item label="登录账号" name="loginAccount"> <a-form-item label="登录账号" name="loginAccount">
<a-input v-model:value="formState.loginAccount" placeholder="请输入登录账号" :disabled="isEdit"> <a-input
<template #prefix> v-model:value="formState.loginAccount"
<KeyOutlined style="color: #B2BEC3;" /> placeholder="请输入登录账号"
</template> :disabled="isEdit"
>
<template #prefix><KeyOutlined style="color: #B2BEC3;" /></template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item v-if="!isEdit" label="密码" name="password"> <a-form-item v-if="!isEdit" label="密码" name="password">
<a-input-password v-model:value="formState.password" placeholder="请输入密码默认123456"> <a-input-password v-model:value="formState.password" placeholder="请输入密码默认123456">
<template #prefix> <template #prefix><LockOutlined style="color: #B2BEC3;" /></template>
<LockOutlined style="color: #B2BEC3;" />
</template>
</a-input-password> </a-input-password>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
<!-- 管理孩子模态框 --> <!-- 管理孩子模态框 -->
<a-modal v-model:open="childrenModalVisible" title="管理关联孩子" :width="650" :footer="null" class="children-modal"> <a-modal
v-model:open="childrenModalVisible"
title="管理关联孩子"
:width="650"
:footer="null"
class="children-modal"
>
<template #title> <template #title>
<span class="modal-title"> <span class="modal-title">
<TeamOutlined class="modal-title-icon" /> <TeamOutlined class="modal-title-icon" />
@ -185,7 +219,10 @@
<div class="list-header"> <div class="list-header">
<span>已关联孩子 ({{ parentChildren.length }})</span> <span>已关联孩子 ({{ parentChildren.length }})</span>
</div> </div>
<a-list :data-source="parentChildren" :loading="childrenLoading"> <a-list
:data-source="parentChildren"
:loading="childrenLoading"
>
<template #renderItem="{ item }"> <template #renderItem="{ item }">
<a-list-item> <a-list-item>
<a-list-item-meta> <a-list-item-meta>
@ -203,7 +240,10 @@
</template> </template>
</a-list-item-meta> </a-list-item-meta>
<template #actions> <template #actions>
<a-popconfirm title="确定要解除关联吗?" @confirm="handleRemoveChild(item.id)"> <a-popconfirm
title="确定要解除关联吗?"
@confirm="handleRemoveChild(item.id)"
>
<a-button type="link" size="small" danger> <a-button type="link" size="small" danger>
<DisconnectOutlined /> 解除 <DisconnectOutlined /> 解除
</a-button> </a-button>
@ -219,8 +259,13 @@
</a-modal> </a-modal>
<!-- 选择学生弹窗 --> <!-- 选择学生弹窗 -->
<a-modal v-model:open="selectStudentModalVisible" title="选择孩子" :width="800" :footer="null" <a-modal
class="select-student-modal"> v-model:open="selectStudentModalVisible"
title="选择孩子"
:width="800"
:footer="null"
class="select-student-modal"
>
<template #title> <template #title>
<span class="modal-title"> <span class="modal-title">
<UserAddOutlined class="modal-title-icon" /> <UserAddOutlined class="modal-title-icon" />
@ -230,10 +275,20 @@
<!-- 搜索和筛选 --> <!-- 搜索和筛选 -->
<div class="select-search-bar"> <div class="select-search-bar">
<a-input-search v-model:value="studentSearchKeyword" placeholder="搜索学生姓名" style="width: 240px;" <a-input-search
@search="handleStudentSearch" allow-clear /> v-model:value="studentSearchKeyword"
<a-select v-model:value="studentClassFilter" placeholder="按班级筛选" style="width: 160px;" allow-clear placeholder="搜索学生姓名"
@change="handleStudentSearch"> style="width: 240px;"
@search="handleStudentSearch"
allow-clear
/>
<a-select
v-model:value="studentClassFilter"
placeholder="按班级筛选"
style="width: 160px;"
allow-clear
@change="handleStudentSearch"
>
<a-select-option v-for="cls in classOptions" :key="cls.id" :value="cls.id"> <a-select-option v-for="cls in classOptions" :key="cls.id" :value="cls.id">
{{ cls.name }} {{ cls.name }}
</a-select-option> </a-select-option>
@ -241,9 +296,17 @@
</div> </div>
<!-- 学生表格 --> <!-- 学生表格 -->
<a-table :columns="studentTableColumns" :data-source="studentTableData" :loading="studentsLoading" <a-table
:pagination="studentPagination" :row-selection="studentRowSelection" row-key="id" size="small" :columns="studentTableColumns"
@change="handleStudentTableChange" style="margin-top: 16px;"> :data-source="studentTableData"
:loading="studentsLoading"
:pagination="studentPagination"
:row-selection="studentRowSelection"
row-key="id"
size="small"
@change="handleStudentTableChange"
style="margin-top: 16px;"
>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'gender'"> <template v-if="column.dataIndex === 'gender'">
{{ record.gender === 'MALE' ? '男' : record.gender === 'FEMALE' ? '女' : '-' }} {{ record.gender === 'MALE' ? '男' : record.gender === 'FEMALE' ? '女' : '-' }}
@ -275,7 +338,12 @@
</a-modal> </a-modal>
<!-- 重置密码确认模态框 --> <!-- 重置密码确认模态框 -->
<a-modal v-model:open="resetPasswordVisible" @ok="confirmResetPassword" :confirm-loading="resetting" :width="400"> <a-modal
v-model:open="resetPasswordVisible"
@ok="confirmResetPassword"
:confirm-loading="resetting"
:width="400"
>
<template #title> <template #title>
<span class="modal-title"> <span class="modal-title">
<KeyOutlined class="modal-title-icon" /> <KeyOutlined class="modal-title-icon" />
@ -301,6 +369,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'; import { ref, reactive, computed, onMounted } from 'vue';
import { import {
SearchOutlined,
IdcardOutlined, IdcardOutlined,
PlusOutlined, PlusOutlined,
PhoneOutlined, PhoneOutlined,
@ -767,6 +836,10 @@ onMounted(() => {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
} }
.search-box :deep(.ant-input-affix-wrapper) {
border-radius: 12px;
border: 2px solid #F0F0F0;
}
.search-box :deep(.ant-input-affix-wrapper:hover) { .search-box :deep(.ant-input-affix-wrapper:hover) {
border-color: #FF8C42; border-color: #FF8C42;

View File

@ -38,14 +38,28 @@
<!-- 操作栏 --> <!-- 操作栏 -->
<div class="action-bar"> <div class="action-bar">
<div class="filters"> <div class="filters">
<a-select v-model:value="selectedClassId" placeholder="选择班级" style="width: 150px;" @change="handleClassChange" <a-select
allow-clear> v-model:value="selectedClassId"
placeholder="选择班级"
style="width: 150px;"
@change="handleClassChange"
allow-clear
>
<a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id"> <a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id">
{{ cls.name }} {{ cls.name }}
</a-select-option> </a-select-option>
</a-select> </a-select>
<a-input-search v-model:value="searchKeyword" placeholder="搜索学生姓名/家长" style="width: 220px;" <a-input-search
@search="handleSearch" allow-clear /> v-model:value="searchKeyword"
placeholder="搜索学生姓名/家长"
style="width: 220px;"
@search="handleSearch"
allow-clear
>
<template #prefix>
<SearchOutlined style="color: #B2BEC3;" />
</template>
</a-input-search>
</div> </div>
<div class="actions"> <div class="actions">
<a-button class="import-btn" @click="showImportModal"> <a-button class="import-btn" @click="showImportModal">
@ -61,7 +75,11 @@
<!-- 学生卡片网格 --> <!-- 学生卡片网格 -->
<div class="student-grid" v-if="!loading && students.length > 0"> <div class="student-grid" v-if="!loading && students.length > 0">
<div v-for="student in students" :key="student.id" class="student-card"> <div
v-for="student in students"
:key="student.id"
class="student-card"
>
<div class="card-header"> <div class="card-header">
<div class="student-avatar" :class="normalizeGender(student.gender) === '男' ? 'boy' : 'girl'"> <div class="student-avatar" :class="normalizeGender(student.gender) === '男' ? 'boy' : 'girl'">
<UserOutlined class="avatar-icon" /> <UserOutlined class="avatar-icon" />
@ -75,8 +93,7 @@
<div class="card-body"> <div class="card-body">
<div class="info-row"> <div class="info-row">
<CalendarOutlined class="info-icon" /> <CalendarOutlined class="info-icon" />
<span class="info-value">{{ calculateAge(student.birthDate) || '--' }}{{ student.birthDate ? '岁' : '' <span class="info-value">{{ calculateAge(student.birthDate) || '--' }}{{ student.birthDate ? '岁' : '' }}</span>
}}</span>
<span class="gender-tag" :class="normalizeGender(student.gender) === '男' ? 'boy' : 'girl'"> <span class="gender-tag" :class="normalizeGender(student.gender) === '男' ? 'boy' : 'girl'">
{{ normalizeGender(student.gender) }} {{ normalizeGender(student.gender) }}
</span> </span>
@ -102,7 +119,10 @@
<a-button type="link" size="small" @click="handleTransfer(student)"> <a-button type="link" size="small" @click="handleTransfer(student)">
<SwapOutlined /> 调班 <SwapOutlined /> 调班
</a-button> </a-button>
<a-popconfirm title="确定要删除这位学生吗?" @confirm="handleDelete(student.id)"> <a-popconfirm
title="确定要删除这位学生吗?"
@confirm="handleDelete(student.id)"
>
<a-button type="link" size="small" danger> <a-button type="link" size="small" danger>
<DeleteOutlined /> 删除 <DeleteOutlined /> 删除
</a-button> </a-button>
@ -130,21 +150,37 @@
<!-- 分页 --> <!-- 分页 -->
<div class="pagination-wrapper" v-if="students.length > 0"> <div class="pagination-wrapper" v-if="students.length > 0">
<a-pagination v-model:current="pagination.current" v-model:pageSize="pagination.pageSize" <a-pagination
:total="pagination.total" :show-size-changer="true" :show-total="(total: number) => `共 ${total} 条`" v-model:current="pagination.current"
@change="handlePageChange" /> v-model:pageSize="pagination.pageSize"
:total="pagination.total"
:show-size-changer="true"
:show-total="(total: number) => `共 ${total} 条`"
@change="handlePageChange"
/>
</div> </div>
<!-- 添加/编辑学生模态框 --> <!-- 添加/编辑学生模态框 -->
<a-modal v-model:open="modalVisible" @ok="handleModalOk" @cancel="handleModalCancel" :confirm-loading="submitting" <a-modal
:width="520"> v-model:open="modalVisible"
@ok="handleModalOk"
@cancel="handleModalCancel"
:confirm-loading="submitting"
:width="520"
>
<template #title> <template #title>
<span class="modal-title"> <span class="modal-title">
<component :is="isEdit ? EditOutlined : PlusOutlined" class="modal-title-icon" /> <component :is="isEdit ? EditOutlined : PlusOutlined" class="modal-title-icon" />
{{ isEdit ? '编辑学生' : '添加学生' }} {{ isEdit ? '编辑学生' : '添加学生' }}
</span> </span>
</template> </template>
<a-form ref="formRef" :model="formState" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }"> <a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
>
<a-form-item label="姓名" name="name"> <a-form-item label="姓名" name="name">
<a-input v-model:value="formState.name" placeholder="请输入学生姓名"> <a-input v-model:value="formState.name" placeholder="请输入学生姓名">
<template #prefix> <template #prefix>
@ -154,17 +190,17 @@
</a-form-item> </a-form-item>
<a-form-item label="性别" name="gender"> <a-form-item label="性别" name="gender">
<a-radio-group v-model:value="formState.gender"> <a-radio-group v-model:value="formState.gender">
<a-radio value="男"> <a-radio value="男"><UserOutlined class="gender-icon boy" /> 男孩</a-radio>
<UserOutlined class="gender-icon boy" /> 男孩 <a-radio value="女"><UserOutlined class="gender-icon girl" /> 女孩</a-radio>
</a-radio>
<a-radio value="女">
<UserOutlined class="gender-icon girl" /> 女孩
</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item label="出生日期" name="birthDate"> <a-form-item label="出生日期" name="birthDate">
<a-date-picker v-model:value="formState.birthDate" style="width: 100%;" value-format="YYYY-MM-DD" <a-date-picker
placeholder="选择出生日期" /> v-model:value="formState.birthDate"
style="width: 100%;"
value-format="YYYY-MM-DD"
placeholder="选择出生日期"
/>
</a-form-item> </a-form-item>
<a-form-item label="所在班级" name="classId"> <a-form-item label="所在班级" name="classId">
<a-select v-model:value="formState.classId" placeholder="请选择班级" :loading="classesLoading"> <a-select v-model:value="formState.classId" placeholder="请选择班级" :loading="classesLoading">
@ -191,8 +227,14 @@
</a-modal> </a-modal>
<!-- 学生调班模态框 --> <!-- 学生调班模态框 -->
<a-modal v-model:open="transferModalVisible" title="学生调班" :confirm-loading="transferSubmitting" <a-modal
@ok="handleTransferSubmit" @cancel="transferModalVisible = false" width="480px"> v-model:open="transferModalVisible"
title="学生调班"
:confirm-loading="transferSubmitting"
@ok="handleTransferSubmit"
@cancel="transferModalVisible = false"
width="480px"
>
<div class="transfer-modal-content"> <div class="transfer-modal-content">
<div class="current-info"> <div class="current-info">
<span>当前学生</span> <span>当前学生</span>
@ -201,14 +243,22 @@
</div> </div>
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item label="目标班级" required> <a-form-item label="目标班级" required>
<a-select v-model:value="transferTargetClassId" placeholder="请选择目标班级" :loading="classesLoading"> <a-select
v-model:value="transferTargetClassId"
placeholder="请选择目标班级"
:loading="classesLoading"
>
<a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id"> <a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id">
{{ cls.name }} ({{ cls.grade }}) {{ cls.name }} ({{ cls.grade }})
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label="调班原因"> <a-form-item label="调班原因">
<a-textarea v-model:value="transferReason" placeholder="请输入调班原因(选填)" :rows="3" /> <a-textarea
v-model:value="transferReason"
placeholder="请输入调班原因(选填)"
:rows="3"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -230,7 +280,11 @@
</a-modal> </a-modal>
<!-- 批量导入模态框 --> <!-- 批量导入模态框 -->
<a-modal v-model:open="importModalVisible" :footer="null" width="560px"> <a-modal
v-model:open="importModalVisible"
:footer="null"
width="560px"
>
<template #title> <template #title>
<span class="modal-title"> <span class="modal-title">
<DownloadOutlined class="modal-title-icon" /> <DownloadOutlined class="modal-title-icon" />
@ -253,8 +307,12 @@
下载导入模板 下载导入模板
</a-button> </a-button>
<a-upload-dragger :before-upload="beforeUpload" :show-upload-list="false" accept=".xlsx,.xls,.csv" <a-upload-dragger
class="upload-area"> :before-upload="beforeUpload"
:show-upload-list="false"
accept=".xlsx,.xls,.csv"
class="upload-area"
>
<div class="upload-content"> <div class="upload-content">
<FileAddOutlined class="upload-icon" /> <FileAddOutlined class="upload-icon" />
<p class="upload-text">点击或拖拽文件到此区域上传</p> <p class="upload-text">点击或拖拽文件到此区域上传</p>
@ -270,15 +328,26 @@
<div v-if="importFile" class="default-class-select"> <div v-if="importFile" class="default-class-select">
<label>默认班级用于未指定班级的学生</label> <label>默认班级用于未指定班级的学生</label>
<a-select v-model:value="importDefaultClassId" placeholder="选择默认班级" style="width: 100%;" allow-clear> <a-select
v-model:value="importDefaultClassId"
placeholder="选择默认班级"
style="width: 100%;"
allow-clear
>
<a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id"> <a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id">
{{ cls.name }} {{ cls.name }}
</a-select-option> </a-select-option>
</a-select> </a-select>
</div> </div>
<a-button type="primary" :loading="importing" :disabled="!importFile" @click="handleImport" block <a-button
class="import-submit-btn"> type="primary"
:loading="importing"
:disabled="!importFile"
@click="handleImport"
block
class="import-submit-btn"
>
<template v-if="!importing"> <template v-if="!importing">
<RocketOutlined class="btn-icon" /> <RocketOutlined class="btn-icon" />
开始导入 开始导入
@ -310,6 +379,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'; import { ref, reactive, computed, onMounted } from 'vue';
import { import {
SearchOutlined,
TeamOutlined, TeamOutlined,
UserOutlined, UserOutlined,
EditOutlined, EditOutlined,
@ -847,9 +917,9 @@ onMounted(() => {
} }
.filters :deep(.ant-select-selector), .filters :deep(.ant-select-selector),
{ .filters :deep(.ant-input-affix-wrapper) {
border-radius: 12px; border-radius: 12px;
border: 2px solid #F0F0F0; border: 2px solid #F0F0F0;
} }
.filters :deep(.ant-select-selector:hover), .filters :deep(.ant-select-selector:hover),
@ -1320,8 +1390,7 @@ border: 2px solid #F0F0F0;
gap: 12px; gap: 12px;
} }
.filters, .filters, .actions {
.actions {
width: 100%; width: 100%;
flex-wrap: wrap; flex-wrap: wrap;
} }

View File

@ -28,8 +28,17 @@
<!-- 操作栏 --> <!-- 操作栏 -->
<div class="action-bar"> <div class="action-bar">
<div class="search-box"> <div class="search-box">
<a-input-search v-model:value="searchKeyword" placeholder="搜索教师姓名/手机号/账号" style="width: 280px;" <a-input-search
@search="handleSearch" allow-clear /> v-model:value="searchKeyword"
placeholder="搜索教师姓名/手机号/账号"
style="width: 280px;"
@search="handleSearch"
allow-clear
>
<template #prefix>
<SearchOutlined style="color: #B2BEC3;" />
</template>
</a-input-search>
</div> </div>
<a-button type="primary" class="add-btn" @click="showAddModal"> <a-button type="primary" class="add-btn" @click="showAddModal">
<PlusOutlined class="btn-icon" /> <PlusOutlined class="btn-icon" />
@ -39,8 +48,12 @@
<!-- 教师卡片列表 --> <!-- 教师卡片列表 -->
<div class="teacher-grid" v-if="!loading && teachers.length > 0"> <div class="teacher-grid" v-if="!loading && teachers.length > 0">
<div v-for="teacher in teachers" :key="teacher.id" class="teacher-card" <div
:class="{ 'inactive': teacher.status !== 'ACTIVE' }"> v-for="teacher in teachers"
:key="teacher.id"
class="teacher-card"
:class="{ 'inactive': teacher.status !== 'ACTIVE' }"
>
<div class="card-header"> <div class="card-header">
<div class="teacher-avatar"> <div class="teacher-avatar">
<SolutionOutlined class="avatar-icon" /> <SolutionOutlined class="avatar-icon" />
@ -66,11 +79,9 @@
<div class="info-row"> <div class="info-row">
<BankOutlined class="info-icon" /> <BankOutlined class="info-icon" />
<span class="info-value classes-tag"> <span class="info-value classes-tag">
<span <span v-if="teacher.classNames && (Array.isArray(teacher.classNames) ? teacher.classNames.length > 0 : teacher.classNames)">
v-if="teacher.classNames && (Array.isArray(teacher.classNames) ? teacher.classNames.length > 0 : teacher.classNames)">
{{ Array.isArray(teacher.classNames) ? teacher.classNames.slice(0, 2).join('、') : teacher.classNames }} {{ Array.isArray(teacher.classNames) ? teacher.classNames.slice(0, 2).join('、') : teacher.classNames }}
<span v-if="Array.isArray(teacher.classNames) && teacher.classNames.length > 2">{{ <span v-if="Array.isArray(teacher.classNames) && teacher.classNames.length > 2">{{ teacher.classNames.length }}个班级</span>
teacher.classNames.length }}个班级</span>
</span> </span>
<span v-else class="no-class">未分配班级</span> <span v-else class="no-class">未分配班级</span>
</span> </span>
@ -88,7 +99,10 @@
<a-button type="link" size="small" @click="handleResetPassword(teacher)"> <a-button type="link" size="small" @click="handleResetPassword(teacher)">
<KeyOutlined /> 重置密码 <KeyOutlined /> 重置密码
</a-button> </a-button>
<a-popconfirm title="确定要删除这位教师吗?" @confirm="handleDelete(teacher.id)"> <a-popconfirm
title="确定要删除这位教师吗?"
@confirm="handleDelete(teacher.id)"
>
<a-button type="link" size="small" danger> <a-button type="link" size="small" danger>
<DeleteOutlined /> 删除 <DeleteOutlined /> 删除
</a-button> </a-button>
@ -114,14 +128,26 @@
<!-- 分页 --> <!-- 分页 -->
<div class="pagination-wrapper" v-if="teachers.length > 0"> <div class="pagination-wrapper" v-if="teachers.length > 0">
<a-pagination v-model:current="pagination.current" v-model:pageSize="pagination.pageSize" <a-pagination
:total="pagination.total" :show-size-changer="true" :show-total="(total: number) => `共 ${total} 条`" v-model:current="pagination.current"
@change="handlePageChange" /> v-model:pageSize="pagination.pageSize"
:total="pagination.total"
:show-size-changer="true"
:show-total="(total: number) => `共 ${total} 条`"
@change="handlePageChange"
/>
</div> </div>
<!-- 添加/编辑教师模态框 --> <!-- 添加/编辑教师模态框 -->
<a-modal v-model:open="modalVisible" :title="isEdit ? '编辑教师' : '添加教师'" @ok="handleModalOk" <a-modal
@cancel="handleModalCancel" :confirm-loading="submitting" :width="520" class="teacher-modal"> v-model:open="modalVisible"
:title="isEdit ? '编辑教师' : '添加教师'"
@ok="handleModalOk"
@cancel="handleModalCancel"
:confirm-loading="submitting"
:width="520"
class="teacher-modal"
>
<template #title> <template #title>
<span class="modal-title"> <span class="modal-title">
<EditOutlined v-if="isEdit" class="modal-title-icon" /> <EditOutlined v-if="isEdit" class="modal-title-icon" />
@ -129,44 +155,49 @@
{{ isEdit ? '编辑教师' : '添加教师' }} {{ isEdit ? '编辑教师' : '添加教师' }}
</span> </span>
</template> </template>
<a-form ref="formRef" :model="formState" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }"> <a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
>
<a-form-item label="姓名" name="name"> <a-form-item label="姓名" name="name">
<a-input v-model:value="formState.name" placeholder="请输入教师姓名"> <a-input v-model:value="formState.name" placeholder="请输入教师姓名">
<template #prefix> <template #prefix><UserOutlined style="color: #B2BEC3;" /></template>
<UserOutlined style="color: #B2BEC3;" />
</template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item label="手机号" name="phone"> <a-form-item label="手机号" name="phone">
<a-input v-model:value="formState.phone" placeholder="请输入手机号"> <a-input v-model:value="formState.phone" placeholder="请输入手机号">
<template #prefix> <template #prefix><PhoneOutlined style="color: #B2BEC3;" /></template>
<PhoneOutlined style="color: #B2BEC3;" />
</template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item label="邮箱" name="email"> <a-form-item label="邮箱" name="email">
<a-input v-model:value="formState.email" placeholder="请输入邮箱(可选)"> <a-input v-model:value="formState.email" placeholder="请输入邮箱(可选)">
<template #prefix> <template #prefix><MailOutlined style="color: #B2BEC3;" /></template>
<MailOutlined style="color: #B2BEC3;" />
</template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item label="登录账号" name="loginAccount"> <a-form-item label="登录账号" name="loginAccount">
<a-input v-model:value="formState.loginAccount" placeholder="请输入登录账号" :disabled="isEdit"> <a-input
<template #prefix> v-model:value="formState.loginAccount"
<KeyOutlined style="color: #B2BEC3;" /> placeholder="请输入登录账号"
</template> :disabled="isEdit"
>
<template #prefix><KeyOutlined style="color: #B2BEC3;" /></template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item v-if="!isEdit" label="密码" name="password"> <a-form-item v-if="!isEdit" label="密码" name="password">
<a-input-password v-model:value="formState.password" placeholder="请输入密码默认123456"> <a-input-password v-model:value="formState.password" placeholder="请输入密码默认123456">
<template #prefix> <template #prefix><LockOutlined style="color: #B2BEC3;" /></template>
<LockOutlined style="color: #B2BEC3;" />
</template>
</a-input-password> </a-input-password>
</a-form-item> </a-form-item>
<a-form-item label="负责班级" name="classIds"> <a-form-item label="负责班级" name="classIds">
<a-select v-model:value="formState.classIds" mode="multiple" placeholder="请选择负责的班级" :loading="classesLoading"> <a-select
v-model:value="formState.classIds"
mode="multiple"
placeholder="请选择负责的班级"
:loading="classesLoading"
>
<a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id"> <a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id">
{{ cls.name }} {{ cls.name }}
</a-select-option> </a-select-option>
@ -176,7 +207,12 @@
</a-modal> </a-modal>
<!-- 重置密码确认模态框 --> <!-- 重置密码确认模态框 -->
<a-modal v-model:open="resetPasswordVisible" @ok="confirmResetPassword" :confirm-loading="resetting" :width="400"> <a-modal
v-model:open="resetPasswordVisible"
@ok="confirmResetPassword"
:confirm-loading="resetting"
:width="400"
>
<template #title> <template #title>
<span class="modal-title"> <span class="modal-title">
<KeyOutlined class="modal-title-icon" /> <KeyOutlined class="modal-title-icon" />
@ -202,6 +238,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'; import { ref, reactive, computed, onMounted } from 'vue';
import { import {
SearchOutlined,
SolutionOutlined, SolutionOutlined,
PlusOutlined, PlusOutlined,
PhoneOutlined, PhoneOutlined,
@ -520,6 +557,11 @@ onMounted(() => {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
} }
.search-box :deep(.ant-input-affix-wrapper) {
border-radius: 12px;
border: 2px solid #F0F0F0;
}
.search-box :deep(.ant-input-affix-wrapper:hover) { .search-box :deep(.ant-input-affix-wrapper:hover) {
border-color: #FF8C42; border-color: #FF8C42;
} }