一、创建项目
1.创建项目
2.安装各种依赖
npm install
@highlightjs/vue-plugin
@moefe/vue-aplayer
aplayer
axios
docx-preview
dplayer
element-plus
highlight.js
js-md5
sass
sass-loader
spark-md5
vue-clipboard3
vue-cookies
vue-pdf-embed
vue-router
vue3-pdfjs
xlsx
--save
3.修改端口号
vite.config.js
server: {port: 1024,hmr: true,proxy: {'/api': {target: 'http://localhost:7090',changeOrigin: true,pathRewrite: {'^api': '/api',},},},},
4.引入各项安装
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
//引入element plus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
//图标图标在附件中
import '@/assets/icon/iconfont.css'
import '@/assets/base.scss'
// 引入cookies
import { VueCookies } from 'vue-cookies'
const app = createApp(App)app.use(router)
app.use(ElementPlus)
app.mount('#app')
index.html
<script src="/easypan-front/public/hls.min.js"></script><title>uu云盘</title>
二、登录注册页面
可以免费下载图片: undraw
(需要前后端源码的可以评论或者私信),这一部分记录的不全面
src/views/Login.vue
<template><div class="login-body"><div class="bg"></div><div class="login-panel"><el-form class="login-register":model="formData" :rules="rules" ref="formDataRef" @submit.prevent><div class="login-title">uu云盘</div><!-- input输入 --><el-form-item prop="email"><el-inputclearablesize="large"placeholder="请输入邮箱"v-model.trim="formData.email"maxLength="150"><!-- 插入图标 --><template #prefix><span class="iconfont icon-account"></span></template></el-input></el-form-item><!--登录密码 --><el-form-item prop="password" v-if="opType==1"><el-inputtype="password"size="large"v-model="formData.password" placeholder="请输入密码" show-password><!-- 插入图标 --><template #prefix><span class="iconfont icon-password"></span></template></el-input></el-form-item><!-- 注册 --><div v-if="opType==0||opType==2"><!-- 邮箱验证码 --><el-form-item prop="emailCode"><div class="send-email-panel"><el-input v-model.trim="formData.emailCode" placeholder="请输入邮箱验证码" size="large" clearable><template #prefix><span class="iconfont icon-checkcode"></span></template></el-input><el-button class="send-mail-btn" type="primary" size="large" @click="getEmailCode">获取验证码</el-button></div><!-- 气泡框 --><el-popover placement="left" :width="500" trigger="click"><div><p>1、在垃圾箱中查找邮箱验证码</p><p>2、在邮箱中头像->设置->反垃圾->白名单->设置邮件地址白名单</p><p>3、将邮箱【laoluo@wuhancoder.com】添加到白名单不知道怎么设置?</p></div><template #reference><span class="a-link" :style="{ 'font-size': '14px' }">未收到邮箱验证码?</span></template></el-popover></el-form-item><!-- 昵称,注册时0才有昵称 --><el-form-item prop="nickName" v-if="opType == 0"><el-inputsize="large"clearableplaceholder="请输入昵称"v-model.trim="formData.nickName"maxLength="20"><template #prefix><span class="iconfont icon-account"></span></template></el-input></el-form-item><!-- 输入密码 --><!-- 注册密码,找回密码 --><el-form-item prop="registerPassword"><el-inputtype="password"size="large"placeholder="请输入密码"v-model.trim="formData.registerPassword"show-password><template #prefix><span class="iconfont icon-password"></span></template></el-input></el-form-item><!-- 再次输入密码 --><el-form-item prop="reRegisterPassword"><el-inputtype="password"size="large"placeholder="请再次输入密码"v-model.trim="formData.reRegisterPassword"show-password><template #prefix><span class="iconfont icon-password"></span></template></el-input></el-form-item></div><!-- 验证码 --><el-form-item prop="checkCode"><div class="check-code-panel"><el-inputsize="large"placeholder="请输入验证码"v-model="formData.checkCode"@keyup.enter="doSubmit"><!-- 插入图标 --><template #prefix><span class="iconfont icon-checkcode"></span></template></el-input><img :src="checkCodeUrl" class="check-code" @click="changeCheckCode(0)"></div></el-form-item><!-- 登录 --><el-form-item v-if="opType==1"><div class="rememberme-panel"><el-checkbox v-model="formData.rememberMe">记住我</el-checkbox></div><div class="no-account"><a href="javascript:void(0)" class="a-link" @click="showPanel(2)">忘记密码?</a><a href="javascript:void(0)" class="a-link" @click="showPanel(0)">没有账号?</a></div></el-form-item><!-- 找回密码时2想起密码,去登陆?点击去登陆1 --><el-form-item v-if="opType == 2"><a href="javascript:void(0)" class="a-link" @click="showPanel(1)">去登陆?</a></el-form-item><!-- 注册时0,想起已有账号?点击去登陆1 --><el-form-item v-if="opType == 0"><a href="javascript:void(0)" class="a-link" @click="showPanel(1)">已有账号?</a></el-form-item><!-- 注册登录重置按钮 --><el-form-item><el-button class="op-btn" type="primary" size="large" @click="doSubmit"><span v-if="opType == 0">注册</span><span v-if="opType == 1">登录</span><span v-if="opType == 2">重置密码</span></el-button></el-form-item><!-- qq登录 --><div class="login-btn-qq" v-if="opType == 1">快捷登录<img src="@/assets/qq.png" @click="qqLogin" /></div></el-form></div><Dialog:show="dialogConfig4SendMailCode.show":title="dialogConfig4SendMailCode.title":buttons="dialogConfig4SendMailCode.buttons"width="500px":showCancel="false"@close="dialogConfig4SendMailCode.show = false"><el-form:model="formData4SendMailCode":rules="rules"ref="formData4SendMailCodeRef"label-width="80px"><!--展示邮箱--><el-form-item label="邮箱">{{ formData.email }}</el-form-item><!--验证码输入--><el-form-item label="验证码" prop="checkCode"><div class="check-code-panel"><el-inputsize="large"placeholder="请输入验证码"v-model.trim="formData4SendMailCode.checkCode"><template #prefix><span class="iconfont icon-checkcode"></span></template></el-input><img:src="checkCodeUrl4SendMailCode"class="check-code"@click="changeCheckCode(1)"/></div></el-form-item></el-form></Dialog></div>
</template><script setup>
import { ref, reactive, getCurrentInstance,nextTick,onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import md5 from "js-md5";
// import axios from 'axios';const { proxy } = getCurrentInstance();
const router = useRouter();
const route = useRoute();
// 定义api
const api = {checkCode:'/api/checkCode',sendMailCode: "/sendEmailCode",register: "/register",login: "/login",resetPwd: "/resetPwd",qqlogin: "/qqlogin",
}
// 操作类型 0:注册 1:登录 2:重置密码
const opType = ref(1)
const showPanel=(type)=>{opType.value=typeresetForm();
}
onMounted(() => {showPanel(1);
});
// 校验再次输入的密码
const checkRePassword = (rule, value, callback) => {if (value !== formData.value.registerPassword) {callback(new Error(rule.message));} else {callback();}
};
// 登录页面
const formData = ref({});
const formDataRef = ref();
// 校验规则
const rules = {email: [{ required: true, message: "请输入邮箱" },{ validator: proxy.Verify.email, message: "请输入正确的邮箱" },],password: [{ required: true, message: "请输入密码" }],emailCode: [{ required: true, message: "请输入邮箱验证码" }],nickName: [{ required: true, message: "请输入昵称" }],registerPassword: [{ required: true, message: "请输入密码" },{validator: proxy.Verify.password,message: "密码只能是数字,字母,特殊字符 8-18位",},],reRegisterPassword: [{ required: true, message: "请再次输入密码" },{validator: checkRePassword,message: "两次输入的密码不一致",},],checkCode: [{ required: true, message: "请输入图片验证码" }],
};
// 连接后台,显示验证码
const checkCodeUrl = ref(api.checkCode)
const checkCodeUrl4SendMailCode = ref(api.checkCode)
// 验证码变化
const changeCheckCode=(type)=>{if(type==0){checkCodeUrl.value =api.checkCode + "?type=" + type + "&time=" + new Date().getTime();}else{checkCodeUrl4SendMailCode.value =api.checkCode + "?type=" + type + "&time=" + new Date().getTime();}
}
// 注册界面 发送邮箱验证码 定义属性
const formData4SendMailCode = ref({});
const formData4SendMailCodeRef = ref();
const dialogConfig4SendMailCode = reactive({show: false,title: "发送邮箱验证码",buttons: [{type: "primary",text: "发送验证码",click: (e) => {sendEmailCode();// submitForm()},},],
});
// 获取邮箱验证码
const getEmailCode = () => {formDataRef.value.validateField("email", (valid) => {if (!valid) {return;}dialogConfig4SendMailCode.show = true;// 清空验证码nextTick(() => {changeCheckCode(1);formData4SendMailCodeRef.value.resetFields();formData4SendMailCode.value = {email: formData.value.email,};});})
}
// 发送邮箱验证码
// 0:注册 1:找回密码
const sendEmailCode = () => {formData4SendMailCodeRef.value.validate(async (valid) => {if (!valid) {return;}const params = Object.assign({}, formData4SendMailCode.value);params.type = opType.value == 0 ? 0 : 1;let result = await proxy.Request({url: api.sendMailCode,params: params,errorCallback: () => {changeCheckCode(1);},});if (!result) {return;}proxy.Message.success("验证码发送成功,请登录邮箱查看");dialogConfig4SendMailCode.show = false;})
}// 重置表单(清空表单)
const resetForm = () => {nextTick(() => {changeCheckCode(0);formDataRef.value.resetFields();formData.value = {};// 登录if (opType.value == 1) {const cookieLoginInfo = proxy.VueCookies.get("loginInfo");if (cookieLoginInfo) {formData.value = cookieLoginInfo;}}});
};
// 登录、注册、重置、提交表单
const doSubmit = () => {formDataRef.value.validate(async (valid) => {if (!valid) {return;}let params = {};Object.assign(params, formData.value);// 注册if (opType.value == 0 || opType.value == 2) {params.password = params.registerPassword;delete params.registerPassword;delete params.reRegisterPassword;}// 登录if (opType.value == 1) {let cookieLoginInfo = proxy.VueCookies.get("loginInfo");let cookiePassword =cookieLoginInfo == null ? null : cookieLoginInfo.password;// 现在的密码和原来的密码不相等的情况下,对当前密码进行md5加密if (params.password !== cookiePassword) {params.password = md5(params.password);}}// 发送http请求let url = null;if (opType.value == 0) {url = api.register;} else if (opType.value == 1) {url = api.login;} else if (opType.value == 2) {url = api.resetPwd;}let result = await proxy.Request({url: url,params: params,errorCallback: () => {changeCheckCode(0);},});if (!result) {return;}// 注册返回if (opType.value == 0) {proxy.Message.success("注册成功,请登录");showPanel(1);} else if (opType.value == 1) {// 检查是否点击 “记住我”if (params.rememberMe) {const loginInfo = {email: params.email,password: params.password,rememberMe: params.rememberMe,};// 将存储七天proxy.VueCookies.set("loginInfo", loginInfo, "7d");} else {proxy.VueCookies.remove("loginInfo");}proxy.Message.success("登录成功");// 存储cookieproxy.VueCookies.set("userInfo", result.data, 0);// 重定向到原始页面const redirectUrl = route.query.redirectUrl || "/";router.push(redirectUrl);} else if (opType.value == 2) {// 重置密码proxy.Message.success("重置密码成功,请登录");showPanel(1);}});
};
// qq登录
const qqLogin = async () => {let result = await proxy.Request({url: api.qqlogin,params: {callbackUrl: route.query.redirectUrl || "",},});if (!result) return;proxy.VueCookies.remove("userInfo");document.location.href = result.data;
};</script><style lang="scss" scoped>
.login-body {height: calc(100vh);// 把背景图像扩展至足够大,以使背景图像完全覆盖背景区域。background-size: cover;background: url("../assets/login_bg.jpg");display: flex;.bg {flex: 1;background-size: cover;background-position: center;background-size: 800px;background-repeat: no-repeat;background-image: url("../assets/login_img.png");}.login-panel {width: 430px;margin-right: 15%;margin-top: calc((100vh - 500px) / 2);.login-register {padding: 25px;background: #fff;border-radius: 5px;.login-title {text-align: center;font-size: 18px;font-weight: bold;margin-bottom: 20px;}.send-email-panel {display: flex;width: 100%;justify-content: space-between;.send-mail-btn {margin-left: 5px;}}.rememberme-panel {width: 100%;}.no-account {width: 100%;display: flex;justify-content: space-between;}.op-btn {width: 100%;}}}.check-code-panel {width: 100%;display: flex;.check-code {margin-left: 5px;cursor: pointer;}}.login-btn-qq {margin-top: 20px;text-align: center;display: flex;align-items: center;justify-content: center;img {cursor: pointer;margin-left: 10px;width: 20px;}}
}
</style>
三、构建框架页
(1)构建基本框架
src/views/Framework.vue
<template><div class="framework"><!-- 头部 --><div class="header"><!-- 左上角logo --><div class="logo"><span class="iconfont icon-pan"></span><div class="name">uu云盘</div></div><!-- 右侧消息弹框 --><div class="right-panel"><!-- 气泡框 --><el-popover :width="800" trigger="click" :v-model:visible="showUploader" :offset="20" transition="none":hide-after="0" :popper-style="{ padding: '0px' }"><template #reference><span class="iconfont icon-transfer"></span></template><template #default><Uploader ref="uploaderRef" @uploadCallback="uploadCallbackHandler"></Uploader></template></el-popover><!-- 下拉框 --><el-dropdown><!-- 用户信息 --><div class="user-info"><!-- 头像 --><div class="avatar"></div><!-- 昵称 --><span class="nick-name">{{ userInfo.nickName }}</span></div><template #dropdown><el-dropdown-menu><el-dropdown-item>修改头像</el-dropdown-item><el-dropdown-item>修改密码</el-dropdown-item><el-dropdown-item>退出</el-dropdown-item></el-dropdown-menu></template></el-dropdown></div></div><!-- 主体 --><div class="body"><!-- 最左侧菜单栏 一级目录 --><div class="left-sider"><div class="menu-list"><div :class="['menu-item',item.menuCode==currentMenu.menuCode?'active':'']" v-for="item in menus" @click="jump(item)"><!-- 一级菜单图标 --><div :class="['iconfont','icon-'+item.icon]"></div><!-- 一级菜单名字 --><div class="text">{{ item.name }}</div></div></div><!-- 二级菜单目录 --><div class="menu-sub-list"><div :class="[' menu-item-sub',currentPath==sub.path?'active':'']" v-for="sub in currentMenu.children" @click="jump(sub)"><!-- 图标 --><span :class="['iconfont', 'icon-' + sub.icon]" v-if="sub.icon"></span><!-- 名字 --><span class="text">{{ sub.name }}</span></div><div class="tips" v-if="currentMenu && currentMenu.tips">{{ currentMenu.tips }}</div><!-- 下方空间使用 --><div class="space-info"><div>空间使用</div><div class="percent"></div></div></div></div><!-- 中间主体内容 --><div class="body-content"><router-view v-slot="{ Component }"><component :is="Component"></component></router-view></div></div></div> </template><script setup> import {ref,reactive,getCurrentInstance,watch,nextTick,computed, } from "vue"; import { useRouter, useRoute } from "vue-router"; const router = useRouter(); const route = useRoute(); const {proxy} = getCurrentInstance(); const userInfo = ref({nickName:'张三' }); // 菜单栏 const menus = [{icon: "cloude",name: "首页",menuCode: "main",path: "/main/all",allShow: true,children: [{icon: "all",name: "全部",category: "all",path: "/main/all",},{icon: "video",name: "视频",category: "video",path: "/main/video",},{icon: "music",name: "音频",category: "music",path: "/main/music",},{icon: "image",name: "图片",category: "image",path: "/main/image",},{icon: "doc",name: "文档",category: "doc",path: "/main/doc",},{icon: "more",name: "其他",category: "others",path: "/main/others",},],},{path: "/myshare",icon: "share",name: "分享",menuCode: "share",allShow: true,children: [{name: "分享记录",path: "/myshare",},],},{path: "/recycle",icon: "del",name: "回收站",menuCode: "recycle",tips: "回收站为你保存10天内删除的文件",allShow: true,children: [{name: "删除的文件",path: "/recycle",},],},{path: "/settings/fileList",icon: "settings",name: "设置",menuCode: "settings",allShow: false,children: [{name: "用户文件",path: "/settings/fileList",},{name: "用户管理",path: "/settings/userList",},{path: "/settings/sysSetting",name: "系统设置",},],}, ]; const currentMenu = ref({}); const currentPath = ref();// 点击一级菜单栏跳转事件回调 const jump = (data) =>{// 判断如果没有路径或者点击的还是当前的路径就不跳转if(!data.path||data.menuCode==currentMenu.value.menuCode){return;}// 否则就跳转到data.pathrouter.push(data.path); } // 设置当前菜单栏 const setMenu = (menuCode, path) => {const menu = menus.find((item) => {return item.menuCode === menuCode;});currentMenu.value = menu;currentPath.value = path; }; // 监听 watch(() => route,(newVal, oldVal) => {if (newVal.meta.menuCode) {setMenu(newVal.meta.menuCode, newVal.path);}},{ immediate: true, deep: true } ); </script><style lang="scss" scoped> .header {box-shadow: 0 3px 10px 0 rgb(0 0 0 / 6%);height: 56px;padding-left: 24px;padding-right: 24px;position: relative;z-index: 200;display: flex;align-items: center;justify-content: space-between;.logo {display: flex;align-items: center;.icon-pan {font-size: 40px;color: #1296db;}.name {font-weight: bold;margin-left: 5px;font-size: 25px;color: #05a1f5;}}.right-panel {display: flex;align-items: center;.icon-transfer {cursor: pointer;}.user-info {margin-right: 10px;display: flex;align-items: center;cursor: pointer;// 头像.avatar {margin: 0px 5px 0px 15px;}// 昵称.nick-name {color: #05a1f5;}}} }.body {display: flex;.left-sider {border-right: 1px solid #f1f2f4;display: flex;.menu-list {height: calc(100vh - 56px);width: 80px;box-shadow: 0 3px 10px 0 rgb(0 0 0 / 6%);border-right: 1px solid #f1f2f4;.menu-item {text-align: center;font-size: 14px;font-weight: bold;padding: 20px 0px;cursor: pointer;&:hover {background: #f3f3f3;}.iconfont {font-weight: normal;font-size: 28px;}}.active {.iconfont {color: #06a7ff;}.text {color: #06a7ff;}}}.menu-sub-list {width: 200px;padding: 20px 10px 0px;position: relative;.menu-item-sub {text-align: center;line-height: 40px;border-radius: 5px;cursor: pointer;&:hover {background: #f3f3f3;}.iconfont {font-size: 14px;margin-right: 20px;}.text {font-size: 13px;}}.active {background: #eef9fe;.iconfont {color: #05a1f5;}.text {color: #05a1f5;}}.tips {margin-top: 10px;color: #888888;font-size: 13px;}.space-info {position: absolute;bottom: 10px;width: 100%;padding: 0px 5px;.percent {padding-right: 10px;}.space-use {margin-top: 5px;color: #7e7e7e;display: flex;justify-content: space-around;.use {flex: 1;}.iconfont {cursor: pointer;margin-right: 20px;color: #05a1f5;}}}}}.body-content {flex: 1;width: 0;padding-left: 20px;} } </style>
(2)添加路由
./src/router/index.js{path: '/',name: 'Framework',component: ()=> import('@/views/Framework.vue'),children: [{path: '/',redirect: "/main/all"},{path: '/main/:category',name: '首页',meta: {needLogin: true,menuCode: "main"},component: () =>import ("@/views/main/Main.vue")},{path: '/myshare',name: '我的分享',meta: {needLogin: true,menuCode: "share"},component: () =>import ("@/views/share/Share.vue")},{path: '/recycle',name: '回收站',meta: {needLogin: true,menuCode: "recycle"},component: () =>import ("@/views/recycle/Recycle.vue")},{path: '/settings/sysSetting',name: '系统设置',meta: {needLogin: true,menuCode: "settings"},component: () =>import ("@/views/admin/SysSettings.vue")},{path: '/settings/userList',name: '用户管理',meta: {needLogin: true,menuCode: "settings"},component: () =>import ("@/views/admin/UserList.vue")},{path: '/settings/fileList',name: '用户文件',meta: {needLogin: true,menuCode: "settings"},component: () =>import ("@/views/admin/FileList.vue")},]},
(3)添加如下组件
(4)main.js引入使用
import Avatar from '@/components/Avatar.vue'
(5)./src/components/Avatar.vue<template><!-- 头像组件 --><span class="avatar"><img :src="avatar && avatar != ''? avatar: `${proxy.globalInfo.avatarUrl}${userId}?${timestamp}`" v-if="userId" /></span> </template><script setup> import { getCurrentInstance } from "vue"; // 获取当前组件实例 const { proxy } = getCurrentInstance();const props = defineProps({userId: {type: String,},avatar: {type: String,},timestamp: {type: Number,default: 0,},width: {type: Number,default: 40,}, }); </script><style lang="scss" scoped> .avatar {display: flex;width: 40px;height: 40px;border-radius: 50%;overflow: hidden;img {width: 100%;object-fit: cover;} } </style>
四、上传头像
(1)封装UpdateAvatar.vue组件
./src/views/UpdateAvatar.vue<template><div><!-- 修改头像弹出框 --><Dialog:show="dialogConfig.show":title="dialogConfig.title":buttons="dialogConfig.buttons"width="500px":showCancel="true"@close="dialogConfig.show = false"><el-form:model="formData"ref="formDataRef"label-width="80px"@submit.prevent><!--显示昵称--><el-form-item label="昵称">{{ formData.nickName }}</el-form-item><!--显示头像--><el-form-item label="头像"><AvatarUpload v-model="formData.avatar"></AvatarUpload></el-form-item></el-form></Dialog></div> </template><script setup> // 引入头像上传组件 import AvatarUpload from "@/components/AvatarUpload.vue"; import { ref, reactive, getCurrentInstance } from "vue"; import { useRouter, useRoute } from "vue-router";const { proxy } = getCurrentInstance(); const router = useRouter(); const route = useRoute();const api = {updateUserAvatar: "updateUserAvatar", };const formData = ref({}); const formDataRef = ref();const show = (data) => {formData.value = Object.assign({}, data);formData.value.avatar = { userId: data.userId, qqAvatar: data.avatar };dialogConfig.value.show = true; }; // 子组件暴露自己的属性 // 父组件需要调用子组件的方法父组件需要调用子组件的方法, // 或者访问子组件的变量 defineExpose({ show });// 定义弹出框的属性 const dialogConfig = ref({show: false,title: "修改头像",buttons: [{type: "primary",text: "确定",click: (e) => {submitForm();},},], });// 1、在子组件中调用defineEmits并定义要发射给父组件的方法 // 2、使用defineEmits会返回一个方法,使用一个变量emit(变量名随意)去接收 // 3、在子组件要触发的方法中,调用emit并传入发射给 父组件的方法(updateAvatar) const emit = defineEmits(["updateAvatar"]); // 点击确定 提交的回调 const submitForm = async () => {// 如果上传的不是文件,将关闭弹出框if (!(formData.value.avatar instanceof File)) {dialogConfig.value.show = false;}let result = await proxy.Request({url: api.updateUserAvatar,params: {avatar: formData.value.avatar,},});if (!result) {return;}dialogConfig.value.show = false;const cookeUserInfo = proxy.VueCookies.get("userInfo");delete cookeUserInfo.avatar;proxy.VueCookies.set("userInfo", cookeUserInfo, 0);// 在子组件要触发的方法中,调用emit并传入发射给 父组件的方法(updateAvatar)emit("updateAvatar"); }; </script><style lang="scss"> </style>
(2)封装全局组件AvatarUpload(上传图片)
./src/commponents/AvatarUpload.vue<template><!-- 头像上传 --><div class="avatar-upload"><div class="avatar-show"><template v-if="localFile"><img :src="localFile" /></template><template v-else><img:src="`${modelValue.qqAvatar}`"v-if="modelValue && modelValue.qqAvatar"/><img :src="`/api/getAvatar/${modelValue.userId}`" v-else /></template></div><div class="select-btn"><el-uploadname="file":show-file-list="false"accept=".png,.PNG,.jpg,.JPG,.jpeg,.JPEG,.gif,.GIF,.bmp,.BMP":multiple="false":http-request="uploadImage"><el-button type="primary">选择</el-button></el-upload></div></div> </template><script setup> import { ref, reactive, getCurrentInstance } from "vue"; import { useRouter, useRoute } from "vue-router";const { proxy } = getCurrentInstance(); const router = useRouter(); const route = useRoute();const timestamp = ref("");// defineProps是一个函数 定义后props可直接在模板中使用,或者在setup其他地方使用 const props = defineProps({modelValue: {type: Object,default: null,}, });// 本地图片 const localFile = ref(null); // 子组件向父组件传值 const emit = defineEmits(); // 上传图片 const uploadImage = async (file) => {file = file.file;let img = new FileReader();img.readAsDataURL(file);img.onload = ({ target }) => {localFile.value = target.result;};emit("update:modelValue", file); }; </script><style lang="scss"> .avatar-upload {display: flex;justify-content: center;align-items: end;.avatar-show {background: rgb(245, 245, 245);width: 150px;height: 150px;display: flex;align-items: center;justify-content: center;overflow: hidden;position: relative;.iconfont {font-size: 50px;color: #ddd;}img {width: 100%;height: 100%;}.op {position: absolute;color: #0e8aef;top: 80px;}}.select-btn {margin-left: 10px;vertical-align: bottom;} } </style>
(3)Framework.vue中引入使用
<!-- 修改头像组件 --> <UpdateAvatar ref="updateAvatarRef" @updateAvatar="reloadAvatar"></UpdateAvatar> import UpdateAvatar from "./UpdateAvatar.vue";
(4)添加点击事件
<el-dropdown-item @click="updateAvatar">修改头像</el-dropdown-item> ...... // 修改头像 const updateAvatarRef = ref(); // 利用defineExpose,父组件调用子组件的函数, // 将用户信息利用show传递给子组件的show函数,使得子组件更新信息 const updateAvatar = () => {updateAvatarRef.value.show(userInfo.value); }; // 重新加载最新头像,利用子组件的 emit("updateAvatar"); 传递回来的信息, const reloadAvatar = () => {userInfo.value = proxy.VueCookies.get("userInfo");timestamp.value = new Date().getTime(); };
五、修改密码
(1)封装UpdatePassword.vue组件
./src/views/UpdatePassword.vue
<template><div><!-- 修改头像弹出框 --><Dialog:show="dialogConfig.show":title="dialogConfig.title":buttons="dialogConfig.buttons"width="500px":showCancel="true"@close="dialogConfig.show = false"><el-form:model="formData":rules="rules"ref="formDataRef"label-width="80px"@submit.prevent><!--输入新密码--><el-form-item label="新密码" prop="password"><el-inputtype="password"size="large"placeholder="请输入密码"v-model.trim="formData.password"show-password><template #prefix><span class="iconfont icon-password"></span></template></el-input></el-form-item><!-- 再次输入密码 --><!--输入新密码--><el-form-item label="确认密码" prop="rePassword"><el-inputtype="password"size="large"placeholder="请再次输入密码"v-model.trim="formData.rePassword"show-password><template #prefix><span class="iconfont icon-password"></span></template></el-input></el-form-item></el-form></Dialog></div> </template><script setup> import AvatarUpload from "@/components/AvatarUpload.vue"; import { ref, reactive, getCurrentInstance, nextTick } from "vue";const { proxy } = getCurrentInstance(); const api = {updatePassword: "updatePassword", };const formData = ref({}); const formDataRef = ref();// 校验再次输入的密码 const checkRePassword = (rule, value, callback) => {if (value !== formData.value.rePassword) {callback(new Error(rule.message));} else {callback();} }; const rules = {password: [{ required: true, message: "请输入密码" },{validator: proxy.Verify.password,message: "密码只能是数字,字母,特殊字符 8-18 位",},],rePassword: [{ required: true, message: "请再次输入密码" },{validator: checkRePassword,message: "两次输入的密码不一致",},], };const show = () => {dialogConfig.value.show = true;nextTick(() => {formDataRef.value.resetFields();formData.value = {};}); }; // 子组件暴露自己的属性 // 父组件需要调用子组件的方法父组件需要调用子组件的方法, // 或者访问子组件的变量 defineExpose({ show });const dialogConfig = ref({show: false,title: "修改密码",buttons: [{type: "primary",text: "确定",click: (e) => {submitForm();},},], });const submitForm = async () => {formDataRef.value.validate(async (valid) => {if (!valid) {return;}let result = await proxy.Request({url: api.updatePassword,params: {password: formData.value.password,},});if (!result) {return;}dialogConfig.value.show = false;proxy.Message.success("密码修改成功");}); }; </script><style lang="scss"> </style>
(2)在Framework.vue引入,使用组件
<!-- 修改密码组件 --><UpdatePassword ref="updatePasswordRef"></UpdatePassword>
import UpdatePassword from "./UpdatePassword.vue";
(3)添加点击事件,点击事件回调
<el-dropdown-item @click="updatePassword">修改密码</el-dropdown-item>
// 修改密码 const updatePasswordRef = ref(); const updatePassword = () => {updatePasswordRef.value.show(); };
六、退出登录
(1)封装全局组件
./src/utils/Confirm.jsimport { ElMessageBox } from 'element-plus'const confirm = (message, okfun) => {ElMessageBox.confirm(message, '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'info',}).then(() => {okfun()}).catch(() => {}) }export default confirm;
(2)main.js引入和配置
import Confirm from './utils/Confirm'; app.config.globalProperties.Confirm=Confirm
(3)添加api接口
const api = {// getUseSpace: "/getUseSpace",logout: "/logout", };
(4)添加点击事件
<el-dropdown-item @click="logout">退出</el-dropdown-item> // 退出登录 const logout = () => {proxy.Confirm(`你确定要删除退出吗`, async () => {let result = await proxy.Request({url: api.logout,});if (!result) {return;}proxy.VueCookies.remove("userInfo");router.push("/login");}); };