vue实现图片上传功能
一、vue的核心插件vuexvuex用于集中存储管理应用的所有组件的状态(state),在一个项目的开发过程中,如果一些值或者方法被多个组件频繁的使用,就把这些值或者方法定义在vuex中,便于组件的调用。vue-router这是一个Vue的官方路由器,让构建单页面应用变得十分简单。二、服务器代理配置当向服务器发送请求的时候,可以对请求进行处理后再发送,可以在vue.config.js中进行配置mo
·
vue实现图片上传功能
一、vue的核心插件
1. vuex
vuex用于集中存储管理应用的所有组件的状态(state),在一个项目的开发过程中,如果一些值或者方法被多个组件频繁的使用,就把这些值或者方法定义在vuex中,便于组件的调用。
2. vue-router
这是一个Vue的官方路由器,让构建单页面应用变得十分简单。
二、服务器代理配置
当向服务器发送请求的时候,可以对请求进行处理后再发送,可以在vue.config.js中进行配置
module.exports = {
devServer: {
proxy: {
'/api': {
// 指向新路径
target: 'http://localhost:8081',
pathRewrite: {
'^/api': ''
}
}
}
}
}
- 请求服务器之前,如果匹配到url是以/api开头的就进行拦截,指向新的路径,并且可以重写路径,去掉/api,因为目标服务器里没有对应的url,这里写正则
^/api
的时候,不要忘记/
三、路由配置
import Vue from "vue";
import VueRouter from "vue-router";
import store from "../store";
import Login from '../views/Login.vue'
import Photo from '../views/Photo.vue'
Vue.use(VueRouter);
const routes = [
// /login路径,打开Login组件显示在App的view-router处
{
path: '/login',
name: 'Login',
component: Login,
// isAuth,功能优化,当向服务器发送请求的时候,可以检测该路由是否需要鉴权,如果不需要鉴权,可以取消本次的发送请求
meta: {
isAuth: false
}
},
{
// 获取用户信息之后,打开该路由
path: '/photo',
name: 'Photo',
component: Photo,
meta: {
isAuth: true
}
},
{
// 点击图片的时候跳转到该路由
path: '/detail/:id',
name: 'Detail',
// 获取id参数
props: true,
component: () => import('../views/Detail.vue')
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
router.beforeEach((to, from, next) => {
if(to.meta.isAuth){
if(store.state.token){
next()
}else{
next({
name: 'Login'
})
}
}else{
next()
}
})
export default router;
- Vue安装vue-router插件
Vue.use(VueRouter)
- meta里面可以存放一些路由
自定义信息
- 获取路由参数,
props:true
- 动态加载组件
component: () => import('../views/Detail.vue')
,因为用户可能不需要这个功能,如果需要,就动态加载。 - router.beforeEach,可以在这个路由守卫里判断该页面是否存在token,如果有就进入该路由内,如果没有,就跳转到Login路由
四、自定义axios
import axios from 'axios'
import store from './store'
import router from './router'
// 自己配置axios
const myAxios = axios.create({
baseURL: '/api'
})
// 请求拦截器,携带token
myAxios.interceptors.request.use(config => {
// token
const token = store.state.token
if(token){
config.headers.authorization = "Bearer " + token
}
// 不要忘记return配置
return config
})
// 优化
myAxios.interceptors.response.use(res => {
return res
},
err => {
if(err.response.status === 401){
router.replace({
name: 'Login'
})
}
})
export default myAxios
- 在发出axios请求之前加入逻辑
myAxios.interceptors.request.use(config => {...})
在请求发送之前,给头信息添加authorization
- 在axios接收请求之前加入逻辑
myAxios.interceptors.response.use(res=> {...})
当请求状态为401或其它的时候,跳转到登录界面
五、请求服务器逻辑
import http from '../http'
export function apiLogin({username, password}){
// 因为Axios的baseURL的配置,其实访问的是local: 8080/api/login
return http.post('/login',{
username,
password
})
}
export function apiGetPhotos(){
// 服务端请求/getPhotos页面的数据
return http.get('/getPhotos')
}
export function apiUpLoad(file){
const formData = new FormData()
formData.append('img', file)
// 把图片上传到后端。
return http.post('/upload', formData)
}
- 前缀加api是开发规范
- http是自定义的axios,在定义的时候配置了baseURL,所以请求的时候url可以省略
/api
- axios返回的是Promise
六、vuex-store
import Vue from "vue";
import Vuex from "vuex";
import {apiLogin, apiGetPhotos} from '../api'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
token: localStorage.getItem('token') || '',
photos: []
},
getters: {
getPhotoById(state){
// 获取对应图片
return id => {
return state.photos.find(item => item.id == id)
}
}
},
mutations: {
login(state,payload){
state.token = payload
},
updatePhotos(state,payload){
state.photos = payload
}
},
actions: {
// 需要等待获取apiLogind获取数据完成
async getInfo({commit}, payload){
const { username, password } = payload
let res = await apiLogin({username, password})
const data = res.data.data
localStorage.setItem('token',data.token)
commit('login', data.token)
},
getPhotos({commit}){
apiGetPhotos().then(res => {
console.log(res.data)
let data = res.data
commit('updatePhotos',data.data.photos)
})
}
},
modules: {}
});
七、登录界面
实现功能:
- 获取用户信息,生成token,便于后端验证用户信息
- 点击按钮,后端验证用户信息,验证成功跳转到图片展示页面(Photo)
Login.vue
<template>
<div class="loginContainer">
<h1>登录</h1>
<div>
姓名:
<input
class="inputStyle"
id="username"
type="text"
name="username"
v-model="username"
/><br />
密码:
<input
class="inputStyle"
id="password"
type="password"
name="pwd"
v-model="password"
/><br />
<button class="loginStyle" id="loginBtn" @click='getInfo'>登录</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
username: '',
password: ''
}
},
methods: {
getInfo() {
this.$store.dispatch('getInfo', {
username: this.username,
password: this.password
}).then(() =>{
this.$router.push('/photo')
})
}
},
};
</script>
<style lang="scss" scoped></style>
- 当需要“花时间”来完成的行为,就需要调用异步函数
- 获取用户信息后,需要向后端进行验证(需要花时间),验证成功后设置token,并且跳转到photo页面
- 因为多个页面需要用到token,所以就把token存在vuex的state里。
- 调用vuex里的异步函数,需要用到
this.$store.dispatch
八、图片展示界面
页面功能:
- 把数据库的图片显示在
图片展示框
内 - 当点击
上传图片
的时候,出现上传图片框
- 当点击图片的时候,显示图片
- 因为点击
上传按钮
的时候需要把打开上传框
的状态发送给子组件,所以,需要设置一个boolean的属性名(visible)传递给子组件来控制子组件框的开关。
Photo.vue
<template>
<div>
<!-- 展示相关 -->
<div class="container">
<div class="photoHeader">
<div class="imgContainer">
<img class="photoName" src="public/img/1.jpg" />
</div>
<div class="btnContainer">
<span class="photoTitle">相册名称</span>
<button class="mybtn" @click="showUpLoadView = true">上传照片</button>
</div>
</div>
<div class="photoContainer">
<template v-for="item in photos">
<router-link :key="item.id" :to="{path:`/detail/${item.id}`}">
<div class="photoItem" :key="item.id">
<img :src="item.imgUrl" />
<span>
{{ item.name }}
</span>
</div>
</router-link>
</template>
</div>
</div>
<up-load-view :visible.sync="showUpLoadView" />
</div>
</template>
<script>
// 显示上传框的组件
import upLoadView from "./upLoadView.vue";
export default {
components: { upLoadView },
data() {
return {
// 用于控制是否打开子组件框
showUpLoadView: false,
};
},
computed: {
photos() {
// 获取图片信息,展示到DOM里
return this.$store.state.photos;
},
},
created() {
// 页面渲染之前需要获取图片数据
// 一般需要时间的操作都需要用到异步操作
// 这里需要用到异步调用,因为获取服务器图片数据需要时间
this.$store.dispatch("getPhotos");
},
};
</script>
- 页面渲染之前需要从服务器获取图片信息(花时间),所以这里调用vuex里的异步操作
九、上传框界面
组件功能:
- 获取上传的图片信息
- 把对应的图片上传到
待上传框内
- 点击
开始上传
,把图片信息存入后端的数据库
UpLoadView.vue
<template>
<!-- 上传相关 -->
<div class="masking" v-show="visible">
<div class="addPhotoContainer"></div>
<div class="addController">
<h3 class="addTitle">
上传照片-普通上传(H5)<span class="close" @click="closeWrapper">╳</span>
</h3>
<div class="photoTitles">
<span class="uploadTo">上传到</span>
<div class="photoSelect">
<img class="showPhoto" src="public/img/1.jpg" />
相册名称
</div>
</div>
<!-- 上传按钮 -->
<div class="showContainer" v-show="upBtn">
<div class="uploadContainer">
<span class="fileinput-button">
<span>上传图片</span>
<!-- 绑定input事件监听为uploadPhoto函数 -->
<input
class="imgFile"
type="file"
name=""
multiple="multiple"
@input="uploadPhoto"
/>
</span>
<span class="hint">
按住Ctrl可多选
</span>
</div>
</div>
<!-- 显示待上传图片 -->
<div class="loadContainer" v-show="upWrapper">
<div class="wantUpload">
<template v-for="(item, index) in toBeUpLoaded">
<!-- 显示待上传图片 -->
<UpLoadPhotoItem :key="index" :item="item" />
</template>
</div>
<div class="addStyle">
<span class="fileinput-add">
<span></span>
<input class="imgFile-add" type="file" multiple="multiple" />
</span>
</div>
<!-- 开始上传按钮 -->
<div class="bottomStyle" @click="beginUpload">
<span class="uploadBtn">开始上传</span>
</div>
</div>
</div>
</div>
</template>
<script>
// 待上传图片的组件
import UpLoadPhotoItem from "./upLoadPhotoItem.vue";
//不是export default导出的模块都要用{}来包括导出。
import { apiUpLoad } from "../api";
export default {
components: {
UpLoadPhotoItem,
},
// visible是父级传入的数据
props: ["visible"],
data() {
return {
// 用于存放待上传的图片信息
toBeUpLoaded: [],
};
},
computed: {
// 如果有需要上传的图片,隐藏上传按钮,把待上传框显示,如果没有要上传的图片,就把上传按钮显示出来,隐藏待上传框
upBtn() {
return this.toBeUpLoaded.length === 0;
},
upWrapper() {
return this.toBeUpLoaded.length > 0;
},
},
methods: {
// 点击上传按钮,触发该函数
async beginUpload() {
// 遍历待上传的图片
// 当图片上传到后端,后端需要把数据存到数据库(花时间)
for (const photos of this.toBeUpLoaded) {
await apiUpLoad(photos);
}
// 写好代码,表达好意图
this.upLoadCompleted();
},
upLoadCompleted() {
// 图片上传完成后,清空待上传数组
this.toBeUpLoaded = [];
// 获取服务端的图片信息,更新vuex里的photos,从而更新DOM
this.$store.dispatch('getPhotos');
},
closeWrapper() {
// 响应父级的visible,把false值传递给父级的visible
this.$emit("update:visible", false);
},
uploadPhoto(e) {
// 获取上传图片的信息
// Array.from 把类数组整理成数组,...解构出来每一个数组对象
// 把上传图片的信息全部放到待上传数组里
this.toBeUpLoaded.push(...Array.from(e.target.files));
},
},
};
</script>
- 不是export default导出的模块都要用{}来包裹导出。
- 向父级传递数据,语法糖
子组件:this.$emit('update:visible', false)
父组件:<up-load-view :visible.sync="showUpLoadView" />
- Array.from可以把类数组整理成数组
十、图片待上传界面
模块功能: 将图片转成base64
UpLoadPhotoItem.vue
<template>
<div class="uploadPhotoItem">
<span class="myProgress">
<span class="plan"></span>
30%
</span>
<!-- 把baseURL赋值给src,直接显示图片 -->
<img :src="imgBase" />
<span class="pictureName">
{{ item.name }}
</span>
</div>
</template>
<script>
export default {
// 获取父级传入的图片信息
props: ['item'],
data() {
return {
imgBase: ''
}
},
created () {
// 把图片转成base64
const fileRender = new FileReader()
fileRender.onload = () => {
// 文件转化完成后执行该逻辑
this.imgBase = fileRender.result
// 显示转化后的结果
}
// 把文件信息转成base64
fileRender.readAsDataURL(this.item)
},
};
</script>
十一、图片详情界面
模块功能: 点击图片显示图片详情
Detail.vue
<template>
<div>
<img :src='photoInfo.imgUrl'>
<p>{{ photoInfo.name }}</p>
<p><button @click='click'>返回</button></p>
</div>
</template>
<script>
// 点击图片的时候,跳转到对应路由
export default {
props: ["id"],
computed: {
// 获取对应的图片信息
photoInfo() {
// 返回对应的图片数据信息
return this.$store.getters.getPhotoById(this.id);
},
},
methods: {
click() {
this.$router.back()
}
},
};
</script>
- 返回上一个路由
this.$router.back()
- 获取响应的数据
this.$store.getters.getPhotoById(this.id)
- Photo.vue组件内的router-link组件,to的路由指向了该组件
十二、引入css文件
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
// 在入口文件引入样式
import './assets/css/login.css'
import './assets/css/photo.css'
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
更多推荐
已为社区贡献1条内容
所有评论(0)