附件类型判断非图片不预览,增加视频预览 (#25)

* 补充了日志九宫格图片上传功能

* 完善日志图片功能

* 增加对文件类型的判断,不是图片则不支持预览

* 增加附加视频预览功能,并对不支持预览的缩略图做不支持处理

* 修复eslint
pull/37/head
guqing 2019-06-26 22:59:23 +08:00 committed by John Niang
parent ee684e2346
commit 4cda3f7df2
6 changed files with 446 additions and 108 deletions

View File

@ -21,12 +21,13 @@
"vue": "^2.6.10",
"vue-clipboard2": "^0.3.0",
"vue-codemirror-lite": "^1.0.4",
"vue-count-to": "^1.0.13",
"vue-cropper": "0.4.9",
"vue-ls": "^3.2.1",
"vue-router": "^3.0.6",
"vue-video-player": "^5.0.2",
"vuejs-logger": "^1.5.3",
"vuex": "^3.1.1",
"vue-count-to": "^1.0.13"
"vuex": "^3.1.1"
},
"devDependencies": {
"@babel/polyfill": "^7.4.4",

View File

@ -0,0 +1,139 @@
<template>
<div class="clearfix">
<a-upload
:name="name"
:customRequest="handleUpload"
listType="picture-card"
:fileList="fileList"
@preview="handlePreview"
@change="handleChange"
>
<div v-if="fileList.length < 9 && plusPhotoVisible" id="plus-photo-uploadbox">
<a-icon type="plus"/>
<div class="ant-upload-text">Upload</div>
</div>
</a-upload>
<a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
<img alt="example" style="width: 100%" :src="previewImage">
</a-modal>
</div>
</template>
<script>
import axios from 'axios'
import attachmentApi from '@/api/attachment'
export default {
props: {
photoList: {
type: Array,
required: false,
default: function() {
return []
}
},
plusPhotoVisible: {
type: Boolean,
required: false,
default: true
}
},
data() {
return {
name: 'file',
previewVisible: false,
previewImage: '',
fileList: [],
uploadHandler: attachmentApi.upload
}
},
created() {
// watch
this.handlerEditPreviewPhoto(this.photoList)
},
watch: {
photoList(newValue, oldValue) {
this.handlerEditPreviewPhoto(newValue)
}
},
methods: {
handlerEditPreviewPhoto(data) {
//
this.fileList = []
//
if (data !== null && data !== undefined) {
for (var i = 0; i < data.length; i++) {
//
this.fileList.push({
uid: data[i].id,
name: data[i].name,
status: 'done',
url: data[i].thumbnail
})
}
}
},
handleCancel() {
this.previewVisible = false
},
handlePreview(file) {
this.previewImage = file.url || file.thumbUrl
this.previewVisible = true
},
handleChange({ fileList }) {
this.fileList = fileList
},
handleUpload(option) {
this.$log.debug('Uploading option', option)
const CancelToken = axios.CancelToken
const source = CancelToken.source()
const data = new FormData()
data.append(this.name, option.file)
this.uploadHandler(
data,
progressEvent => {
if (progressEvent.total > 0) {
progressEvent.percent = (progressEvent.loaded / progressEvent.total) * 100
}
this.$log.debug('Uploading percent: ', progressEvent.percent)
option.onProgress(progressEvent)
},
source.token,
option.file
)
.then(response => {
this.$log.debug('Uploaded successfully', response)
option.onSuccess(response, option.file)
this.$emit('success', response, option.file)
})
.catch(error => {
this.$log.debug('Failed to upload file', error)
option.onError(error, error.response)
this.$emit('failure', error, option.file)
})
return {
abort: () => {
this.$log.debug('Upload operation aborted by the user')
source.cancel('Upload operation canceled by the user.')
}
}
}
}
}
</script>
<style>
/* you can make up upload button and sample style by using stylesheets */
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
.ant-upload-list-picture-card {
/* 将浮动恢复为默认值,避免出现纵向换行情况 */
float: initial;
}
</style>

View File

@ -99,7 +99,8 @@
@click="handleShowDetailDrawer(item)"
>
<div class="attach-thumb">
<img :src="item.thumbPath">
<span v-show="!handleJudgeMediaType(item)"></span>
<img :src="item.thumbPath" v-show="handleJudgeMediaType(item)">
</div>
<a-card-meta>
<ellipsis
@ -242,6 +243,23 @@ export default {
onUploadClose() {
this.loadAttachments()
this.loadMediaTypes()
},
handleJudgeMediaType(attachment) {
var mediaType = attachment.mediaType
//
if (mediaType) {
var prefix = mediaType.split('/')[0]
if (prefix === 'image') {
//
return true
} else {
//
return false
}
}
// false
return false
}
}
}
@ -262,13 +280,20 @@ export default {
position: relative;
padding-bottom: 56%;
overflow: hidden;
img {
img, span{
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
span {
display: flex;
font-size: 12px;
align-items: center;
justify-content: center;
color: #9b9ea0;
}
}
.ant-card-meta {

View File

@ -18,7 +18,15 @@
:paragraph="{rows: 8}"
>
<div class="attach-detail-img">
<img :src="attachment.path">
<div v-show="nonsupportPreviewVisible"></div>
<img :src="attachment.path" v-show="photoPreviewVisible">
<video-player
class="video-player-box"
v-show="videoPreviewVisible"
ref="videoPlayer"
:options="playerOptions"
:playsinline="true">
</video-player>
</div>
</a-skeleton>
</a-col>
@ -74,7 +82,7 @@
<span slot="title">附件大小</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item v-if="photoPreviewVisible">
<a-list-item-meta :description="attachment.height+'x'+attachment.width">
<span slot="title">图片尺寸</span>
</a-list-item-meta>
@ -100,7 +108,7 @@
</span>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item v-if="photoPreviewVisible">
<a-list-item-meta>
<span slot="description">![{{ attachment.name }}]({{ attachment.path }})</span>
<span slot="title">
@ -148,15 +156,40 @@
import { mixin, mixinDevice } from '@/utils/mixin.js'
import attachmentApi from '@/api/attachment'
import photoApi from '@/api/photo'
import 'video.js/dist/video-js.css'
import { videoPlayer } from 'vue-video-player'
export default {
name: 'AttachmentDetailDrawer',
mixins: [mixin, mixinDevice],
components: {
videoPlayer
},
data() {
return {
detailLoading: true,
editable: false,
photo: {}
photo: {},
photoPreviewVisible: false,
videoPreviewVisible: false,
nonsupportPreviewVisible: false,
playerOptions: {
// videojs options
muted: true,
language: 'zh-CN',
aspectRatio: '16:9',
fluid: true,
controls: true,
loop: false,
playbackRates: [0.7, 1.0, 1.5, 2.0],
sources: [{
type: 'video/mp4',
src: 'https://cdn.theguardian.tv/webM/2015/07/20/150716YesMen_synd_768k_vp8.webm'
}],
poster: '/static/images/author.jpg',
width: document.documentElement.clientWidth,
notSupportedMessage: '此视频暂无法播放,请稍后再试'
}
}
},
model: {
@ -182,6 +215,11 @@ export default {
created() {
this.loadSkeleton()
},
computed: {
player() {
return this.$refs.videoPlayer.player
}
},
watch: {
visiable: function(newValue, oldValue) {
this.$log.debug('old value', oldValue)
@ -189,6 +227,12 @@ export default {
if (newValue) {
this.loadSkeleton()
}
},
attachment: function(newValue, oldValue) {
if (newValue) {
var attachment = newValue
this.handleJudgeMediaType(attachment)
}
}
},
methods: {
@ -250,8 +294,48 @@ export default {
},
onClose() {
this.$emit('close', false)
},
handleJudgeMediaType(attachment) {
var mediaType = attachment.mediaType
//
if (mediaType) {
var prefix = mediaType.split('/')[0]
if (prefix === 'video' || prefix === 'flv') {
this.videoPreviewVisible = true
this.photoPreviewVisible = false
this.nonsupportPreviewVisible = false
//
this.$set(this.playerOptions.sources, 0, {
type: mediaType,
src: attachment.path
})
console.log(this.playerOptions.sources)
} else if (prefix === 'image') {
this.photoPreviewVisible = true
this.videoPreviewVisible = false
this.nonsupportPreviewVisible = false
} else {
this.nonsupportPreviewVisible = true
this.videoPreviewVisible = false
this.photoPreviewVisible = false
}
}
}
// handleDownLoadPhoto(attachment) {
// var path = attachment.path
// var index = path.lastIndexOf('/')
// var filename = path.substr(index+1, path.length)
// // chrome/firefox
// var aTag = document.createElement('a')
// aTag.download = filename
// aTag.href = path//URL.createObjectURL(blob)
// aTag.target = '_blank'
// aTag.click()
// URL.revokeObjectURL(aTag.href)
// }
}
}
</script>
@ -259,4 +343,7 @@ export default {
.attach-detail-img img {
width: 100%;
}
.video-player-box {
width: 100%;
}
</style>

View File

@ -200,11 +200,26 @@
placeholder="写点什么吧..."
/>
</a-form-item>
<!-- 日志图片上传 -->
<a-form-item v-show="showMoreOptions">
<UploadPhoto
@success="handlerPhotoUploadSuccess"
:photoList="photoList"
></UploadPhoto>
</a-form-item>
<a-form-item>
<a-button
type="primary"
@click="handleCreateJournalClick"
>保存</a-button>
<a
href="javascript:;"
class="more-options-btn"
type="default"
@click="handleUploadPhotoWallClick"
>更多选项<a-icon type="down" /></a>
</a-form-item>
</a-form>
</a-card>
@ -318,6 +333,7 @@ import logApi from '@/api/log'
import adminApi from '@/api/admin'
import journalApi from '@/api/journal'
import countTo from 'vue-count-to'
import UploadPhoto from '../../components/Upload/UploadPhoto.vue'
export default {
name: 'Dashboard',
mixins: [mixin, mixinDevice],
@ -325,10 +341,13 @@ export default {
PageView,
AnalysisCard,
RecentCommentTab,
countTo
countTo,
UploadPhoto
},
data() {
return {
photoList: [],
showMoreOptions: false,
startVal: 0,
logType: logApi.logType,
activityLoading: true,
@ -339,7 +358,11 @@ export default {
postData: [],
logData: [],
countsData: {},
journal: {},
journal: {
content: '',
photos: []
},
journalPhotos: [], //
logs: [],
options: [],
keys: ['blog_url'],
@ -398,6 +421,18 @@ export default {
next()
},
methods: {
handlerPhotoUploadSuccess(response, file) {
var callData = response.data.data
var photo = {
name: callData.name,
url: callData.path,
thumbnail: callData.thumbPath,
suffix: callData.suffix,
width: callData.width,
height: callData.height
}
this.journalPhotos.push(photo)
},
loadOptions() {
optionApi.listAll(this.keys).then(response => {
this.options = response.data.data
@ -426,11 +461,20 @@ export default {
this.$router.push({ name: 'PostEdit', query: { postId: post.id } })
},
handleCreateJournalClick() {
//
this.journal.photos = this.journalPhotos
journalApi.create(this.journal).then(response => {
this.$message.success('发表成功!')
this.journal = {}
this.photoList = []
this.showMoreOptions = false
})
},
handleUploadPhotoWallClick() {
//
this.showMoreOptions = !this.showMoreOptions
},
handleShowLogDrawer() {
this.logDrawerVisiable = true
this.loadLogs()
@ -458,3 +502,10 @@ export default {
}
}
</script>
<style scoped="scoped">
.more-options-btn{
margin-left: 15px;
text-decoration: none;
}
</style>

View File

@ -6,18 +6,12 @@
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="48">
<a-col
:md="6"
:sm="24"
>
<a-col :md="6" :sm="24">
<a-form-item label="关键词">
<a-input v-model="queryParam.keyword" />
<a-input v-model="queryParam.keyword"/>
</a-form-item>
</a-col>
<a-col
:md="6"
:sm="24"
>
<a-col :md="6" :sm="24">
<a-form-item label="状态">
<a-select placeholder="请选择状态">
<a-select-option value="1">公开</a-select-option>
@ -25,32 +19,19 @@
</a-select>
</a-form-item>
</a-col>
<a-col
:md="6"
:sm="24"
>
<a-col :md="6" :sm="24">
<span class="table-page-search-submitButtons">
<a-button
type="primary"
@click="loadJournals(true)"
>查询</a-button>
<a-button
style="margin-left: 8px;"
@click="resetParam"
>重置</a-button>
<a-button type="primary" @click="loadJournals(true)"></a-button>
<a-button style="margin-left: 8px;" @click="resetParam"></a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<div class="table-operator">
<a-button
type="primary"
icon="plus"
@click="handleNew"
>写日志</a-button>
<a-button type="primary" icon="plus" @click="handleNew"></a-button>
</div>
<a-divider />
<a-divider/>
<div style="margin-top:15px">
<a-list
itemLayout="vertical"
@ -58,42 +39,46 @@
:dataSource="journals"
:loading="listLoading"
>
<a-list-item
slot="renderItem"
slot-scope="item, index"
:key="index"
<a-list-item slot="renderItem" slot-scope="item, index" :key="index">
<!-- 日志图片集合 -->
<a-card
hoverable
v-for="(photo, photoIndex) in item.photos"
:key="photoIndex"
class="photo-card"
@click="handlerPhotoPreview(photo)"
>
<img alt="example" :src="photo.thumbnail" slot="cover">
</a-card>
<a-modal :visible="previewVisible" :footer="null" @cancel="handleCancelPreview">
<img
:alt="previewPhoto.name + previewPhoto.description"
style="width: 100%"
:src="previewPhoto.url"
>
</a-modal>
<template slot="actions">
<span>
<a href="javascript:void(0);">
<a-icon
type="like-o"
style="margin-right: 8px"
/>{{ item.likes }}
<a-icon type="like-o" style="margin-right: 8px"/>
{{ item.likes }}
</a>
</span>
<span>
<a
href="javascript:void(0);"
@click="handleCommentShow(item)"
>
<a-icon
type="message"
style="margin-right: 8px"
/>{{ item.commentCount }}
<a href="javascript:void(0);" @click="handleCommentShow(item)">
<a-icon type="message" style="margin-right: 8px"/>
{{ item.commentCount }}
</a>
</span>
<!-- <span>
From 微信
</span> -->
</span>-->
</template>
<template slot="extra">
<a
href="javascript:void(0);"
@click="handleEdit(item)"
>编辑</a>
<a-divider type="vertical" />
<a href="javascript:void(0);" @click="handleEdit(item)"></a>
<a-divider type="vertical"/>
<a-popconfirm
title="你确定要删除这条日志?"
@confirm="handleDelete(item.id)"
@ -103,13 +88,10 @@
<a href="javascript:void(0);">删除</a>
</a-popconfirm>
</template>
<a-list-item-meta :description="item.content">
<span slot="title">{{ item.createTime | moment }}</span>
<a-avatar
slot="avatar"
size="large"
:src="user.avatar"
/>
<a-avatar slot="avatar" size="large" :src="user.avatar"/>
</a-list-item-meta>
</a-list-item>
<div class="page-wrapper">
@ -132,29 +114,35 @@
<!-- 编辑日志弹窗 -->
<a-modal v-model="visible">
<template slot="title">
{{ title }} <a-tooltip
slot="action"
title="只能输入250字"
>
<a-icon type="info-circle-o" />
{{ title }}
<a-tooltip slot="action" title="只能输入250字">
<a-icon type="info-circle-o"/>
</a-tooltip>
</template>
<template slot="footer">
<a-button
key="submit"
type="primary"
@click="createOrUpdateJournal"
>
发布
</a-button>
<a-button key="submit" type="primary" @click="createOrUpdateJournal"></a-button>
</template>
<a-form layout="vertical">
<a-form-item>
<a-input
type="textarea"
:autosize="{ minRows: 8 }"
v-model="journal.content"
/>
<a-input type="textarea" :autosize="{ minRows: 8 }" v-model="journal.content"/>
</a-form-item>
<a-form-item v-show="showMoreOptions">
<UploadPhoto
@success="handlerPhotoUploadSuccess"
:photoList="photoList"
:plusPhotoVisible="plusPhotoVisible"
></UploadPhoto>
</a-form-item>
<a-form-item>
<a
href="javascript:;"
class="more-options-btn"
type="default"
@click="handleUploadPhotoWallClick"
>
更多选项
<a-icon type="down"/>
</a>
</a-form-item>
</a-form>
</a-modal>
@ -166,21 +154,11 @@
v-model="selectCommentVisible"
>
<template slot="footer">
<a-button
key="submit"
type="primary"
@click="handleReplyComment"
>
回复
</a-button>
<a-button key="submit" type="primary" @click="handleReplyComment"></a-button>
</template>
<a-form layout="vertical">
<a-form-item>
<a-input
type="textarea"
:autosize="{ minRows: 8 }"
v-model="replyComment.content"
/>
<a-input type="textarea" :autosize="{ minRows: 8 }" v-model="replyComment.content"/>
</a-form-item>
</a-form>
</a-modal>
@ -194,23 +172,16 @@
destroyOnClose
@close="()=>this.commentVisiable = false"
>
<a-row
type="flex"
align="middle"
>
<a-row type="flex" align="middle">
<a-col :span="24">
<a-comment>
<a-avatar
:src="user.avatar"
:alt="user.nickname"
slot="avatar"
/>
<a-avatar :src="user.avatar" :alt="user.nickname" slot="avatar"/>
<p slot="content">{{ journal.content }}</p>
<span slot="datetime">{{ journal.createTime | moment }}</span>
</a-comment>
</a-col>
<a-divider />
<a-divider/>
<a-col :span="24">
<journal-comment-tree
v-for="(comment,index) in comments"
@ -231,12 +202,22 @@ import { mixin, mixinDevice } from '@/utils/mixin.js'
import journalApi from '@/api/journal'
import journalCommentApi from '@/api/journalComment'
import userApi from '@/api/user'
import UploadPhoto from '@/components/Upload/UploadPhoto.vue'
export default {
mixins: [mixin, mixinDevice],
components: { JournalCommentTree },
components: { JournalCommentTree, UploadPhoto },
data() {
return {
plusPhotoVisible: true,
photoList: [], //
previewVisible: false,
showMoreOptions: false,
previewPhoto: {
//
name: '',
description: '',
url: ''
},
title: '发表',
listLoading: false,
visible: false,
@ -255,7 +236,12 @@ export default {
},
journals: [],
comments: [],
journal: {},
journal: {
id: undefined,
content: '',
photos: []
},
journalPhotos: [], //
selectComment: null,
replyComment: {},
user: {}
@ -266,6 +252,30 @@ export default {
this.loadUser()
},
methods: {
handleCancelPreview() {
this.previewVisible = false
},
handlerPhotoPreview(photo) {
//
this.previewVisible = true
this.previewPhoto = photo
},
handlerPhotoUploadSuccess(response, file) {
var callData = response.data.data
var photo = {
name: callData.name,
url: callData.path,
thumbnail: callData.thumbPath,
suffix: callData.suffix,
width: callData.width,
height: callData.height
}
this.journalPhotos.push(photo)
},
handleUploadPhotoWallClick() {
//
this.showMoreOptions = !this.showMoreOptions
},
loadJournals(isSearch) {
this.queryParam.page = this.pagination.page - 1
this.queryParam.size = this.pagination.size
@ -289,11 +299,19 @@ export default {
this.title = '新建'
this.visible = true
this.journal = {}
//
this.plusPhotoVisible = true
this.photoList = []
},
handleEdit(item) {
this.title = '编辑'
this.journal = item
this.visible = true
// ,
this.plusPhotoVisible = false
this.photoList = item.photos
},
handleDelete(id) {
journalApi.delete(id).then(response => {
@ -330,6 +348,9 @@ export default {
})
},
createOrUpdateJournal() {
//
this.journal.photos = this.journalPhotos
if (this.journal.id) {
journalApi.update(this.journal.id, this.journal).then(response => {
this.$message.success('更新成功!')
@ -339,6 +360,7 @@ export default {
journalApi.create(this.journal).then(response => {
this.$message.success('发表成功!')
this.loadJournals()
this.photoList = []
})
}
this.visible = false
@ -356,3 +378,16 @@ export default {
}
}
</script>
<style scoped="scoped">
.more-options-btn {
margin-left: 15px;
text-decoration: none;
}
/* 日志图片卡片样式 */
.photo-card {
width: 104px;
display: inline-block;
margin-right: 5px;
}
</style>