网站前台做哪些工作内容,做设计需要知道的几个网站,做外贸商城网站,做爰明星视频网站目录
01: 通用组件#xff1a;input 构建方案分析
02: 通用组件#xff1a;input 构建方案
03: 构建用户资料基础样式
04: 用户基本资料修改方案
05: 处理不保存时的同步问题
06: 头像修改方案流程分析
07: 通用组件#xff1a;Dialog 构建方案分析
08: 通用组件input 构建方案分析
02: 通用组件input 构建方案
03: 构建用户资料基础样式
04: 用户基本资料修改方案
05: 处理不保存时的同步问题
06: 头像修改方案流程分析
07: 通用组件Dialog 构建方案分析
08: 通用组件Dialog 构建方案
09: 应用 Dialog 展示头像
10: 头像裁剪构建方案
11. 阿里云 OSS 与腾讯云 COS 对象存储方案分析
腾讯云 COS
COS SDKCOS 的包
阿里云 OSS
OSS 基础概念
创建存储桶Bucket
使用 STS 临时访问凭证访问 OSS
上传图片到 Bucket 的流程分析
配置 CORS 跨域处理
12. 使用临时凭证上传裁剪图片到阿里云 OSS
13. 完成头像更新操作
14. 登录鉴权解决方案
15: 总结 01: 通用组件input 构建方案分析 期望通用组件 input 至少满足 4 个功能 1. 支持单行文本输入 2. 支持多行文本输入 3. 通过 v-model 实现双向数据绑定 4. 支持最大文本输入
根据以上功能点可判断出 input 组件要有 3 个 prop 1. v-model 2. type单行 or 多行 3. max支持最大字符数
02: 通用组件input 构建方案
- src/libs
- - input
- - - index.vue
// src/libs/input/index.vuetemplatediv classrelativeinputv-iftype TYPE_TEXTclassborder-gray-200 dark:border-zinc-600 dark:bg-zinc-800 duration-100 dark:text-zinc-400 border-[1px] outline-0 py-0.5 px-1 text-sm rounded-sm focus:border-blue-400 w-fulltypetextv-modeltext:maxlengthmax/textareav-iftype TYPE_TEXTAREAv-modeltext:maxlengthmaxrows5classborder-gray-200 dark:border-zinc-600 dark:bg-zinc-800 duration-100 dark:text-zinc-400 border-[1px] outline-0 py-0.5 px-1 text-sm rounded-sm focus:border-blue-400 w-full/textareaspanv-ifmaxclassabsolute right-1 bottom-0.5 text-zinc-400 text-xs:class{ text-red-700: currentNumber parseInt(max) }{{ currentNumber }} / {{ max }}/span/div
/templatescript
const TYPE_TEXT text
const TYPE_TEXTAREA textarea
/scriptscript setup
import { useVModel } from vueuse/core
import { computed } from vueconst props defineProps({modelValue: {required: true,type: String},type: {type: String,default: TYPE_TEXT,validator(value) {const arr [TYPE_TEXT, TYPE_TEXTAREA]const result arr.includes(value)if (!result) {throw new Error(type 的值必须在可选范围内 [${arr.join(、)}])}return result}},max: {type: [String, Number]}
})// 事件声明
defineEmits([update:modelValue])// 输入的字符
const text useVModel(props)// 输入的字符数
const currentNumber computed(() {return text.value?.length
})
/scriptstyle langscss scoped/style03: 构建用户资料基础样式
tailwindcss 先构建移动端再构建 PC 端更方便。
- src/views
- - profile
- - - index.vue
// 路由信息{path: /profile,name: profile,component: () import(/views/profile/index.vue),// 标记当前的页面只有用户登录之后才可以进入meta: {user: true}
},
// src/views/profile/index.vuetemplatedivclassh-full bg-zinc-200 dark:bg-zinc-800 duration-400 overflow-auto xl:pt-1divclassrelative max-w-screen-lg mx-auto bg-white dark:bg-zinc-900 duration-400 xl:rounded-sm xl:border-zinc-200 xl:dark:border-zinc-600 xl:border-[1px] xl:px-4 xl:py-2!-- 移动端 navbar --m-navbar sticky v-ifisMobileTerminal :clickLeftonNavbarLeftClick个人资料/m-navbar!-- pc 端 --div v-else classtext-lg font-bold text-center mb-4 dark:text-zinc-300个人资料/divdiv classh-full w-full px-1 pb-4 text-sm mt-2 xl:w-2/3 xl:pb-0!-- 头像 --div classpy-1 xl:absolute xl:right-[16%] xl:text-centerspanclassw-8 inline-block mb-2 font-bold text-sm dark:text-zinc-300 xl:block xl:mx-auto我的头像/span!-- 头像部分 --divclassrelative w-[80px] h-[80px] group xl:cursor-pointer xl:left-[50%] xl:translate-x-[-50%]clickonAvatarClickimgv-lazy:src$store.getters.userInfo.avataraltclassrounded-[50%] w-full h-full xl:inline-block/divclassabsolute top-0 rounded-[50%] w-full h-full bg-[rgba(0,0,0,.4)] hidden xl:group-hover:blockm-svg-iconnamechange-header-imageclassw-2 h-2 m-auto mt-2/m-svg-icondivclasstext-xs text-white dark:text-zinc-300 scale-90 mt-0.5点击更换头像/div/div/div!-- 隐藏域 --inputv-showfalserefinputFileTargettypefileaccept.png, .jpeg, .jpg, .gifchangeonSelectImgHandler/p classmt-1 text-zinc-500 dark:text-zinc-400 text-xs xl:w-10支持 jpg、png、jpeg 格式大小 5M 以内的图片/p/div!-- 用户名 --div classpy-1 xl:flex xl:items-center xl:my-1span classw-8 block mb-1 font-bold dark:text-zinc-300 xl:mb-0用户名/spanm-inputv-modeluserInfo.nicknameclassw-fulltypetextmax20/m-input/div!-- 职位 --div classpy-1 xl:flex xl:items-center xl:my-1span classw-8 block mb-1 font-bold dark:text-zinc-300 xl:mb-0职位/spanm-inputv-modeluserInfo.titleclassw-fulltypetext/m-input/div!-- 公司 --div classpy-1 xl:flex xl:items-center xl:my-1span classw-8 block mb-1 font-bold dark:text-zinc-300 xl:mb-0公司/spanm-inputv-modeluserInfo.companyclassw-fulltypetext/m-input/div!-- 个人主页 --div classpy-1 xl:flex xl:items-center xl:my-1span classw-8 block mb-1 font-bold dark:text-zinc-300 xl:mb-0个人主页/spanm-inputv-modeluserInfo.homePageclassw-fulltypetext/m-input/div!-- 个人介绍 --div classpy-1 xl:flex xl:my-1span classw-8 block mb-1 font-bold dark:text-zinc-300 xl:mb-0个人介绍/spanm-inputv-modeluserInfo.introductionclassw-fulltypetextareamax50/m-input/div!-- 保存修改 --m-buttonclassw-full mt-2 mb-4 dark:text-zinc-300 dark:bg-zinc-800 xl:w-[160px] xl:ml-[50%] xl:translate-x-[-50%]:loadingloadingclickonChangeProfile保存修改/m-button!-- 移动端退出登录 --m-buttonv-ifisMobileTerminalclassw-full dark:text-zinc-300 dark:bg-zinc-800 xl:w-[160px] xl:ml-[50%] xl:translate-x-[-50%]clickonLogoutClick退出登录/m-button/div/div!-- PC 端 --m-dialog v-if!isMobileTerminal v-modelisDialogVisiblechange-avatar-vue:blobcurrentBolbcloseisDialogVisible false/change-avatar-vue/m-dialog!-- 移动端 --m-popupv-else:class{ h-screen: isDialogVisible }v-modelisDialogVisiblechange-avatar-vue:blobcurrentBolbcloseisDialogVisible false/change-avatar-vue/m-popup/div
/templatescript
export default {name: profile
}
/scriptscript setup
import { isMobileTerminal } from /utils/flexible
import { putProfile } from /api/sys
import { message, confirm } from /libs
import { useRouter } from vue-router
import { useStore } from vuex
import { ref, watch } from vue
import changeAvatarVue from ./components/change-avatar.vueconst store useStore()
const router useRouter()// 隐藏域
const inputFileTarget ref(null)
// 头像 dialog 展示
const isDialogVisible ref(false)
// 选中的图片
const currentBolb ref()
/*** 更换头像点击事件*/
const onAvatarClick () {inputFileTarget.value.click()
}/*** 头像选择之后的回调*/
const onSelectImgHandler () {// 获取选中的文件const imgFile inputFileTarget.value.files[0]// 生成 blob 对象const blob URL.createObjectURL(imgFile)// 获取选中的图片currentBolb.value blob// 展示 DialogisDialogVisible.value true
}/*** 监听 dialog 关闭*/
watch(isDialogVisible, (val) {if (!val) {// 防止 change 不重复触发inputFileTarget.value.value null}
})/*** 数据本地的双向同步增加一个单层深拷贝*/
const userInfo ref({ ...store.getters.userInfo })
// const changeStoreUserInfo (key, value) {
// store.commit(user/setUserInfo, {
// ...store.getters.userInfo,
// [key]: value
// })
// }/*** 修改个人信息*/
const loading ref(false)
const onChangeProfile async () {loading.value trueawait putProfile(userInfo.value)message(success, 用户信息修改成功)// 更新 vuexstore.commit(user/setUserInfo, userInfo.value)loading.value false
}/*** 移动端后退处理*/
const onNavbarLeftClick () {// 配置跳转方式store.commit(app/changeRouterType, back)router.back()
}/*** 移动端退出登录*/
const onLogoutClick () {confirm(确定要退出登录吗).then(() {store.dispatch(user/logout)})
}
/scriptstyle langscss scoped/style
04: 用户基本资料修改方案
// 第一种方式直接修改 vuex state 数据违背 vue 理念。m-input v-model$store.getters.userInfo.nickname /// 第二种方式优化第一种方式。m-input :modelValue$store.getters.userInfo.nicknameupdate:modelValuechangeStoreUserInfo(nickname, $event)
/const changeStoreUserInfo (key, value) {store.commit(user/setUserInfo, {...store.getters.userInfo,[key]: value})
}// 第三种方式解决用户信息未提交但提前缓存问题。推荐。m-input v-modeluserInfo.nickname //*** 数据本地的双向同步增加一个单层深拷贝*/
const userInfo ref({ ...store.getters.userInfo })/*** 修改个人信息*/
const onChangeProfile async () {// 发送修改请求 代码省略// 更新 vuexstore.commit(user/setUserInfo, userInfo.value)
}
05: 处理不保存时的同步问题
讲解一下 v-model 拆解的问题。
给不理解的同学讲解一下为什么不能用 v-model 直接绑定 vuex 中的数据以及绑定之后会产生什么样的问题
思路及代码在上一小节之中。
06: 头像修改方案流程分析
接下来我们需要处理头像修改的业务逻辑。
对于该功能而言分为 PC 端和 移动端 两种情况我们需要分别进行处理
1. PC 端 1. 点击更换头像。 2. 选择对应文件。 3. 通过 Dialog 展示图片剪裁。 4. 剪裁后图片上传。 5. 功能完成。
2. 移动端 1. 点击更换头像。 2. 选择对应文件。 3. 通过 popup 展示图片剪裁。 4. 剪裁后图片上传。 5. 功能完成。
由此可以发现两者之间需要通过不同的组件进行裁剪展示。
因此我们后续的开发流程为 1. 通用组件Dialog。 2. 处理图片剪裁。 3. 处理剪裁后上传。
07: 通用组件Dialog 构建方案分析
首先我们来处理 Dialog 通用组件。
对于 Dialog 通用组件而言我们可以参考 confirm 组件的构建过程。
它们两个构建方案非常相似唯二不同的地方是 1. Dialog 无需通过方法调用的形式展示。 2. Dialog 的内容区可以渲染任意的内容。
排除这两点之后其余与 confirm 完全相同。
08: 通用组件Dialog 构建方案
- src/libs
- - dialog
- - - index.vue
templatediv!-- 蒙版 --transition namefadedivv-ifisVisableclickcloseclassw-screen h-screen bg-zinc-900/80 z-40 fixed top-0 left-0/div/transition!-- 内容 --transition nameupdivv-ifisVisableclassmax-w-[80%] max-h-[80%] overflow-auto fixed top-[10%] left-[50%] translate-x-[-50%] z-50 px-2 py-1.5 rounded-sm border dark:border-zinc-600 cursor-pointer bg-white dark:bg-zinc-800 xl:min-w-[35%]!-- 标题 --divclasstext-lg font-bold text-zinc-900 dark:text-zinc-200 mb-2v-iftitle{{ title }}/div!-- 内容 --div classtext-base text-zinc-900 dark:text-zinc-200 mb-2slot //div!-- 按钮 --div classflex justify-end v-ifcancelHandler || confirmHandlerm-button typeinfo classmr-2 clickonCancelClick{{cancelText}}/m-buttonm-button typeprimary clickonConfirmClick{{confirmText}}/m-button/div/div/transition/div
/templatescript setup
import { useVModel } from vueuse/coreconst props defineProps({// 控制开关modelValue: {type: Boolean,required: true},// 标题title: {type: String},// 取消按钮文本cancelText: {type: String,default: 取消},// 确定按钮文本confirmText: {type: String,default: 确定},// 取消按钮点击事件cancelHandler: {type: Function},// 确定按钮点击事件confirmHandler: {type: Function},// 关闭的回调close: {type: Function}
})defineEmits([update:modelValue])// 控制显示处理
const isVisable useVModel(props)/*** 取消按钮点击事件*/
const onCancelClick () {if (props.cancelHandler) {props.cancelHandler()}close()
}/*** 确定按钮点击事件*/
const onConfirmClick () {if (props.confirmHandler) {props.confirmHandler()}close()
}const close () {isVisable.value falseif (props.close) {props.close()}
}
/scriptstyle langscss scoped
.fade-enter-active,
.fade-leave-active {transition: all 0.3s;
}.fade-enter-from,
.fade-leave-to {opacity: 0;
}.up-enter-active,
.up-leave-active {transition: all 0.3s;
}.up-enter-from,
.up-leave-to {opacity: 0;transform: translate3d(-50%, 100px, 0);
}
/style09: 应用 Dialog 展示头像
// src/views/profile/index.vuetemplateimg :srccurrentBolb /
/templatescript setup// 选中的图片
const currentBolb ref()/*** 头像选择之后的回调*/
const onSelectImgHandler () {// 获取选中的文件const imgFile inputFileTarget.value.files[0]// 生成 blob 对象const blob URL.createObjectURL(imgFile)// 获取选中的图片currentBolb.value blob// 展示 DialogisDialogVisible.value true
}/*** 当 input file 两次选择文件是同一个的时候change 的回调不会被再次触发。* 想要解决这个问题只需要在每次选择的图片不再被使用之后清空掉 input file 的 value。* 监听 dialog 关闭*/
watch(isDialogVisible, (val) {if (!val) {// 防止 change 不重复触发inputFileTarget.value.value null}
})
/script
- src/views/profile
- - components
- - - change-avatar.vue
// src/views/profile/components/change-avatar.vuetemplatediv classoverflow-auto relative flex flex-col items-centerm-svg-iconv-ifisMobileTerminalnamecloseclassw-3 h-3 p-0.5 m-1 ml-autofillClassfill-zinc-900 dark:fill-zinc-200 clickclose/m-svg-iconimg class refimageTarget :srcblob /m-buttonclassmt-4 w-[80%] xl:w-1/2:loadingloadingclickonConfirmClick确定/m-button/div
/templatescript
const EMITS_CLOSE close// 移动端配置对象
const mobileOptions {// 将裁剪框限制在画布的大小viewMode: 1,// 移动画布裁剪框不动dragMode: move,// 裁剪框固定纵横比1:1aspectRatio: 1,// 裁剪框不可移动cropBoxMovable: false,// 不可调整裁剪框大小cropBoxResizable: false
}// PC 端配置对象
const pcOptions {// 裁剪框固定纵横比1:1aspectRatio: 1
}
/scriptscript setup
import { isMobileTerminal } from /utils/flexible
import Cropper from cropperjs
import cropperjs/dist/cropper.css
import { ref, onMounted } from vue
import { getOSSClient } from /utils/sts
import { message } from /libs
import { useStore } from vuex
import { putProfile } from /api/sysdefineProps({blob: {type: String,required: true}
})const emits defineEmits([EMITS_CLOSE])/*** 图片裁剪处理*/
const imageTarget ref(null)
let cropper null
onMounted(() {/*** 接收两个参数* 1. 需要裁剪的图片 DOM* 2. options 配置对象*/cropper new Cropper(imageTarget.value,isMobileTerminal.value ? mobileOptions : pcOptions)
})/*** 确定按钮点击事件*/
const loading ref(false)
const onConfirmClick () {loading.value true// 获取裁剪后的图片cropper.getCroppedCanvas().toBlob((blob) {// 裁剪后的 blob 地址// console.log(URL.createObjectURL(blob))putObjectToOSS(blob)})
}/*** 进行 OSS 上传*/
let ossClient null
let store useStore()
const putObjectToOSS async (file) {if (!ossClient) {ossClient await getOSSClient()}try {// 因为当前凭证只具备 images 文件夹下的访问权限所以图片需要上传到 images/xxx.xx 。否则你将得到一个 《AccessDeniedError: You have no right to access this object because of bucket acl.》 的错误const fileTypeArr file.type.split(/)const fileName ${store.getters.userInfo.username}/${Date.now()}.${fileTypeArr[fileTypeArr.length - 1]}// 文件存放路径文件const res await ossClient.put(images/${fileName}, file)// 通知服务器onChangeProfile(res.url)} catch (e) {message(error, e)}
}/*** 上传新头像到服务器*/
const onChangeProfile async (avatar) {// 更新本地数据store.commit(user/setUserInfo, {...store.getters.userInfo,avatar})// 更新服务器数据await putProfile(store.getters.userInfo)// 通知用户message(success, 用户头像修改成功)// 关闭 loadingloading.value false// 关闭 dialogclose()
}/*** 关闭事件*/
const close () {emits(EMITS_CLOSE)
}
/scriptstyle langscss scoped/style10: 头像裁剪构建方案 接下来我们需要在 src/views/profile/components/change-avatar.vue 中处理对应的图片裁剪功能。 想要处理图片裁剪我们需要使用到 cropperjs它是一个 Javascript 的库同时支持 PC 端 和 移动端。 目前 cropperjs 的最新发布版本为 1.6.2V2 级别的版本还是 RC 阶段所以我们还是使用它的 V1 版本。
1. 安装 cropperjs
npm install cropperjs1.5.12 --save
2. 在 src/views/profile/components/change-acatar.vue 中进行导入
import Cropper from cropperjs
import cropperjs/dist/cropper.css
3. 使用 new Cropper 进行初始化区分 PC端 和 移动端所有配置项
// 移动端配置对象
const mobileOptions {// 将裁剪框限制在画布的大小viewMode: 1,// 移动画布裁剪框不动dragMode: move,// 裁剪框固定纵横比1:1aspectRatio: 1,// 裁剪框不可移动cropBoxMovable: false,// 不可调整裁剪框大小cropBoxResizable: false
}// PC 端配置对象
const pcOptions {// 裁剪框固定纵横比1:1aspectRatio: 1
}
4.
/*** 图片裁剪处理*/
const imageTarget ref(null)
let cropper null
onMounted(() {/*** 接收两个参数* 1. 需要裁剪的图片 DOM* 2. options 配置对象*/cropper new Cropper(imageTarget.value,isMobileTerminal.value ? mobileOptions : pcOptions)
})
5.
/*** 确定按钮点击事件*/
const loading ref(false)
const onConfirmClick () {loading.value true// 获取裁剪后的图片cropper.getCroppedCanvas().toBlob((blob) {// 裁剪后的 blob 地址// console.log(URL.createObjectURL(blob))putObjectToOSS(blob)})
}
11. 阿里云 OSS 与腾讯云 COS 对象存储方案分析
当图片裁剪处理完成之后接下来我们就可以处理裁剪之后的图片上传了。 通常情况下在企业开发中图片或文件的管理都会通过 对象存储 的方案进行。目前国内常见的对象存储云方案主要有两个平台 1. 阿里云 OSS 2. 腾讯云 COS
腾讯云 COS
腾讯云 COS 目前提供了 实名认证赠送 6个月 对象存储的服务点击跳转 这个服务对大家而言是非常好的一个练习服务大家可以直接使用该服务来实现 裁剪图片上传到腾讯云。
以下为腾讯云 COS 使用流程 1. 注册 腾讯云 账号并完成 实名认证。 2. 选择免费产品。 3. 选择腾讯云 COS。 4. 点击立即开通。 5. 创建存储桶。 6. 点击创建。 7. 选择 公有读、私有写。 8. 一路下一步创建即可。
此时你可以得到对应的存储桶所有的文件都会被上传到该存储桶之中。
接下来就可以按照以下步骤进行图片上传
COS SDKCOS 的包
对应文档地址
1. 下载依赖包
npm i cos-js-sdk-v5 --save
2. 构建 cos 实例初始化 cos 对象参数
名称描述SecretId开发者拥有的项目身份识别 ID用以身份认证可在 API 密钥管理 页面获取SecretKey开发者拥有的项目身份密钥可在 API 密钥管理 页面获取
import COS from cos-js-sdk-v5const cos new COS({SecretId: ,SecretKey:
})
3. 执行上传操作
cos.putObject({Bucket: , // 填入您自己的存储痛必须字段Region: , // 存储桶所在地域例如 ap-beijing必须字段Key: params.file.name, // 存储在桶里的对象键例如 1.jpg a/b/test.txt必须字段StorageClass: STANDARD,Body: , // 上传文件对象onProgress: function (progressData) {console.log(JSON.stringify(progressData)}
}, function (err, data) {// 上传成功返回的数据data.location 为图片的地址console.log(err || data)
})
4. 图片上传成功在存储桶中即可查看上传的图片。
阿里云 OSS
目前国内企业使用最多的云服务为 阿里云所以说咱们文章将会以 阿里云 为例进行开发。
阿里云的 OSS 服务有新人三个月的免费试用。 我们将使用 OSS 进行图片的上传。
阿里云 OSS 使用流程 1. 注册登录 阿里云服务。 2. 找到 阿里云 OSS 对象存储服务。 3. 点击 立即开通。 4. 选择 立即开通。 5. 勾选服务点击立即开通。 6. 提示开通成功之后可以进行两个操作。 1. 点击 管理控制台 进入 OSS 控制台。 2. 点击 对象存储新手入门 查看文档。
OSS 基础概念
OSS 中包含了很多的基础概念可以点击 这里 直接进行访问。
需要大家了解的基础概念有 1. Bucket存储空间。 2. Object存储文件。
创建存储桶Bucket
在 控制台 左侧点击 Bucket 列表然后点击 创建 Bucket读写权限为 私有
使用 STS 临时访问凭证访问 OSS
在 Bucket 构建完成之后接下来我们就需要去处理 访问凭证。
注意构建访问凭证会涉及到服务端的配置。在实际开发中不需要 前端工程师来处理访问凭证相关的内容。
具体的构建流程可以点击 这里 进行查看。
名词解释 RAM(Resource Access Management) STS(Security Token Service) ARN是指云服务所定义的资源(Aliyun Resource Name)
上传图片到 Bucket 的流程分析
1. 想要上传文件到 Bucket我们需要使用 ali-sdk ali-oss。
2. 利用 ali-oss 生成 OSS 对象。
3. 在生成 OSS 对象时需要传递 文件上传凭证 1. accessKeyId。 2. accessKeySecret。 3. stsToken。
4. 需要通过接口 /user/sts 获取 文件上传凭证。
整体的文件上传流程为 1. 通过接口 /user/sts 获取 文件上传凭证。 2. 通过 npm i ali-oss 安装依赖包。 3. 使用凭证中的数据构建 OSS 对象。点击这里查看文档。
配置 CORS 跨域处理
当我们尝试使用 put 方法上传文件时会得到一个跨域的错误。
想要处理这个问题则需要 配置跨域规则。点击这里查看配置方案。
12. 使用临时凭证上传裁剪图片到阿里云 OSS
本小节我们将讲解如何将裁剪后的图片上传到阿里云 OSS。
具体步骤如下 1. 安装 ali-oss 依赖。 2. 通过接口获取临时访问凭证生成 OSS 实例。 3. 利用 ossClient.put 方法完成对应上传。
接下来我们一步一步去做
1. 安装 ali-oss 依赖。
npm i --save ali-oss6.17.0
2. 创建 src/utils/sts.js 模块用来生成 OSS 实例。
import OSS from ali-oss
import { REGION, BUCKET } from /constants
import { getSts } from /api/sysexport const getOSSClient async () {const res await getSts()return new OSS({// yourRegion填写Bucket所在地域。以华东1杭州为例Region填写为oss-cn-hangzhou。region: REGION,// 从STS服务获取的临时访问密钥AccessKey ID和AccessKey Secret。accessKeyId: res.Credentials.AccessKeyId,accessKeySecret: res.Credentials.AccessKeySecret,// 从STS服务获取的安全令牌SecurityToken。stsToken: res.Credentials.SecurityToken,// 填写Bucket名称。bucket: BUCKET,// 刷新 token在 token 过期后自动调用但是并不生效可能会在后续的版本中修复refreshSTSToken: async () {// 向您搭建的STS服务获取临时访问凭证。const res await getSts()return {accessKeyId: res.Credentials.AccessKeyId,accessKeySecret: res.Credentials.AccessKeySecret,stsToken: res.Credentials.SecurityToken}},// 刷新临时访问凭证的时间间隔单位为毫秒。refreshSTSTokenInterval: 5 * 1000})
}3. 在 constants 中定义 REGIONBUCKET
// STS 上传数据
export const REGION oss-cn-beijing
export const BUCKET imooc-front
4.
/*** 进行 OSS 上传*/
let ossClient null
let store useStore()
const putObjectToOSS async (file) {if (!ossClient) {ossClient await getOSSClient()}try {// 因为当前凭证只具备 images 文件夹下的访问权限所以图片需要上传到 images/xxx.xx 。否则你将得到一个 《AccessDeniedError: You have no right to access this object because of bucket acl.》 的错误const fileTypeArr file.type.split(/)const fileName ${store.getters.userInfo.username}/${Date.now()}.${fileTypeArr[fileTypeArr.length - 1]}// 文件存放路径文件const res await ossClient.put(images/${fileName}, file)// 通知服务器onChangeProfile(res.url)} catch (e) {message(error, e)}
}/*** 上传新头像到服务器*/
const onChangeProfile async (avatar) {// 更新本地数据store.commit(user/setUserInfo, {...store.getters.userInfo,avatar})// 更新服务器数据await putProfile(store.getters.userInfo)// 通知用户message(success, 用户头像修改成功)// 关闭 loadingloading.value false// 关闭 dialogclose()
}/*** 关闭事件*/
const close () {emits(EMITS_CLOSE)
}
13. 完成头像更新操作 为了代码连贯性故代码写在上一小节中。
14. 登录鉴权解决方案
// src/permission.jsimport router from /router
import store from /store
import { message } from /libs/*** 处理需登录页面的访问权限*/
router.beforeEach((to, from) {// 无需登录的页面访问if (!to.meta.user) {return}// 已登录可进入if (store.getters.token) {return true}// 未登录警告然后返回首页message(warn, 登录失效请重新登录!)return /
})15: 总结
在本篇文章中我们接触到了两个新的通用组件 1. input 2. dialog 除此之外我们还接触到了头像裁剪、OSS、COS 的概念。这些概念可能对有些同学而言会比较新奇。大家也可以自己申请一个对应的 OSS 或者 COS 的账号推荐 COS。走一遍完整的流程这样大家会对这个操作更加的熟悉。