uni-halo/pagesB/attachment/attachment.vue
2022-12-06 15:08:29 +08:00

768 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="app-page flex flex-col">
<!-- 搜索区域 -->
<view class="filter-wrap bg-white shadow-3">
<tm-search v-model="queryParams.keyword" :round="24" :shadow="0" color="light-blue" insert-color="light-blue" :clear="true" @confirm="fnOnSearch"></tm-search>
<tm-dropDownMenu :shadow="0" color="light-blue" @confirm="fnOnConfirm" :list="filterList"></tm-dropDownMenu>
</view>
<!-- 占位区域 -->
<view style="width: 100vw;height: 184rpx;"></view>
<view v-if="loading != 'success'" class="loading-wrap">
<tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton>
</view>
<view class="page-content pa-24 flex flex-col" v-else>
<view v-if="dataList.length == 0" class="content-empty flex flex-center">
<!-- 空布局 -->
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无任何附件"></tm-empty>
</view>
<block v-else>
<!-- 内容区域 -->
<block v-for="(item, index) in dataList" :key="index">
<tm-translate animation-name="fadeUp" :wait="(index + 1) * 50">
<view class="attachment-card mb-24 flex bg-white pa-24 round-3" @click="fnShowDetail(item)">
<view class="cover round-3 flex flex-center" :class="[fnGetIconClass(item.mediaType)]">
<text class="icon iconfont">{{ item.suffix }}</text>
</view>
<!-- 内容区域 -->
<view class="info pl-36">
<view class="file-name text-size-m text-weight-b">{{ item.name }}</view>
<view class="file-media mt-12 text-size-s text-grey-darken-1">附件类型{{ item.mediaType }}</view>
<view class="file-media mt-8 text-size-s text-grey-darken-1">附件大小{{ item.sizeText }}</view>
<view class="file-path mt-8 text-size-s text-grey-darken-1">上传日期{{ { d: item.createTime, f: 'yyyy年MM月dd日 星期w' } | formatTime }}</view>
</view>
</view>
</tm-translate>
</block>
<tm-flotbutton @click="fnToTopPage" :offset="[16, 80]" color="bg-gradient-light-blue-accent" size="m" icon="icon-angle-up"></tm-flotbutton>
<view class="load-text ">{{ loadMoreText }}</view>
</block>
<!-- 弹窗详情 -->
<tm-poup v-model="detail.show" position="bottom" height="80vh">
<view class="poup-content">
<view class="poup-head text-align-center text-weight-b text-size-g ma-24 ">附件详情</view>
<scroll-view class="poup-body pa-24 pt-0" :scroll-y="true" @touchmove.stop>
<view class="preview-file pl-24 pr-24">
<!-- 图片预览 -->
<tm-images
v-if="$utils.fnCheckIsFileType('image', detail.form)"
:width="700"
:height="350"
:round="3"
:src="$utils.checkThumbnailUrl(detail.form.path)"
model="aspectFill"
></tm-images>
<!-- 视频 -->
<video
v-else-if="$utils.fnCheckIsFileType('video', detail.form)"
class="round-3"
style="width: 100%;height: 350rpx;"
:src="$utils.checkUrl(detail.form.path)"
controls
></video>
<view v-else class="flex flex-center border-a-1 round-3 text-align-center text-grey-darken-1" style="height: 280rpx;background-color: #f2f2f2;">
该文件暂不支持预览
</view>
</view>
<tm-input title="附件名称" v-model="detail.form.name" :clear="true" placeholder="请输入附件名称..."></tm-input>
<tm-input :disabled="true" color="grey" title="附件类型" v-model="detail.form.mediaType"></tm-input>
<tm-input :disabled="true" color="grey" title="附件位置" v-model="detail.form.type"></tm-input>
<tm-input :disabled="true" color="grey" title="附件大小" v-model="detail.form.sizeText"></tm-input>
<tm-input :disabled="true" color="grey" title="上传日期" v-model="detail.form.createTime"></tm-input>
<tm-input
:disabled="true"
:vertical="true"
:borderBottom="false"
input-type="textarea"
title="文件地址"
color="grey"
bg-color="grey-lighten-5"
:height="120"
v-model="detail.form.path"
>
<template v-slot:default="{ title }">
{{ title }}
<tm-button text theme="light-blue" size="xs" @click="$utils.copyText(detail.form.path, '复制成功!')">复制</tm-button>
</template>
</tm-input>
</scroll-view>
<view class="btn-wrap flex flex-center">
<tm-button size="m" navtie-type="form" theme="bg-gradient-blue-accent" @click="fnOnUpdate()">保存</tm-button>
<tm-button size="m" theme="bg-gradient-red-accent" @click="fnOnDelete()">删除</tm-button>
<tm-button size="m" theme="bg-gradient-blue-grey-accent" @click="detail.show = false">关闭</tm-button>
</view>
</view>
</tm-poup>
</view>
<tm-flotbutton :show-text="true" color="bg-gradient-orange-accent" @click="fnOnFlotButton"></tm-flotbutton>
<tm-actionSheetMenu title="文件上传操作" @change="fnOnFileSelectActionChange" v-model="fileSelectAction.show" :actions="fileSelectAction.list"></tm-actionSheetMenu>
</view>
</template>
<script>
// #ifdef APP-PLUS
const AfDocument = uni.requireNativePlugin('Aq-ChooseFile'); // 临时替代
// #endif
import { getAdminAccessToken } from '@/utils/auth.js';
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
import tmSearch from '@/tm-vuetify/components/tm-search/tm-search.vue';
import tmDropDownMenu from '@/tm-vuetify/components/tm-dropDownMenu/tm-dropDownMenu.vue';
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
import tmCheckbox from '@/tm-vuetify/components/tm-checkbox/tm-checkbox.vue';
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
import tmForm from '@/tm-vuetify/components/tm-form/tm-form.vue';
import tmUpload from '@/tm-vuetify/components/tm-upload/tm-upload.vue';
import tmInput from '@/tm-vuetify/components/tm-input/tm-input.vue';
import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue';
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
import tmActionSheetMenu from '@/tm-vuetify/components/tm-actionSheetMenu/tm-actionSheetMenu.vue';
export default {
components: {
tmSkeleton,
tmSearch,
tmDropDownMenu,
tmEmpty,
tmButton,
tmCheckbox,
tmPoup,
tmForm,
tmUpload,
tmInput,
tmImages,
tmFlotbutton,
tmTranslate,
tmActionSheetMenu
},
data() {
return {
loading: 'loading',
filterLoading: 'loading',
btnOption: {
actions: [
{
icon: 'icon-like',
color: 'bg-gradient-orange-accent'
},
{
icon: 'icon-commentdots-fill',
color: 'bg-gradient-green-accent'
},
{
icon: 'icon-share1',
color: 'bg-gradient-blue-accent'
}
]
},
filterList: [
{
title: '存储位置',
children: [
{
title: '',
name: 'attachmentType',
model: 'radio',
children: []
}
]
},
{
title: '文件类型',
children: [
{
title: '',
name: 'mediaType',
model: 'radio',
children: []
}
]
}
],
queryParams: {
size: 8,
page: 0,
attachmentType: undefined,
keyword: undefined,
mediaType: undefined,
sort: undefined
},
result: {},
dataList: [],
total: 0,
detail: {
params: {},
form: {},
show: false
},
isLoadMore: false,
loadMoreText: '加载中...',
fileSelectAction: {
show: false,
list: []
}
};
},
onLoad() {
this.fnSetPageTitle('附件管理');
},
created() {
this.fnGetData();
this.fnGetFilters();
},
onPullDownRefresh() {
this.queryParams.page = 0;
this.dataList = [];
this.fnGetData();
if (this.filterLoading != 'success') {
this.fnGetFilters();
}
},
onReachBottom(e) {
if (this.result.hasNext) {
this.queryParams.page += 1;
this.isLoadMore = true;
this.fnGetData();
} else {
uni.showToast({
icon: 'none',
title: '没有更多数据了'
});
}
},
methods: {
/**
* 文件大小 字节转换单位
* @param size
* @returns {string|*}
*/
fnTransFileSize(size) {
if (!size) return '';
if (size < this.fnPow1024(1)) return size + ' B';
if (size < this.fnPow1024(2)) return (size / this.fnPow1024(1)).toFixed(2) + ' KB';
if (size < this.fnPow1024(3)) return (size / this.fnPow1024(2)).toFixed(2) + ' MB';
if (size < this.fnPow1024(4)) return (size / this.fnPow1024(3)).toFixed(2) + ' GB';
return (size / this.fnPow1024(4)).toFixed(2) + ' TB';
},
// 求次幂
fnPow1024(num) {
return Math.pow(1024, num);
},
fnGetIconClass(mediaType) {
let _class = '';
if (mediaType.indexOf('video/') != -1) {
_class = 'is-video';
} else if (mediaType.indexOf('image/') != -1) {
_class = 'is-image';
}
return _class;
},
fnCheckIsFileType(type, attachment) {
if (type == 'video') return attachment.mediaType.indexOf('video/') != -1;
else if (type == 'image') return attachment.mediaType.indexOf('image/') != -1;
else return false;
},
fnGetFilters() {
this.filterLoading = 'loading';
Promise.all([this.$httpApi.admin.getAttachmentsTypes(), this.$httpApi.admin.getAttachmentsMediaTypes()])
.then(res => {
const res1 = res[0];
this.filterList[0].children[0].children = res1.data.map(item => {
return {
title: item,
id: item
};
});
const res2 = res[1];
this.filterList[1].children[0].children = res2.data.map(item => {
return {
title: item,
id: item
};
});
this.filterLoading = 'success';
})
.catch(err => {
this.filterLoading = 'error';
uni.$tm.toast('搜索条件数据加载失败!');
});
},
// 搜索区域确定
fnOnConfirm(e) {
this.queryParams.attachmentType = e[0].children.map(x => x.id).join('') || undefined;
this.queryParams.mediaType = e[1].children.map(x => x.id).join('') || undefined;
this.fnOnSearch();
},
fnOnSearch() {
this.queryParams.page = 0;
this.dataList = [];
this.fnGetData();
},
fnGetData() {
uni.showLoading({
mask: true,
title: '加载中...'
});
// 设置状态为加载中
if (!this.isLoadMore) {
this.loading = 'loading';
}
this.loadMoreText = '加载中...';
this.$httpApi.admin
.getAttachmentsByPage(this.queryParams)
.then(res => {
if (res.status == 200) {
this.loadMoreText = res.data.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
this.result = res.data;
this.total = res.data.total;
if (this.isLoadMore) {
let _list = this.dataList.concat(res.data.content);
this.dataList = _list.map(item => {
item['sizeText'] = this.fnTransFileSize(item.size);
return item;
});
} else {
this.dataList = res.data.content.map(item => {
item['sizeText'] = this.fnTransFileSize(item.size);
return item;
});
}
this.loading = 'success';
} else {
uni.$tm.toast('加载失败,请重试!');
thi.loading = 'error';
}
})
.catch(err => {
console.error(err);
uni.$tm.toast('加载失败,请重试!');
this.loading = 'error';
this.loadMoreText = '加载失败,请下拉刷新!';
})
.finally(() => {
setTimeout(() => {
uni.hideLoading();
uni.stopPullDownRefresh();
}, 300);
});
},
// 详情
fnShowDetail(item) {
this.detail.params = { ...item };
this.detail.form = { ...item };
this.detail.show = true;
},
// 删除
fnOnDelete() {
uni.$eShowModal({
title: '提示',
content: `您确定要删除该附件吗?`,
showCancel: true,
cancelText: '否',
cancelColor: '#999999',
confirmText: '是',
confirmColor: '#03a9f4'
})
.then(res => {
uni.showLoading({
mask: true,
title: '正在删除...'
});
this.$httpApi.admin
.deleteAttachmentById(this.detail.params.id)
.then(res => {
if (res.status == 200) {
uni.$tm.toast('删除成功!');
setTimeout(() => {
this.detail.show = false;
this.detail.params = {};
this.detail.form = {};
this.fnGetData();
}, 1000);
} else {
uni.$tm.toast('删除失败,请重试!');
}
})
.catch(err => {
console.error(err);
uni.$tm.toast('删除失败,请重试!');
});
})
.catch(err => {});
},
fnOnUpdate() {
if (this.detail.form.name == '') {
return uni.$tm.toast('附件名称不能为空!');
}
uni.showLoading({
mask: true,
title: '正在保存...'
});
this.$httpApi.admin
.updateAttachmentById(this.detail.params.id, this.detail.form.name)
.then(res => {
if (res.status == 200) {
uni.$tm.toast('保存成功!');
// setTimeout(() => {
// this.detail.show = false;
// this.detail.params = {};
// this.detail.form = {};
// this.fnGetData();
// }, 1000);
} else {
uni.$tm.toast('保存失败,请重试!');
}
})
.catch(err => {
uni.$tm.toast('保存失败,请重试!');
});
},
// 浮动按钮点击
fnOnFlotButton() {
// 微信小程序:提供从消息列表和媒体选择
// #ifdef MP-WEIXIN
this.fileSelectAction.list = ['上传照片', '上传视频', '上传文件'];
// #endif
// 安卓app使用第三方上传
// #ifdef APP-PLUS
this.fileSelectAction.list = ['上传照片', '上传视频', '上传文件'];
// #endif
// h5
// #ifdef H5
this.fileSelectAction.list = ['上传照片', '上传视频', '上传文件'];
// #endif
// #ifndef H5||MP-WEIXIN||APP-PLUS
this.fileSelectAction.list = ['上传照片', '上传视频'];
// #endif
this.fileSelectAction.show = true;
},
fnOnFileSelectActionChange(e) {
// 微信小程序:提供从消息列表和媒体选择
// #ifdef MP-WEIXIN
switch (e.index) {
case 0:
// 上传照片
uni.chooseImage({
count: 1,
success: res => {
console.log(res);
this.fnOnUploadFileByPath(res.tempFilePaths[0]);
}
});
break;
case 1:
// 上传视频
uni.chooseVideo({
count: 1,
success: res => {
console.log(res);
this.fnOnUploadFileByPath(res.tempFilePath);
}
});
break;
case 2:
// 会话选择
uni.chooseMessageFile({
count: 1,
success: res => {
this.fnOnUploadFileByPath(res.tempFiles[0].path);
}
});
break;
}
// #endif
// 安卓app
// #ifdef APP-PLUS
switch (e.index) {
case 0:
// 上传照片
uni.chooseImage({
count: 1,
success: res => {
console.log(res);
this.fnOnUploadFileByPath(res.tempFilePaths[0]);
}
});
break;
case 1:
// 上传视频
uni.chooseVideo({
count: 1,
success: res => {
console.log(res);
this.fnOnUploadFileByPath(res.tempFilePath);
}
});
break;
case 2:
// 上传文件:
// todo:使用第三方控件
AfDocument.openMode(
{
size: 1,
isDown: true,
types: [
{
name: '文档',
value: ['doc', 'wps', 'docx', 'xls', 'xlsx', 'pdf']
},
{
name: '视频',
value: ['mp4']
},
{
name: '音乐',
value: ['mp3', 'flac']
},
{
name: '图片',
value: ['jpg', 'png']
}
]
},
res => {
// this.data = JSON.stringify(res);
this.fnOnUploadFileByFile(res[0].path);
}
);
break;
}
// #endif
// h5
// #ifdef H5
switch (e.index) {
case 0:
// 上传照片
uni.chooseImage({
count: 1,
success: res => {
console.log(res);
this.fnOnUploadFileByPath(res.tempFilePaths[0]);
}
});
break;
case 1:
// 上传视频
uni.chooseVideo({
count: 1,
success: res => {
console.log(res);
this.fnOnUploadFileByPath(res.tempFilePath);
}
});
break;
case 2:
// 上传文件
uni.chooseFile({
count: 1,
success: res => {
console.log(res);
this.fnOnUploadFileByPath(res.tempFilePaths[0]);
}
});
break;
}
// #endif
// #ifndef H5||MP-WEIXIN||APP-PLUS
switch (e.index) {
case 0:
// 上传照片
uni.chooseImage({
count: 1,
success: res => {
this.fnOnUploadFileByFile(res.tempFiles[0]);
}
});
break;
case 1:
// 上传视频
uni.chooseVideo({
count: 1,
success: res => {
this.fnOnUploadFileByFile(res.tempFile);
}
});
break;
}
// #endif
},
// 使用文件路径上传文件
fnOnUploadFileByPath(path) {
uni.showLoading({
title: '文件上传中...',
mask: true
});
uni.uploadFile({
filePath: path,
header: {
'admin-authorization': getAdminAccessToken()
},
url: this.$baseApiUrl + '/api/admin/attachments/upload',
name: 'file',
success: upladRes => {
const _uploadRes = JSON.parse(upladRes.data);
if (_uploadRes.status == 200) {
uni.$tm.toast('文件上传成功!');
setTimeout(() => {
this.fnGetData();
}, 1000);
} else {
uni.$tm.toast(_uploadRes.message);
}
},
fail: err => {
uni.$tm.toast(err.message);
}
});
},
// 使用文件对象上传文件
fnOnUploadFileByFile(file) {
uni.showLoading({
title: '文件上传中...',
mask: true
});
uni.uploadFile({
// #ifdef H5
file: file,
// #endif
// #ifndef H5
files: [file],
// #endif
header: {
'admin-authorization': getAdminAccessToken()
},
url: this.$baseApiUrl + '/api/admin/attachments/upload',
name: 'file',
success: upladRes => {
const _uploadRes = JSON.parse(upladRes.data);
if (_uploadRes.status == 200) {
uni.$tm.toast('文件上传成功!');
setTimeout(() => {
this.fnGetData();
}, 1000);
} else {
uni.$tm.toast(_uploadRes.message);
}
},
fail: err => {
uni.$tm.toast(err.message);
}
});
}
}
};
</script>
<style lang="scss" scoped>
.app-page {
width: 100vw;
min-height: 100vh;
}
.loading-wrap {
padding: 24rpx;
}
.filter-wrap {
position: fixed;
left: 0;
right: 0;
/* #ifdef H5 */
top: 88rpx;
/* #endif */
/* #ifndef H5 */
top: 0;
/* #endif */
width: 100vw;
z-index: 6;
}
.page-content {
}
.content-empty {
width: 100vw;
height: 65vh;
}
.attachment-card {
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
.cover {
width: 170rpx;
height: 160rpx;
background-color: rgb(104, 136, 151);
// box-shadow: 0 6rpx 10rpx rgba(51, 154, 204, 0.2);
&.is-video {
background-color: rgb(3, 174, 252);
}
&.is-image {
background-color: #1cbcb4;
}
.icon {
font-size: 40rpx;
color: #fff;
}
.thumbnai {
width: 100%;
height: 100%;
box-sizing: border-box;
box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
border: 6rpx solid #fff;
}
}
.info {
width: 0;
flex-grow: 1;
.time-icon {
font-size: 24rpx;
}
}
}
.poup-body {
box-sizing: border-box;
height: 66vh;
}
.poup-content {
width: 100%;
overflow: hidden;
}
.btn-wrap {
width: 100%;
}
.file-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
word-break: break-all;
}
</style>