Add image preview plugin.

This commit is contained in:
zicla
2018-11-26 15:49:56 +08:00
parent 3cccfd71b0
commit a17ad46355
12 changed files with 360 additions and 36 deletions
+3 -1
View File
@@ -17,6 +17,7 @@
"animate.css": "^3.5.2",
"babel-polyfill": "^6.26.0",
"bootstrap": "^3.3.7",
"clipboard": "^2.0.4",
"editor.md": "^1.5.0",
"element-ui": "^2.0.2",
"enquire.js": "^2.1.6",
@@ -33,7 +34,8 @@
"vue-resource": "^1.3.4",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"vuex-router-sync": "^5.0.0"
"vuex-router-sync": "^5.0.0",
"x-photoswipe": "^4.1.3-rc.1"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
+2 -2
View File
@@ -151,10 +151,10 @@
that.$emit('createDirectorySuccess', that.imageCache)
}, function (response) {
}, function (errorMessage) {
that.director.createMode = false
that.editMode = false
Message.error(response.data.msg)
Message.error(errorMessage)
})
}
},
+27 -22
View File
@@ -61,17 +61,16 @@
<div class="middle-part">
<span class="matter-name-edit" v-if="matter.editMode">
<input ref="editInput" class="form-control"
:class="matter.uuid"
v-model="matter.name"
v-model="renameMatterName"
placeholder="请输入名称"
@blur="blurTrigger()"
v-on:keyup.13="enterTrigger()"/>
</span>
<span class="matter-name" :class="{'alien':matter.alien}" v-else>
{{matter.name}} <i class="fa fa-unlock" v-if="!matter.dir && !matter.privacy" title="公有文件,任何人可以访问"></i>
</span>
{{matter.name}} <i class="fa fa-unlock" v-if="!matter.dir && !matter.privacy" title="公有文件,任何人可以访问"></i>
</span>
</div>
</div>
@@ -139,14 +138,17 @@
import $ from 'jquery'
import Director from './Director'
import {Message, MessageBox, Notification} from 'element-ui'
import {setInputSelection} from '../../../common/util/Utils'
import {currentHost, setInputSelection} from '../../../common/util/Utils'
import PhotoSwipeHelper from "../../../common/plugin/photoswipe/PhotoSwipeHelper";
export default {
data() {
return {
//正在向服务器提交rename的请求
renamingLoading: false,
showMore: false
showMore: false,
//正在重命名的临时字段
renameMatterName: null
}
},
components: {
@@ -181,7 +183,8 @@
if (this.matter.dir) {
this.$emit('goToDirectory', that.matter.uuid)
} else {
this.download()
PhotoSwipeHelper.showPhoto(that.matter.getDownloadUrl())
//this.download()
}
},
@@ -220,12 +223,14 @@
//告诉导演,自己正在编辑
this.director.renameMode = true
this.matter.editMode = true
this.renameMatterName = this.matter.name
//稍作延迟,vue的组件才能加载出来
setTimeout(function () {
let dotIndex = that.matter.name.lastIndexOf('.')
if (dotIndex === -1) {
setInputSelection(that.$refs.editInput, 0, that.matter.name.length)
setInputSelection(that.$refs.editInput, 0, that.renameMatterName)
} else {
setInputSelection(that.$refs.editInput, 0, dotIndex)
}
@@ -239,17 +244,18 @@
if (that.renamingLoading) {
return
}
that.renamingLoading = true
this.matter.httpRename(function () {
this.matter.httpRename(that.renameMatterName, function () {
that.renamingLoading = false
Message.info('重命名成功!')
Message.success('重命名成功!')
//告诉导演,自己编辑完毕
that.director.renameMode = false
that.matter.editMode = false
}, function (response) {
}, function (errorMessage) {
that.renamingLoading = false
Message.error(response.data.msg)
Message.error(errorMessage)
//告诉导演,自己编辑完毕
that.director.renameMode = false
that.matter.editMode = false
@@ -267,10 +273,10 @@
that.$emit('createDirectorySuccess', that.matter)
}, function (response) {
}, function (errorMessage) {
that.director.createMode = false
that.editMode = false
Message.error(response.data.msg)
Message.error(errorMessage)
})
},
blurTrigger() {
@@ -291,14 +297,13 @@
},
clipboard() {
let $temp = $("<input>");
$("body").append($temp);
$temp.val(location.protocol + "//" + location.host + this.matter.getDownloadUrl()).select();
document.execCommand("copy");
$temp.remove();
Message.success('已复制!');
let textToCopy = currentHost() + this.matter.getDownloadUrl();
this.$copyPlguin.copy(textToCopy, function () {
Message.success({
message: "复制成功!",
center: true
})
})
}
},
+16 -7
View File
@@ -7,6 +7,7 @@ import User from '../user/User'
import UserInputSelection from '../../../backyard/user/widget/UserInputSelection'
import Vue from "vue"
import {FilterType} from "../base/FilterType";
import {handleImageUrl} from "../../util/ImageUtil";
export default class Matter extends BaseEntity {
@@ -33,7 +34,6 @@ export default class Matter extends BaseEntity {
this.privacy = true
this.path = null
/*
这部分是辅助UI的字段信息
*/
@@ -54,6 +54,7 @@ export default class Matter extends BaseEntity {
//实时上传速度 byte/s
this.speed = 0
}
getFilters() {
@@ -96,7 +97,10 @@ export default class Matter extends BaseEntity {
} else if (startWith(mimeType, 'text')) {
return "/static/img/file/text.svg"
} else if (startWith(mimeType, 'image')) {
return "/static/img/file/image.svg"
//对于图片,使用其缩略图
return handleImageUrl(this.getDownloadUrl(), false, 100, 100)
} else if (endWith(this.name, 'zip') || endWith(this.name, 'rar') || endWith(this.name, '7z') || endWith(this.name, 'tar') || endWith(this.name, 'tar') || endWith(this.name, 'gz')) {
return "/static/img/file/archive.svg"
} else {
@@ -128,20 +132,25 @@ export default class Matter extends BaseEntity {
}, errorCallback)
}
httpRename(successCallback, errorCallback) {
httpRename(name, successCallback, errorCallback) {
let that = this
this.httpPost(Matter.URL_MATTER_RENAME, {'uuid': this.uuid, 'name': this.name}, function (response) {
this.httpPost(Matter.URL_MATTER_RENAME, {'uuid': this.uuid, 'name': name}, function (response) {
that.render(response.data.data)
typeof successCallback === 'function' && successCallback(response)
}, errorCallback)
}
httpChangePrivacy(privacy, successCallback, errorCallback) {
let that = this
this.httpPost(Matter.URL_CHANGE_PRIVACY, {'uuid': this.uuid, 'privacy': privacy}, function (response) {
that.privacy = privacy
Message.success(response.data.msg)
typeof successCallback === 'function' && successCallback(response)
if (typeof successCallback === "function") {
successCallback(response)
} else {
Message.success(response.data.msg)
}
}, errorCallback)
}
@@ -363,7 +372,7 @@ export default class Matter extends BaseEntity {
}
getDownloadUrl() {
return Vue.http.options.root + '/alien/download/' + this.uuid + '/' + this.name
return '/api/alien/download/' + this.uuid + '/' + this.name
}
}
@@ -0,0 +1,61 @@
<template>
<button ref="clipboardBtn"
class="copy-plugin-clipboard-btn"
data-clipboard-text="textToCopy"
style="position: absolute;opacity:0;">
{{textToCopy}}
</button>
</template>
<script>
import Clipboard from 'clipboard';
import {safeCallback} from "../../util/Utils";
let nop = () => {
};
//这个组件主要用于提供dom元素。
export default {
data() {
return {
textToCopy: "",
clipboardBtn: null,
successCallback: null,
errorCallback: null
}
},
computed: {},
props: {},
watch: {},
methods: {
copy(text, successCallback, errorCallback) {
let that = this;
this.successCallback = successCallback
this.errorCallback = errorCallback
this.textToCopy = text;
//这里不用延迟设置,否则backyard会出错
that.$refs.clipboardBtn.setAttribute("data-clipboard-text", text)
that.$refs.clipboardBtn.click()
}
},
mounted() {
let that = this;
this.clipboardBtn = new Clipboard(this.$refs.clipboardBtn);
this.clipboardBtn.on('success', function (e) {
console.info('clipboard success');
safeCallback(that.successCallback)()
});
this.clipboardBtn.on('error', function (e) {
console.error('clipboard error:', e);
safeCallback(that.errorCallback)()
});
}
}
</script>
+32
View File
@@ -0,0 +1,32 @@
import CopyPlguinLayout from "./CopyPlguinLayout"
export default class CopyPlugin {
constructor() {
this.$vm = null;
}
install(Vue, options) {
const CopyPlguinComponent = Vue.extend(CopyPlguinLayout)
if (!this.$vm) {
this.$vm = new CopyPlguinComponent({
el: document.createElement('div'),
propsData: {}
})
document.body.appendChild(this.$vm.$el)
}
Vue.$copyPlguin = this;
Vue.mixin({
created: function () {
this.$copyPlguin = Vue.$copyPlguin
}
})
}
copy(text, successCallback) {
this.$vm.copy(text, successCallback);
}
}
@@ -0,0 +1,151 @@
import PhotoSwipeLayout from "./PhotoSwipeLayout";
import Vue from 'vue'
import PhotoSwipe from 'x-photoswipe/dist/photoswipe'
import PhotoSwipeUIDefault from 'x-photoswipe/dist/photoswipe-ui-default'
//PhotoSwipe的样式
import "x-photoswipe/dist/photoswipe.css";
import "x-photoswipe/dist/default-skin/default-skin.css";
/**
*
* 图片预览控件,主要提供给H5端使用。
*/
export default class PhotoSwipeHelper {
constructor() {
//核心
this.photoSwipe = null;
//小本本记住,动态创建Vue组件。
let ComponentClass = Vue.extend(PhotoSwipeLayout)
this.vueInstance = new ComponentClass()
this.init()
}
//初始化工作一口气做完
init() {
//挂载组件,非常重要。否则$el就不会出现。
this.vueInstance.$mount()
document.body.appendChild(this.vueInstance.$el)
}
//展示一张图片
showSinglePhoto(url, width = 0, height = 0) {
let that = this;
let items = [{
src: url,
w: width,
h: height
}];
let options = {
//不需要历史纪录
history: false,
//不需要全屏按钮
fullscreenEl: false,
//不需要分享按钮
shareEl: false,
//点击不要让控制按钮消失
tapToToggleControls: false,
//当前从第0张展示。
index: 0
};
this.photoSwipe = new PhotoSwipe(this.vueInstance.$el, PhotoSwipeUIDefault, items, options);
this.photoSwipe.listen('gettingData', function (index, item) {
if (!item.w || !item.h || item.w < 1 || item.h < 1) {
const img = new Image()
img.onload = function () {
item.w = this.width
item.h = this.height
that.photoSwipe.updateSize(true)
}
img.src = item.src
}
})
this.photoSwipe.init();
this.photoSwipe.listen('close', () => {
console.log("photoSwipe事件:close")
})
this.photoSwipe.listen('afterChange', (a, b) => {
console.log("photoSwipe事件:afterChange")
})
}
//展示一系列图片
showMultiPhoto(urls = [], index = 0) {
let that = this;
let items = [];
urls.forEach((url) => {
items.push({
src: url,
w: 0,
h: 0
})
})
let options = {
//不需要历史纪录
history: false,
//不需要全屏按钮
fullscreenEl: false,
//不需要分享按钮
shareEl: false,
//点击不要让控制按钮消失
tapToToggleControls: false,
//当前从第0张展示。
index: index
};
this.photoSwipe = new PhotoSwipe(this.vueInstance.$el, PhotoSwipeUIDefault, items, options);
this.photoSwipe.listen('gettingData', function (index, item) {
if (!item.w || !item.h || item.w < 1 || item.h < 1) {
const img = new Image()
img.onload = function () {
item.w = this.width
item.h = this.height
that.photoSwipe.updateSize(true)
}
img.src = item.src
}
})
this.photoSwipe.init();
this.photoSwipe.listen('close', () => {
//photoSwipe事件:close
})
this.photoSwipe.listen('afterChange', (a, b) => {
//photoSwipe事件:afterChange
})
}
//单例,为了只有一个实例
static singleton = null;
//使用懒加载
static getSingleton() {
if (!PhotoSwipeHelper.singleton) {
PhotoSwipeHelper.singleton = new PhotoSwipeHelper();
}
return PhotoSwipeHelper.singleton;
}
//展示一张图片。
static showPhoto(url) {
PhotoSwipeHelper.getSingleton().showSinglePhoto(url);
}
//展示一系列图片
static showPhotos(urls, index = 0) {
PhotoSwipeHelper.getSingleton().showMultiPhoto(urls, index);
}
}
@@ -0,0 +1,62 @@
<template>
<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div ref="root1" class="pswp vux-previewer" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
<!-- Container that holds slides.
PhotoSwipe keeps only 3 of them in the DOM to save memory.
Don't modify these 3 pswp__item elements, data is added later on. -->
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title="Share"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)"></button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)"></button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
</template>
<script>
//这个组件主要用于提供dom元素。
export default {
data() {
return {}
},
computed: {},
props: {},
watch: {},
methods: {}
}
</script>
+1 -1
View File
@@ -27,7 +27,7 @@ export function handleImageUrl(url = null, origin = false, width = 200, height =
if (origin) {
return url;
} else {
return url + "?imageProcess=resize&imageResizeM=fill&imageResizeW=" + width + "&imageResizeH=" + height
return url + "?ir=fill_" + width + "_" + height
}
} else {
if (NumberUtil.fractionEqual(1, 1, width, height)) {
-2
View File
@@ -15,8 +15,6 @@ const state = {
},
debug: true,
host: '/api',
//当前版本信息。
version: {
identifier: 'cn.eyeblue.tank',
+5 -1
View File
@@ -4,6 +4,9 @@ import "babel-polyfill";
//custom directive.
import "./common/directive/directive"
//自定义文本框插件
import CopyPlugin from "./common/plugin/copy/CopyPlugin";
import Vue from 'vue'
import App from './App.vue'
import store from './common/vuex'
@@ -25,9 +28,10 @@ Vue.store = store;
Vue.router = router;
sync(store, router)
Vue.use(VueResource)
Vue.http.options.root = store.state.host;
Vue.use(NProgress)
//使用自定义插件
Vue.use(new CopyPlugin())
// register global utility filters.
Object.keys(filters).forEach(key => {
View File