在上一篇文章中,我们做好了头像的后端读取,下面就要进行的是前端对于文件的发送,以及个人中心的资料设定等内容。
前端上传文件
前端上传的时候要选择http请求头类型为multipart/form-data
,需要在axios
请求发送的时候配置。
<script setup>
import axios from 'axios'
import {useUserAccountStore} from '../stores/userAccount'
const userAccountStore = useUserAccountStore();
function upload(event){
const file = event.target.files[0];
console.log(file);
axios.post('/server/uploadAvatar',{
avatar: file,
id: userAccountStore.id,
},{
headers: {'Content-Type':'multipart/form-data'}
})
.then((response)=>{
alert('上传头像成功');
})
.catch((err)=>{
alert(`上传头像失败,${err}`);
})
}
</script>
<template>
<div>
上传头像
<img>
<input type="file" @change="upload"/>
</div>
</template>
<style scoped>
</style>
组件库的安装与使用
为了使用个人中心的编辑生日功能,我们需要一个组件用来选择日期。这种组件写起来既麻烦又难写,对于我们这种比较初级的项目来说,便可以借助别人已经写好的组件库来进行使用。这里我们选择饿了么前端团队推出的element-plus
。
由于我们不需要整个组件库,而是只需要其中的一个或者几个组件,因此我们先来学习按需导入。按需导入使用的是一种叫做Tree Shaking
的技术,借助unplugin-element-plus
和unplugin-auto-import
包。
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
})
]
// ...
})
接下来当我们想使用对应的组件的时候即可直接按需导入。
<h3>出生日期</h3>
<div><el-date-picker></el-date-picker></div>
现在在个人空间里就可以选择自己的出生日期了。
axios的二次封装
在个人信息的前端获取中,我们需要一个方法来获取个人信息,并且对于所有的用户ID,我们需要一个通用的方法来去后端获取。因此我们需要把axios封装成一个新的函数。这里以get方法为例介绍一下axios的二次封装。
function getUserInformationById(id){
return new Promise((resolve, reject)=>{
axios.get(`/server/getInformation/${id}`)
.then((response)=>{
resolve (response.data)
})
})
}
如果想查看其他的封装,可以看下文的完整实现。
个人资料的编辑和保存
接下来我们就可以实现个人资料的编辑和保存了。大致思路是这样的:主页中有个按钮用来控制当前的状态isEditing
,然后组件根据状态变量来显示是保存还是编辑(除了头像之外),然后旁边会附带一个保存按钮,用来把更新后的数据上传。
思路弄好了之后就直接展示最终的代码好了。
这是前端代码:
<script setup>
import { ref } from 'vue';
import uploadAvatar from './uploadAvatar.vue';
import {useUserAccountStore} from '../stores/userAccount'
import { useUserInformationStore } from '../stores/userInformation';
import axios from 'axios';
const userAccountStore = useUserAccountStore();
const userInformationStore = useUserInformationStore();
const uploadAvaterIsOn = ref(false);
const isEditing = ref(false);
const userInformationInput = ref({
id: userAccountStore.id,
username: '',
birthday: '',
avatar: '',
profileText: '',
})
function updateInformation(){
userInformationStore.updateInformation(userInformationInput.value)
.then((val)=>{
isEditing.value = !isEditing.value;
alert(val);
})
}
userInformationStore.getUserInformationById(userAccountStore.id).then((val)=>{
const url = `server/files/avatars/${val.avatar}`;
userInformationInput.value = val;
userInformationInput.value.avatar = url;
})
</script>
<template>
基本信息
<div>
<button v-if="!isEditing" @click="isEditing = !isEditing">编辑</button>
<div v-else>
<button @click="updateInformation">保存</button>
<button @click="isEditing = !isEditing">取消</button>
</div>
<ul>
<li>
<h3 >头像</h3>
<img :src="userInformationInput.avatar"
@click="uploadAvaterIsOn = !uploadAvaterIsOn" id="avatar"/>
</li>
<li>
<h3>用户名</h3>
<div v-if="!isEditing">{{ userInformationInput.username }}</div>
<input v-model="userInformationInput.username" v-else/>
</li>
<li>
<h3>用户id</h3>
<div>{{ userInformationInput.id }}</div>
</li>
<li>
<h3>注册邮箱</h3>
<div>{{ }}</div>
</li>
<li>
<h3>出生日期</h3>
<div v-if="!isEditing" >{{ userInformationInput.birthday }}</div>
<div v-else>
<el-date-picker v-model="userInformationInput.birthday"
value-format="YYYY-MM-DD">
</el-date-picker>
</div>
</li>
<li>
<h3>签名</h3>
<div v-if="!isEditing">{{ userInformationInput.profileText }}</div>
<div v-else><input v-model="userInformationInput.profileText"></div>
</li>
</ul>
</div>
</template>
<style scoped>
#avatar{
height: 200px;
width: 200px;
}
</style>
以及配套的userInformationStore
的代码:
import axios from "axios";
import { defineStore } from "pinia";
export const useUserInformationStore = defineStore('userInformation',()=>{
function getUserInformationById(id){
return new Promise((resolve, reject)=>{
axios.get(`/server/getInformation/${id}`)
.then((response)=>{
resolve(response.data);
})
})
}
function updateInformation(information){
return new Promise((resolve, reject)=>{
axios.post('/server/updateInformation',information)
.then((response)=>{
resolve(response.data);
})
.catch((err)=>{
reject(err);
})
})
}
return{
getUserInformationById,
updateInformation
}
})
对应的后端代码如下:
// getInformation.js
const express = require('express');
const router = express.Router();
const connection = require('../funcitons/sqlConnection');
router.get('/getInformation/:id',(req, res)=>{
const id = req.params.id;
const sql = `select * from userInformation where id = '${id}'`;
connection.query(sql,(err, result)=>{
if(err){
res.status(500).send(err);
}else{
res.send(result[0]);
}
})
})
module.exports = router;
// updateInformation.js
const express = require('express');
const router = express.Router();
const connection = require('../funcitons/sqlConnection');
router.post('/updateInformation',(req, res)=>{
const information ={
username: req.body.username,
birthday: req.body.birthday,
profileText: req.body.profileText
};
const sql = `
update userInformation set username = '${information.username}',
birthday = '${information.birthday}', profileText = '${information.profileText}'
where id = '${req.body.id}'
`;
connection.query(sql, (err, result)=>{
if(err){
res.status(500).send(err);
}else{
res.send('更新完成');
}
});
});
module.exports = router;
在接下来的文章中,我们在搭建完个人中心后,便会着重处理页面外观,包括响应式、ui设计以及组件美化等。