cys #28
|
@ -29,6 +29,7 @@
|
|||
"jsencrypt": "^3.3.2",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"vue-cropper": "^1.1.3",
|
||||
"vue-router": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询用户个人信息
|
||||
export function getUserProfile() {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户个人信息
|
||||
export function updateUserProfile(data) {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function updateUserPwd(oldPassword, newPassword) {
|
||||
const data = {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户头像上传
|
||||
export function uploadAvatar(data) {
|
||||
return request({
|
||||
url: '/system/user/profile/avatar',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
<el-dropdown-item @click="changePage('/profile')">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logout">
|
||||
<span>退出登录</span>
|
||||
</el-dropdown-item>
|
||||
|
|
|
@ -31,6 +31,12 @@ export const constantRoutes = [
|
|||
component: () => import('@/views/teach/index.vue'),
|
||||
name: 'teach',
|
||||
meta: {title: '授课'}
|
||||
},
|
||||
{
|
||||
path: '/profile',
|
||||
component: () => import('@/views/profile/index.vue'),
|
||||
name: 'profile',
|
||||
meta: {title: '个人中心'}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card class="box-card">
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>个人信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-center">
|
||||
<userAvatar />
|
||||
</div>
|
||||
<ul class="list-group list-group-striped">
|
||||
<li class="list-group-item">
|
||||
<div class="left-align">
|
||||
<UserFilled class="UserFilled"/>
|
||||
<span>用户名称</span>
|
||||
</div>
|
||||
<div class="right-align">{{ state.user.userName }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="left-align">
|
||||
<Cellphone class="Cellphone"/>
|
||||
<span>手机号码</span>
|
||||
</div>
|
||||
<div class="right-align">{{ state.user.phonenumber }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="left-align">
|
||||
<Message class="Message"/>
|
||||
<span>用户邮箱</span>
|
||||
</div>
|
||||
<div class="right-align">{{ state.user.email }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="left-align">
|
||||
<Suitcase class="Suitcase"/>
|
||||
<span>所属部门</span>
|
||||
</div>
|
||||
<div class="right-align" v-if="state.user.dept">
|
||||
{{ state.user.dept.deptName }} / {{ state.postGroup }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="left-align">
|
||||
<Avatar class="Avatar"/>
|
||||
<span>所属角色</span>
|
||||
</div>
|
||||
<div class="right-align">{{ state.roleGroup }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="left-align">
|
||||
<Calendar class="Calendar"/>
|
||||
<span>创建日期</span>
|
||||
</div>
|
||||
<div class="right-align">{{ state.user.createTime }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-card>
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>基本资料</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基本资料" name="userinfo">
|
||||
<userInfo :user="state.user" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改密码" name="resetPwd">
|
||||
<resetPwd />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Profile">
|
||||
import { UserFilled, Cellphone, Message, Suitcase, Avatar, Calendar } from '@element-plus/icons-vue'
|
||||
import { ref, reactive } from 'vue'
|
||||
import userAvatar from './userAvatar.vue'
|
||||
import userInfo from './userInfo.vue'
|
||||
import resetPwd from './resetPwd.vue'
|
||||
import { getUserProfile } from '@/api/system/user'
|
||||
|
||||
const activeTab = ref('userinfo')
|
||||
const state = reactive({
|
||||
user: {},
|
||||
roleGroup: {},
|
||||
postGroup: {}
|
||||
})
|
||||
|
||||
function getUser() {
|
||||
getUserProfile().then((response) => {
|
||||
state.user = response.data
|
||||
state.roleGroup = response.roleGroup
|
||||
state.postGroup = response.postGroup
|
||||
})
|
||||
}
|
||||
|
||||
getUser()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.clearfix {
|
||||
text-align: left;
|
||||
}
|
||||
.text-center {
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
.list-group-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
padding-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.left-align {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.right-align {
|
||||
align-items: right;
|
||||
}
|
||||
.UserFilled, .Cellphone, .Message, .Suitcase, .Avatar, .Calendar {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
|
||||
<el-form-item label="旧密码" prop="oldPassword">
|
||||
<el-input
|
||||
v-model="user.oldPassword"
|
||||
placeholder="请输入旧密码"
|
||||
type="password"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPassword">
|
||||
<el-input
|
||||
v-model="user.newPassword"
|
||||
placeholder="请输入新密码"
|
||||
type="password"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPassword">
|
||||
<el-input
|
||||
v-model="user.confirmPassword"
|
||||
placeholder="请确认新密码"
|
||||
type="password"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { updateUserPwd } from '@/api/system/user'
|
||||
|
||||
// const { proxy } = getCurrentInstance();
|
||||
|
||||
const user = reactive({
|
||||
oldPassword: undefined,
|
||||
newPassword: undefined,
|
||||
confirmPassword: undefined
|
||||
})
|
||||
|
||||
const equalToPassword = (rule, value, callback) => {
|
||||
if (user.newPassword !== value) {
|
||||
callback(new Error('两次输入的密码不一致'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const rules = ref({
|
||||
oldPassword: [{ required: true, message: '旧密码不能为空', trigger: 'blur' }],
|
||||
newPassword: [
|
||||
{ required: true, message: '新密码不能为空', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
|
||||
{ required: true, validator: equalToPassword, trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
|
||||
/** 提交按钮 */
|
||||
function submit() {
|
||||
proxy.$refs.pwdRef.validate((valid) => {
|
||||
if (valid) {
|
||||
updateUserPwd(user.oldPassword, user.newPassword).then((response) => {
|
||||
proxy.$modal.msgSuccess('修改成功')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
<template>
|
||||
<div class="user-info-head" @click="editCropper()">
|
||||
<img :src="options.img" title="点击上传头像" class="img-circle" />
|
||||
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||
<el-row>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:img="options.img"
|
||||
:info="true"
|
||||
:autoCrop="options.autoCrop"
|
||||
:autoCropWidth="options.autoCropWidth"
|
||||
:autoCropHeight="options.autoCropHeight"
|
||||
:fixedBox="options.fixedBox"
|
||||
:outputType="options.outputType"
|
||||
@realTime="realTime"
|
||||
v-if="visible"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<div class="avatar-upload-preview">
|
||||
<img :src="options.previews.url" :style="options.previews.img" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<br />
|
||||
<el-row>
|
||||
<el-col :lg="2" :md="2">
|
||||
<el-upload
|
||||
action="#"
|
||||
:http-request="requestUpload"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<el-button>
|
||||
选择
|
||||
<el-icon class="el-icon--right"><Upload /></el-icon>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
||||
<el-button :icon="Plus" @click="changeScale(1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button :icon="Minus" @click="changeScale(-1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button :icon="RefreshLeft" @click="rotateLeft()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button :icon="RefreshRight" @click="rotateRight()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
|
||||
<el-button type="primary" @click="uploadImg()">提 交</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Upload, Plus, Minus, RefreshLeft, RefreshRight } from '@element-plus/icons-vue'
|
||||
import { ref, reactive } from 'vue'
|
||||
import "vue-cropper/dist/index.css";
|
||||
import { VueCropper } from "vue-cropper";
|
||||
import { uploadAvatar } from "@/api/system/user";
|
||||
import useUserStore from "@/store/modules/user";
|
||||
|
||||
const userStore = useUserStore();
|
||||
// const { proxy } = getCurrentInstance();
|
||||
|
||||
const open = ref(false);
|
||||
const visible = ref(false);
|
||||
const title = ref("修改头像");
|
||||
|
||||
//图片裁剪数据
|
||||
const options = reactive({
|
||||
img: userStore.avatar, // 裁剪图片的地址
|
||||
autoCrop: true, // 是否默认生成截图框
|
||||
autoCropWidth: 200, // 默认生成截图框宽度
|
||||
autoCropHeight: 200, // 默认生成截图框高度
|
||||
fixedBox: true, // 固定截图框大小 不允许改变
|
||||
outputType: "png", // 默认生成截图为PNG格式
|
||||
previews: {} //预览数据
|
||||
});
|
||||
|
||||
/** 编辑头像 */
|
||||
function editCropper() {
|
||||
open.value = true;
|
||||
}
|
||||
/** 打开弹出层结束时的回调 */
|
||||
function modalOpened() {
|
||||
visible.value = true;
|
||||
}
|
||||
/** 覆盖默认上传行为 */
|
||||
function requestUpload() {}
|
||||
/** 向左旋转 */
|
||||
function rotateLeft() {
|
||||
proxy.$refs.cropper.rotateLeft();
|
||||
}
|
||||
/** 向右旋转 */
|
||||
function rotateRight() {
|
||||
proxy.$refs.cropper.rotateRight();
|
||||
}
|
||||
/** 图片缩放 */
|
||||
function changeScale(num) {
|
||||
num = num || 1;
|
||||
proxy.$refs.cropper.changeScale(num);
|
||||
}
|
||||
/** 上传预处理 */
|
||||
function beforeUpload(file) {
|
||||
if (file.type.indexOf("image/") == -1) {
|
||||
proxy.$modal.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。");
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
options.img = reader.result;
|
||||
};
|
||||
}
|
||||
}
|
||||
/** 上传图片 */
|
||||
function uploadImg() {
|
||||
proxy.$refs.cropper.getCropBlob(data => {
|
||||
let formData = new FormData();
|
||||
formData.append("avatarfile", data);
|
||||
uploadAvatar(formData).then(response => {
|
||||
open.value = false;
|
||||
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl;
|
||||
userStore.avatar = options.img;
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
visible.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
/** 实时预览 */
|
||||
function realTime(data) {
|
||||
options.previews = data;
|
||||
}
|
||||
/** 关闭窗口 */
|
||||
function closeDialog() {
|
||||
options.img = userStore.avatar;
|
||||
options.visible = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.user-info-head {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.user-info-head:hover:after {
|
||||
content: "+";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
color: #eee;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
font-size: 24px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
cursor: pointer;
|
||||
line-height: 110px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.img-circle {
|
||||
height: 120px;
|
||||
width: auto
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<el-form ref="userRef" :model="user" :rules="rules" label-width="80px">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="user.nickName" maxlength="30" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="user.phonenumber" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="user.email" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group >
|
||||
<el-radio label="0">男</el-radio>
|
||||
<el-radio label="1">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="任教学科">
|
||||
<el-radio-group v-model="user.edusubject">
|
||||
<template v-for="item in subjectList">
|
||||
<el-radio v-if="item.edustage==user.edustage" :label="item.itemtitle">{{ item.itemtitle }}</el-radio>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="primary" @click="handleclick1">测验</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { updateUserProfile } from "@/api/system/user";
|
||||
import { listEvaluation } from "@/api/subject/index";
|
||||
|
||||
const props = defineProps({
|
||||
user: {
|
||||
type: Object
|
||||
}
|
||||
});
|
||||
|
||||
// const { proxy } = getCurrentInstance();
|
||||
|
||||
const rules = ref({
|
||||
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
|
||||
phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
|
||||
});
|
||||
|
||||
const subjectList = ref([]);
|
||||
|
||||
function handleclick1() {
|
||||
console.log(props.user.edudegree)
|
||||
}
|
||||
|
||||
// 获取基础的学科
|
||||
listEvaluation({ itemkey: "subject", pageSize: 500 }).then((res) => {
|
||||
subjectList.value = res.rows;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
if (props.user.edudegree != '') {
|
||||
var upcaseName = ['一年级', '二年级', '三年级', '四年级', '五年级', '六年级', '初一', '初二', '初三', '高一', '高二', '高三'];
|
||||
var d = parseInt(props.user.edudegree.replace('年级', ''));
|
||||
if (d <= 6) {
|
||||
props.user.edustage = '小学';
|
||||
} else if (d >= 7 && d <= 9) {
|
||||
props.user.edustage = '初中';
|
||||
} else if (d >= 9 && d <= 12) {
|
||||
props.user.edustage = '高中';
|
||||
}
|
||||
props.user.stagelabel = upcaseName[d-1];
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
/** 提交按钮 */
|
||||
function submit() {
|
||||
proxy.$refs.userRef.validate(valid => {
|
||||
if (valid) {
|
||||
console.log(props.user);
|
||||
updateUserProfile(props.user).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
Loading…
Reference in New Issue