个人界面

main
abab2320 2 months ago
parent af1e136e3a
commit a48e551ef8

@ -314,7 +314,7 @@ async function login(){
padding: 0; padding: 0;
} }
:global(body){ body{
height: 100vh;; height: 100vh;;
/*弹性布局,水平垂直居中*/ /*弹性布局,水平垂直居中*/
display:flex; display:flex;
@ -554,7 +554,8 @@ async function login(){
<style> <style>
body{ body{
height: 100vh; height: 100%;
width:100%;
/*弹性布局,水平垂直居中*/ /*弹性布局,水平垂直居中*/
display:flex; display:flex;
justify-content: center; justify-content: center;

@ -1,47 +1,177 @@
<script set lang="ts"> <script set lang="ts">
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
import{useForm,useField,Form} from 'vee-validate';
import * as yup from 'yup';
import { useGetDerivedNamespace } from 'element-plus';
export default defineComponent({ export default defineComponent({
name: 'Manager', name: 'Manager',
//
// usernamegenderintroductionbirthdayemail,password()
//email
//usernamegenderintroductionbirthday
//
//使struct orignData{username,gender,introduction,birthday,email,password}
//使ToolTip
setup() { setup() {
const email = ref('test@example.com'); const originData = ref({
const username = ref('测试员'); username:"测试员",
return{ gender:2,
email, introduction:'测试员的个人简介只要不出bug一切都好',
username birthday:'2023-10-01',
email:"test@example.com",
password:'123456',
})
const formData = ref({...originData.value});//
//
const isEditable = ref(false);
//
const showErrors = ref(false);
const PasswordEdit = ref(false);
const newpassword = ref('');
const newpasswordConfirm = ref('');
const emailCode = ref(''); //
const toggleEdit = () => {
isEditable.value = !isEditable.value;
};
const togglePasswordEdit = () => {
PasswordEdit.value = !PasswordEdit.value;
};
//
//
const ProfileScheme = useForm({
initialValues:formData.value,
validationSchema:yup.object(
{
username:yup.string().required('昵称不能为空'),
introduction:yup.string().required("自我介绍不能为空"),
}
)
})
const{errors} = ProfileScheme;
//
const PasswordSheme = useForm({
initialValues:{
newpassword:newpassword.value,
newpasswordConfirm:newpasswordConfirm.value,
emailCode:emailCode.value
},
validationSchema:yup.object(
{
newpassword:yup.string().required('新密码不能为空').min(6,'密码至少6位'),
newpasswordConfirm:yup.string().required('确认密码不能为空').oneOf([yup.ref('newpassword')],'两次输入的密码不一致'),
emailCode:yup.string().required('验证码不能为空')
}
)
})
//
const onProfileSubmit = () =>{
console.log("调用个人信息提交函数");
ProfileScheme.resetForm({values:{...formData.value}});
ProfileScheme.handleSubmit((values)=>{
console.log("表单调用成功",values);
console.log("保存成功");
isEditable.value = false;
},(err) =>{
console.log("表单调用失败",err);
showErrors.value = true;
formData.value = {...originData.value};
})();
} }
const onPasswordSubmit = ()=>
{
console.log("调用密码提交函数");
PasswordSheme.resetForm({values:{
newpassword:newpassword.value,
newpasswordConfirm:newpasswordConfirm.value,
emailCode:emailCode.value
}});
PasswordSheme.handleSubmit((values)=>{
console.log("表单调用成功",values);
console.log("修改成功");
PasswordEdit.value = false;
},(err) =>{
console.log("表单调用失败",err);
showErrors.value = true;
})();
}
//
return {
originData,
formData,
isEditable,
newpassword,
newpasswordConfirm,
emailCode,
PasswordEdit,
toggleEdit,
togglePasswordEdit,
ProfileScheme,
PasswordSheme,
onProfileSubmit,
errors,
showErrors,
onPasswordSubmit
};
} }
}); });
</script> </script>
<template> <template>
<transition name = "fade-up">
<el-alert
class = "error-msg"
v-if="showErrors"
:title= errors.username
type="error"
effect="dark"
:closable="true"
@close="showErrors = false"
show-icon = "true"
center/>
</transition>
<div class = "container"> <div class = "container">
<div class="main-content"> <div class="main-content">
<!-- 左侧信息设置区 --> <!-- 左侧信息设置区 -->
<div class="card profile-info-card"> <div class="card profile-info-card">
<!-- 个人信息部分 --> <!-- 个人信息部分 -->
<div class="form-section"> <Form :validation-schema="ProfileScheme">
<div class="form-section">
<h2 class="section-title">个人信息</h2> <h2 class="section-title">个人信息</h2>
<div class="form-group"> <div class="form-group">
<label class="form-label">昵称</label> <label class="form-label">昵称</label>
<input type="text" class="form-input" :placeholder="username" readonly> <input type="text" class="form-input" v-model="formData.username" :readonly="!isEditable"/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">个人简介</label> <label class="form-label">个人简介</label>
<input type="text" class="form-input" placeholder="这是一个个人简介"> <input type="text" class="form-input" v-model="formData.introduction" :readonly="!isEditable">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">性别</label> <label class="form-label">性别</label>
<div class="radio-group"> <div class="radio-group">
<label class="radio-label"> <label class="radio-label">
<input type="radio" name="gender" class="radio-input" checked> <input type="radio" name="gender" class="radio-input" v-model = "formData.gender" :value="2" :disabled="!isEditable">
<span></span> <span></span>
</label> </label>
<label class="radio-label"> <label class="radio-label">
<input type="radio" name="gender" class="radio-input"> <input type="radio" name="gender" class="radio-input" v-model = "formData.gender" :value="1" :disabled="!isEditable">
<span></span> <span></span>
</label> </label>
</div> </div>
@ -49,38 +179,52 @@ export default defineComponent({
<div class="form-group"> <div class="form-group">
<label class="form-label">生日</label> <label class="form-label">生日</label>
<input type="date" class="date-input"> <input type="date" class="date-input" v-model = "formData.birthday" :readonly="!isEditable">
</div> </div>
</div> </div>
<div class="btn-save-container">
<button type = "submit" class="btn btn-primary" @click.prevent="onProfileSubmit()" :disabled = "!isEditable"> </button>
<button type = "button"class="btn btn-secondary" @click="toggleEdit">{{ isEditable ? '' : ' ' }}</button>
</div>
</Form>
<div class="divider"></div> <div class="divider"></div>
<!-- 账号信息部分 --> <!-- 账号信息部分 -->
<Form :validation-schema="PasswordSheme">
<div class="form-section"> <div class="form-section">
<h2 class="section-title">账号信息</h2> <h2 class="section-title">账号信息</h2>
<!--展示邮箱同时提供发送验证码的按钮-->
<div class="form-group"> <div class="form-group">
<label class="form-label">绑定邮箱</label> <label class="form-label">绑定邮箱</label>
<input type="email" class="form-input" :placeholder="email" readonly> <input type="email" class="form-input" v-model="formData.email" readonly>
<button class="btn btn-primary" v-if="PasswordEdit"></button>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">修改密码</label> <label class="form-label">修改密码</label>
<div class="password-inputs"> <div class="password-inputs" v-if = "!PasswordEdit">
<input type="password" class="form-input" placeholder="xxxxxx旧密码"> <input type="password" class="form-input" v-model = "formData.password" readonly>
<input type="password" class="form-input" placeholder="xxxxxx新密码">
</div> </div>
<button class="btn btn-primary btn-password">确认修改</button> <div class = "password-inputs" v-if = "PasswordEdit">
<input type="password" class="form-input" v-model = "newpassword" placeholder="新密码"/>
<input type="password" class="form-input" v-model = "newpasswordConfirm" placeholder="确认新密码"/>
<input type="email-code" class="form-input" v-model="emailCode" placeholder="验证码">
</div>
<button type ="button" class="btn btn-primary btn-password" @click = "togglePasswordEdit()">
{{ PasswordEdit ? '取消' : '修改' }}
</button>
<button type = "submit" class="btn btn-secondary btn-password" v-if="PasswordEdit" @click.prevent="onPasswordSubmit()">
保存
</button>
</div> </div>
<p class="form-hint">* 密码至少包含6个字符建议使用字母数字和符号的组合</p> <p class="form-hint">* 密码至少包含6个字符建议使用字母数字和符号的组合</p>
</div> </div>
</Form>
<div class="divider"></div>
<!-- 保存按钮 -->
<div class="btn-save-container">
<button class="btn btn-primary"> </button>
</div>
</div> </div>
<!-- 右侧预览区 --> <!-- 右侧预览区 -->
@ -90,12 +234,12 @@ export default defineComponent({
<img src="../../../public/images/默认头像.jpg" alt="用户头像"> <img src="../../../public/images/默认头像.jpg" alt="用户头像">
</div> </div>
<h3>{{ username }}</h3> <h3>{{ formData.username }}</h3>
<p class="preview-email">{{ email }}</p> <p class="preview-email">{{ formData.email }}</p>
<div class="preview-bio"> <div class="preview-bio">
这是一个个人简介 {{ formData.introduction }}
</div> </div>
</div> </div>
@ -119,6 +263,39 @@ export default defineComponent({
margin: 0; margin: 0;
} }
.error-msg{
z-index: 1000;
height:50px;
width:30%;
position: absolute;
top:8%;
left:20%;
display: flex;
transition:2s;
}
/*错误信息提示*/
.fade-up-enter-active,
.fade-up-leave-active{
transition: all 0.4s ease;
}
.fade-up-enter-from,
.fade-up-leave-to{
opacity:0;
transform:translateY(10px);
}
.fade-up-enter-to,
.fade-up-leave-from{
opacity:1;
transform:translateY(0);
}
.hidden
{
display: none;
}
/* 主容器 */ /* 主容器 */
.container { .container {
position:absolute; position:absolute;
@ -166,7 +343,7 @@ export default defineComponent({
/* 主内容区 */ /* 主内容区 */
.main-content { .main-content {
flex: 1; flex: 1;
height:90%; height:94%;
padding: 30px; padding: 30px;
display: flex; display: flex;
gap: 20px; gap: 20px;
@ -307,6 +484,7 @@ export default defineComponent({
.btn { .btn {
outline:none; outline:none;
padding: 10px 24px; padding: 10px 24px;
margin:10px;
border: none; border: none;
border-radius: 25px; border-radius: 25px;
cursor: pointer; cursor: pointer;
@ -334,6 +512,18 @@ export default defineComponent({
margin-top: 20px; margin-top: 20px;
} }
.btn-secondary {
background-color: #e6e6fa;
color: #666;
box-shadow: 0 4px 10px rgba(230, 230, 250, 0.3);
}
.btn-secondary:hover {
background-color: #dcdcdc;
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(230, 230, 250, 0.4);
}
/* 预览区样式 */ /* 预览区样式 */
.preview-section { .preview-section {
display: flex; display: flex;

@ -10,5 +10,12 @@
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true "noUncheckedSideEffectImports": true
}, },
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": [
"src/**/*_test.vue",
"src/**/*.test.vue",
"src/**/*.spec.vue",
"src/**/__tests__/*",
"src/components/Personal/AcountManager_test.vue"
]
} }

@ -1,174 +0,0 @@
# UniLife 系统功能验收测试文档
## 1. UniLife系统功能模块验收测试
### 1.1 用户系统验收测试UserModule
UniLife 的用户系统模块是平台使用的基础,支持用户注册、登录、信息管理及权限控制,确保身份合法、安全访问平台资源。
#### 1.1.1 测试用例
1. **测试用例名称**:用户注册功能测试
- 目的:验证用户是否可通过学校邮箱成功注册账户。
- 前置条件:学校邮箱地址未注册过系统账户。
- 测试数据:有效邮箱、密码、昵称、学号等注册信息。
- 执行步骤:
1. 向 `/users/register` 发起POST请求。
2. 提交注册数据。
- 期望结果返回状态码200提示“注册成功”系统生成新用户ID并写入数据库。
2. **测试用例名称**:邮箱验证码登录测试
- 目的:验证用户可通过邮箱验证码方式登录。
- 前置条件:邮箱已注册;验证码服务正常。
- 测试数据:合法邮箱,匹配验证码。
- 执行步骤:
1. 请求 `/users/code` 接口获取验证码;
2. 使用验证码通过 `/users/login/code` 登录。
- 期望结果系统返回token及用户信息状态为“登录成功”。
#### 1.1.2 验收标准
- 用户注册流程无异常,注册信息写入数据库。
- 邮箱验证码登录流程安全、成功率达100%,验证码有效期控制合理。
#### 1.1.3 验收步骤
1. 使用有效邮箱进行注册,确认响应数据结构完整、用户信息入库成功;
2. 请求验证码并完成登录检查返回token和用户信息结构完整。
---
### 1.2 课表管理模块验收测试ScheduleModule
课表管理支持导入课程安排并整合至个人行程,是时间管理的核心组件。
#### 1.2.1 测试用例
1. **测试用例名称**:课表导入功能测试
- 目的:测试用户是否可导入课程表数据。
- 前置条件:用户已登录。
- 测试数据标准课程JSON数据包含课程名、时间、地点等字段。
- 执行步骤:
1. 调用 `/schedule/import` 接口提交数据。
- 期望结果:课程信息被正确解析并写入用户课表视图。
2. **测试用例名称**:课表提醒功能测试
- 目的:测试是否能按时间提醒用户上课。
- 前置条件系统时间模拟到课程开始前10分钟。
- 测试数据:课程时间与提醒设置。
- 执行步骤:
1. 启用提醒服务;
2. 等待触发时间;
- 期望结果:系统触发提醒通知,包含课程名、时间、地点。
#### 1.2.2 验收标准
- 导入功能兼容常见课程数据格式;
- 所有课程数据完整显示;
- 提醒功能按时间精准触发,无冗余通知。
#### 1.2.3 验收步骤
1. 模拟课程数据上传,观察课程表渲染及存储状态;
2. 检查提醒是否按预设时间推送并通知用户。
---
### 1.3 作业共享模块验收测试AssignmentSharing
支持上传、查询与筛选作业文档,提升学习协作与信息共享效率。
#### 1.3.1 测试用例
1. **测试用例名称**:作业上传功能测试
- 目的:验证作业上传接口是否稳定、支持多格式。
- 前置条件:用户登录状态;课程已选。
- 测试数据PDF、Word等格式作业文件。
- 执行步骤:
1. 调用 `/assignment/upload` 接口上传文件并附加课程ID、作业类型。
- 期望结果:返回上传成功提示,作业入库,前端可展示。
2. **测试用例名称**:按关键词查询作业
- 目的:验证作业查询功能可按课程名、关键词、提交时间检索。
- 前置条件:系统已有多份作业文档。
- 测试数据:关键词如“线性代数”、“实验报告”。
- 执行步骤:
1. 访问 `/assignment/search?keyword=线性代数`
- 期望结果:返回符合关键词的作业文件,信息完整展示。
#### 1.3.2 验收标准
- 文件上传稳定,存储路径正确;
- 文件支持多种格式PDF、Word
- 查询功能响应快、结果准确。
#### 1.3.3 验收步骤
1. 上传多类型作业文件,检查前端渲染与后端数据库记录;
2. 多轮查询测试,验证关键词、课程名筛选功能。
---
### 1.4 AI学习辅助模块验收测试AIScheduler
此模块结合课表与任务数据,借助大模型制定学习计划、发送智能提醒。
#### 1.4.1 测试用例
1. **测试用例名称**AI制定学习计划测试
- 目的:验证是否能根据课表、作业量自动生成学习计划。
- 前置条件:已有课表数据及作业清单。
- 测试数据:课程表+作业截止时间数据。
- 执行步骤:
1. 调用 `POST /ai/plan`,提交用户当前日程;
2. 查看返回的学习任务清单。
- 期望结果:返回合理学习时间分配与推荐学习任务。
#### 1.4.2 验收标准
- 学习计划生成时间 ≤ 5 秒;
- 返回的任务清单结构完整、覆盖率高;
- 提醒频率合理,无骚扰式弹窗。
#### 1.4.3 验收步骤
1. 使用指定数据调用生成接口;
2. 验证学习任务合理性及个性化程度。
---
### 1.5 积分系统验收测试PointSystem
积分系统通过行为激励提升活跃度,积分可兑换特权资源。
#### 1.5.1 测试用例
1. **测试用例名称**:发帖积分累积测试
- 目的:测试发帖行为是否会产生积分变化。
- 前置条件:用户已登录。
- 测试数据:用户当前积分记录。
- 执行步骤:
1. 用户发布新帖子;
2. 查询 `/users/points`
- 期望结果:积分增加,系统记录该行为。
2. **测试用例名称**:积分兑换操作测试
- 目的:测试是否可使用积分兑换论坛特权(如置顶、下载权限)。
- 前置条件:用户积分余额满足兑换条件。
- 执行步骤:
1. 调用 `/points/redeem` 接口;
2. 选择特权项目。
- 期望结果:扣除积分、功能权限生效。
#### 1.5.2 验收标准
- 行为触发积分变动及时;
- 积分记录可追溯;
- 兑换流程顺畅,权限变更即时生效。
#### 1.5.3 验收步骤
1. 操作多项积分触发行为;
2. 查看积分变化与记录;
3. 执行一次积分兑换,验证权限变更。
Loading…
Cancel
Save