mirror of https://github.com/halo-dev/halo-admin
refactor: attachment detail (#375)
* refactor: attachment detail modal * refactor: attachment media type judgment * refactor: pagination * refactor: delete AttachmentDetailDrawer.vue * refactor: remove unnecessary code * perf: previous and next button statepull/376/head^2
parent
d3eb288327
commit
2939cbbfbd
|
@ -31,7 +31,6 @@
|
|||
"filepond": "^4.30.3",
|
||||
"filepond-plugin-file-validate-type": "^1.2.6",
|
||||
"filepond-plugin-image-preview": "^4.6.10",
|
||||
"flv.js": "^1.6.2",
|
||||
"halo-editor": "^2.8.3",
|
||||
"marked": "^2.1.3",
|
||||
"nprogress": "^0.2.0",
|
||||
|
@ -41,7 +40,6 @@
|
|||
"vue-clipboard2": "^0.3.3",
|
||||
"vue-contextmenujs": "^1.3.13",
|
||||
"vue-count-to": "^1.0.13",
|
||||
"vue-dplayer": "0.0.10",
|
||||
"vue-filepond": "^6.0.3",
|
||||
"vue-ls": "^3.2.2",
|
||||
"vue-router": "^3.5.3",
|
||||
|
|
|
@ -26,7 +26,6 @@ specifiers:
|
|||
filepond: ^4.30.3
|
||||
filepond-plugin-file-validate-type: ^1.2.6
|
||||
filepond-plugin-image-preview: ^4.6.10
|
||||
flv.js: ^1.6.2
|
||||
halo-editor: ^2.8.3
|
||||
husky: ^6.0.0
|
||||
less: ^3.13.1
|
||||
|
@ -42,7 +41,6 @@ specifiers:
|
|||
vue-clipboard2: ^0.3.3
|
||||
vue-contextmenujs: ^1.3.13
|
||||
vue-count-to: ^1.0.13
|
||||
vue-dplayer: 0.0.10
|
||||
vue-filepond: ^6.0.3
|
||||
vue-ls: ^3.2.2
|
||||
vue-router: ^3.5.3
|
||||
|
@ -62,7 +60,6 @@ dependencies:
|
|||
filepond: 4.30.3
|
||||
filepond-plugin-file-validate-type: 1.2.6_filepond@4.30.3
|
||||
filepond-plugin-image-preview: 4.6.10_filepond@4.30.3
|
||||
flv.js: 1.6.2
|
||||
halo-editor: 2.8.3
|
||||
marked: 2.1.3
|
||||
nprogress: 0.2.0
|
||||
|
@ -72,7 +69,6 @@ dependencies:
|
|||
vue-clipboard2: 0.3.3
|
||||
vue-contextmenujs: 1.3.13
|
||||
vue-count-to: 1.0.13
|
||||
vue-dplayer: 0.0.10
|
||||
vue-filepond: 6.0.3_filepond@4.30.3+vue@2.6.14
|
||||
vue-ls: 3.2.2
|
||||
vue-router: 3.5.3
|
||||
|
@ -2820,13 +2816,6 @@ packages:
|
|||
resolution: {integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==}
|
||||
dev: true
|
||||
|
||||
/axios/0.19.2:
|
||||
resolution: {integrity: sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==}
|
||||
deprecated: Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410
|
||||
dependencies:
|
||||
follow-redirects: 1.5.10
|
||||
dev: false
|
||||
|
||||
/axios/0.21.4:
|
||||
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
|
||||
dependencies:
|
||||
|
@ -3142,10 +3131,6 @@ packages:
|
|||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
dev: true
|
||||
|
||||
/balloon-css/1.2.0:
|
||||
resolution: {integrity: sha512-urXwkHgwp6GsXVF+it01485Z2Cj4pnW02ICnM0TemOlkKmCNnDLmyy+ZZiRXBpwldUXO+aRNr7Hdia4CBvXJ5A==}
|
||||
dev: false
|
||||
|
||||
/base/0.11.2:
|
||||
resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -4383,12 +4368,6 @@ packages:
|
|||
ms: 2.0.0
|
||||
dev: true
|
||||
|
||||
/debug/3.1.0:
|
||||
resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==}
|
||||
dependencies:
|
||||
ms: 2.0.0
|
||||
dev: false
|
||||
|
||||
/debug/3.2.7:
|
||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||
dependencies:
|
||||
|
@ -4714,14 +4693,6 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/dplayer/1.26.0:
|
||||
resolution: {integrity: sha512-uOE0w/WdlX7N9d0ppIEcAYrcnUjY52TMX+MBL4lj9Mj+JMljVuaEc5w88HkZp5Q11VqvN/jxnM8ktx2Dr7/MgA==}
|
||||
dependencies:
|
||||
axios: 0.19.2
|
||||
balloon-css: 1.2.0
|
||||
promise-polyfill: 8.1.3
|
||||
dev: false
|
||||
|
||||
/duplexer/0.1.2:
|
||||
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
||||
dev: true
|
||||
|
@ -4893,10 +4864,6 @@ packages:
|
|||
is-symbol: 1.0.4
|
||||
dev: true
|
||||
|
||||
/es6-promise/4.2.8:
|
||||
resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
|
||||
dev: false
|
||||
|
||||
/escalade/3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -5537,13 +5504,6 @@ packages:
|
|||
readable-stream: 2.3.7
|
||||
dev: true
|
||||
|
||||
/flv.js/1.6.2:
|
||||
resolution: {integrity: sha512-xre4gUbX1MPtgQRKj2pxJENp/RnaHaxYvy3YToVVCrSmAWUu85b9mug6pTXF6zakUjNP2lFWZ1rkSX7gxhB/2A==}
|
||||
dependencies:
|
||||
es6-promise: 4.2.8
|
||||
webworkify-webpack: 2.1.5
|
||||
dev: false
|
||||
|
||||
/follow-redirects/1.14.4:
|
||||
resolution: {integrity: sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==}
|
||||
engines: {node: '>=4.0'}
|
||||
|
@ -5553,13 +5513,6 @@ packages:
|
|||
debug:
|
||||
optional: true
|
||||
|
||||
/follow-redirects/1.5.10:
|
||||
resolution: {integrity: sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
dependencies:
|
||||
debug: 3.1.0
|
||||
dev: false
|
||||
|
||||
/for-each/0.3.3:
|
||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||
dependencies:
|
||||
|
@ -8055,6 +8008,7 @@ packages:
|
|||
|
||||
/ms/2.0.0:
|
||||
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
|
||||
dev: true
|
||||
|
||||
/ms/2.1.1:
|
||||
resolution: {integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==}
|
||||
|
@ -9318,10 +9272,6 @@ packages:
|
|||
resolution: {integrity: sha1-mEcocL8igTL8vdhoEputEsPAKeM=}
|
||||
dev: true
|
||||
|
||||
/promise-polyfill/8.1.3:
|
||||
resolution: {integrity: sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==}
|
||||
dev: false
|
||||
|
||||
/prompts/2.4.1:
|
||||
resolution: {integrity: sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==}
|
||||
engines: {node: '>= 6'}
|
||||
|
@ -11222,13 +11172,6 @@ packages:
|
|||
resolution: {integrity: sha512-6R4OVBVNtQTlcbXu6SJ8ENR35M2/CdWt3Jmv57jOUM+1ojiFmjVGvZPH8DfHpMDSA+ITs+EW5V6qthADxeyYOQ==}
|
||||
dev: false
|
||||
|
||||
/vue-dplayer/0.0.10:
|
||||
resolution: {integrity: sha512-l6d6OnhEUO87lhAees9mEBUFsG5Thqv/VeEVZlSQ993/S0oxvWTDLjjkzH59hO2PALimBK2kB6yNYsHyJBgotw==}
|
||||
dependencies:
|
||||
dplayer: 1.26.0
|
||||
vue-github-badge: 1.0.1
|
||||
dev: false
|
||||
|
||||
/vue-eslint-parser/7.11.0_eslint@6.8.0:
|
||||
resolution: {integrity: sha512-qh3VhDLeh773wjgNTl7ss0VejY9bMMa0GoDG2fQVyDzRFdiU3L7fw74tWZDHNQXdZqxO3EveQroa9ct39D2nqg==}
|
||||
engines: {node: '>=8.10'}
|
||||
|
@ -11257,10 +11200,6 @@ packages:
|
|||
vue: 2.6.14
|
||||
dev: false
|
||||
|
||||
/vue-github-badge/1.0.1:
|
||||
resolution: {integrity: sha1-3/fOBzIOZKIY7fEGsVpDF27AYQY=}
|
||||
dev: false
|
||||
|
||||
/vue-hot-reload-api/2.3.4:
|
||||
resolution: {integrity: sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==}
|
||||
dev: true
|
||||
|
@ -11607,10 +11546,6 @@ packages:
|
|||
engines: {node: '>=0.8.0'}
|
||||
dev: true
|
||||
|
||||
/webworkify-webpack/2.1.5:
|
||||
resolution: {integrity: sha512-2akF8FIyUvbiBBdD+RoHpoTbHMQF2HwjcxfDvgztAX5YwbZNyrtfUMgvfgFVsgDhDPVTlkbb5vyasqDHfIDPQw==}
|
||||
dev: false
|
||||
|
||||
/whatwg-encoding/1.0.5:
|
||||
resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
|
||||
dependencies:
|
||||
|
|
|
@ -1,41 +1,41 @@
|
|||
<template>
|
||||
<page-view>
|
||||
<a-row :gutter="12" type="flex" align="middle">
|
||||
<a-row :gutter="12" align="middle" type="flex">
|
||||
<a-col :span="24" class="pb-3">
|
||||
<a-card :bordered="false" :bodyStyle="{ padding: '16px' }">
|
||||
<a-card :bodyStyle="{ padding: '16px' }" :bordered="false">
|
||||
<div class="table-page-search-wrapper">
|
||||
<a-form layout="inline">
|
||||
<a-row :gutter="48">
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="关键词:">
|
||||
<a-input v-model="queryParam.keyword" @keyup.enter="handleQuery()" />
|
||||
<a-input v-model="list.params.keyword" @keyup.enter="handleQuery()" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="存储位置:">
|
||||
<a-select
|
||||
v-model="queryParam.attachmentType"
|
||||
@change="handleQuery()"
|
||||
:loading="typesLoading"
|
||||
v-model="list.params.attachmentType"
|
||||
:loading="types.loading"
|
||||
allowClear
|
||||
@change="handleQuery()"
|
||||
>
|
||||
<a-select-option v-for="item in types" :key="item" :value="item">{{
|
||||
attachmentType[item].text
|
||||
}}</a-select-option>
|
||||
<a-select-option v-for="item in types.data" :key="item" :value="item">
|
||||
{{ item | typeText }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="文件类型:">
|
||||
<a-select
|
||||
v-model="queryParam.mediaType"
|
||||
@change="handleQuery()"
|
||||
:loading="mediaTypesLoading"
|
||||
v-model="list.params.mediaType"
|
||||
:loading="mediaTypes.loading"
|
||||
allowClear
|
||||
@change="handleQuery()"
|
||||
>
|
||||
<a-select-option v-for="(item, index) in mediaTypes" :key="index" :value="item">{{
|
||||
item
|
||||
}}</a-select-option>
|
||||
<a-select-option v-for="(item, index) in mediaTypes.data" :key="index" :value="item"
|
||||
>{{ item }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
@ -51,19 +51,19 @@
|
|||
</a-form>
|
||||
</div>
|
||||
<div class="mb-0 table-operator">
|
||||
<a-button type="primary" icon="cloud-upload" @click="() => (uploadVisible = true)">上传</a-button>
|
||||
<a-button icon="select" v-show="!supportMultipleSelection" @click="handleMultipleSelection">
|
||||
<a-button icon="cloud-upload" type="primary" @click="upload.visible = true">上传</a-button>
|
||||
<a-button v-show="!supportMultipleSelection" icon="select" @click="handleMultipleSelection">
|
||||
批量操作
|
||||
</a-button>
|
||||
<a-button
|
||||
type="danger"
|
||||
icon="delete"
|
||||
v-show="supportMultipleSelection"
|
||||
icon="delete"
|
||||
type="danger"
|
||||
@click="handleDeleteAttachmentInBatch"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
<a-button icon="close" v-show="supportMultipleSelection" @click="handleCancelMultipleSelection">
|
||||
<a-button v-show="supportMultipleSelection" icon="close" @click="handleCancelMultipleSelection">
|
||||
取消
|
||||
</a-button>
|
||||
</div>
|
||||
|
@ -71,36 +71,36 @@
|
|||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-list
|
||||
class="attachments-group"
|
||||
:dataSource="list.data"
|
||||
:grid="{ gutter: 12, xs: 2, sm: 2, md: 4, lg: 6, xl: 6, xxl: 6 }"
|
||||
:dataSource="formattedDatas"
|
||||
:loading="listLoading"
|
||||
:loading="list.loading"
|
||||
class="attachments-group"
|
||||
>
|
||||
<a-list-item slot="renderItem" slot-scope="item, index" :key="index">
|
||||
<a-list-item :key="index" slot="renderItem" slot-scope="item, index">
|
||||
<a-card
|
||||
:bodyStyle="{ padding: 0 }"
|
||||
hoverable
|
||||
@click="handleShowDetailDrawer(item)"
|
||||
@click="handleOpenDetail(item)"
|
||||
@contextmenu.prevent="handleContextMenu($event, item)"
|
||||
>
|
||||
<div class="attach-thumb attachments-group-item">
|
||||
<span v-if="!handleJudgeMediaType(item)" class="attachments-group-item-type">{{ item.suffix }}</span>
|
||||
<span v-if="!isImage(item)" class="attachments-group-item-type">{{ item.suffix }}</span>
|
||||
<span
|
||||
v-else
|
||||
class="attachments-group-item-img"
|
||||
:style="`background-image:url(${item.thumbPath})`"
|
||||
class="attachments-group-item-img"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<a-card-meta class="p-3">
|
||||
<ellipsis :length="isMobile() ? 12 : 16" tooltip slot="description">{{ item.name }}</ellipsis>
|
||||
<ellipsis slot="description" :length="isMobile() ? 12 : 16" tooltip>{{ item.name }}</ellipsis>
|
||||
</a-card-meta>
|
||||
<a-checkbox
|
||||
class="select-attachment-checkbox"
|
||||
:style="getCheckStatus(item.id) ? selectedAttachmentStyle : ''"
|
||||
:checked="getCheckStatus(item.id)"
|
||||
@click="handleAttachmentSelectionChanged($event, item)"
|
||||
v-show="supportMultipleSelection"
|
||||
:checked="getCheckStatus(item.id)"
|
||||
:style="getCheckStatus(item.id) ? selectedAttachmentStyle : ''"
|
||||
class="select-attachment-checkbox"
|
||||
@click="handleAttachmentSelectionChanged($event, item)"
|
||||
></a-checkbox>
|
||||
</a-card>
|
||||
</a-list-item>
|
||||
|
@ -109,87 +109,120 @@
|
|||
</a-row>
|
||||
<div class="page-wrapper">
|
||||
<a-pagination
|
||||
class="pagination"
|
||||
:current="pagination.page"
|
||||
:total="pagination.total"
|
||||
:defaultPageSize="pagination.size"
|
||||
:pageSizeOptions="['18', '36', '54', '72', '90', '108']"
|
||||
showSizeChanger
|
||||
@change="handlePaginationChange"
|
||||
@showSizeChange="handlePaginationChange"
|
||||
:total="pagination.total"
|
||||
class="pagination"
|
||||
showLessItems
|
||||
showSizeChanger
|
||||
@change="handlePageChange"
|
||||
@showSizeChange="handlePageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
<a-modal title="上传附件" v-model="uploadVisible" :footer="null" :afterClose="onUploadClose" destroyOnClose>
|
||||
<FilePondUpload ref="upload" :uploadHandler="uploadHandler"></FilePondUpload>
|
||||
<a-modal v-model="upload.visible" :afterClose="onUploadClose" :footer="null" destroyOnClose title="上传附件">
|
||||
<FilePondUpload ref="upload" :uploadHandler="upload.handler"></FilePondUpload>
|
||||
</a-modal>
|
||||
<AttachmentDetailDrawer
|
||||
v-model="drawerVisible"
|
||||
v-if="selectAttachment"
|
||||
:attachment="selectAttachment"
|
||||
<AttachmentDetailModal
|
||||
:addToPhoto="true"
|
||||
:attachment="list.selected"
|
||||
:visible.sync="detailVisible"
|
||||
@delete="handleListAttachments()"
|
||||
/>
|
||||
>
|
||||
<template #extraFooter>
|
||||
<a-button :disabled="selectPreviousButtonDisabled" @click="handleSelectPrevious">上一项</a-button>
|
||||
<a-button :disabled="selectNextButtonDisabled" @click="handleSelectNext">下一项</a-button>
|
||||
</template>
|
||||
</AttachmentDetailModal>
|
||||
</page-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
||||
import { PageView } from '@/layouts'
|
||||
import AttachmentDetailDrawer from './components/AttachmentDetailDrawer'
|
||||
import AttachmentDetailModal from './components/AttachmentDetailModal.vue'
|
||||
import attachmentApi from '@/api/attachment'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PageView,
|
||||
AttachmentDetailDrawer
|
||||
AttachmentDetailModal
|
||||
},
|
||||
mixins: [mixin, mixinDevice],
|
||||
filters: {
|
||||
typeText(type) {
|
||||
return attachmentApi.type[type].text
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
attachmentType: attachmentApi.type,
|
||||
listLoading: true,
|
||||
uploadVisible: false,
|
||||
list: {
|
||||
data: [],
|
||||
loading: false,
|
||||
total: 0,
|
||||
hasNext: false,
|
||||
hasPrevious: false,
|
||||
selected: {},
|
||||
params: {
|
||||
page: 0,
|
||||
size: 18,
|
||||
keyword: null,
|
||||
mediaType: null,
|
||||
attachmentType: null
|
||||
}
|
||||
},
|
||||
|
||||
mediaTypes: {
|
||||
data: [],
|
||||
loading: false
|
||||
},
|
||||
|
||||
types: {
|
||||
data: [],
|
||||
loading: false
|
||||
},
|
||||
|
||||
upload: {
|
||||
handler: attachmentApi.upload,
|
||||
visible: false
|
||||
},
|
||||
|
||||
detailVisible: false,
|
||||
|
||||
supportMultipleSelection: false,
|
||||
selectedAttachmentCheckbox: {},
|
||||
batchSelectedAttachments: [],
|
||||
selectAttachment: {},
|
||||
attachments: [],
|
||||
mediaTypes: [],
|
||||
mediaTypesLoading: false,
|
||||
types: [],
|
||||
typesLoading: false,
|
||||
editable: false,
|
||||
pagination: {
|
||||
page: 1,
|
||||
size: 18,
|
||||
sort: null,
|
||||
total: 1
|
||||
},
|
||||
queryParam: {
|
||||
page: 0,
|
||||
size: 18,
|
||||
sort: null,
|
||||
keyword: null,
|
||||
mediaType: null,
|
||||
attachmentType: null
|
||||
},
|
||||
drawerVisible: false,
|
||||
uploadHandler: attachmentApi.upload
|
||||
batchSelectedAttachments: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedDatas() {
|
||||
return this.attachments.map(attachment => {
|
||||
attachment.typeProperty = this.attachmentType[attachment.type]
|
||||
return attachment
|
||||
})
|
||||
},
|
||||
selectedAttachmentStyle() {
|
||||
return {
|
||||
border: `2px solid ${this.color()}`
|
||||
}
|
||||
},
|
||||
isImage() {
|
||||
return function(attachment) {
|
||||
if (!attachment || !attachment.mediaType) {
|
||||
return false
|
||||
}
|
||||
return attachment.mediaType.startsWith('image')
|
||||
}
|
||||
},
|
||||
pagination() {
|
||||
return {
|
||||
page: this.list.params.page + 1,
|
||||
size: this.list.params.size,
|
||||
total: this.list.total
|
||||
}
|
||||
},
|
||||
selectPreviousButtonDisabled() {
|
||||
const index = this.list.data.findIndex(attachment => attachment.id === this.list.selected.id)
|
||||
return index === 0 && !this.list.hasPrevious
|
||||
},
|
||||
selectNextButtonDisabled() {
|
||||
const index = this.list.data.findIndex(attachment => attachment.id === this.list.selected.id)
|
||||
return index === this.list.data.length - 1 && !this.list.hasNext
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -197,71 +230,79 @@ export default {
|
|||
this.handleListMediaTypes()
|
||||
this.handleListTypes()
|
||||
},
|
||||
destroyed: function() {
|
||||
if (this.drawerVisible) {
|
||||
this.drawerVisible = false
|
||||
}
|
||||
},
|
||||
beforeRouteLeave(to, from, next) {
|
||||
if (this.drawerVisible) {
|
||||
this.drawerVisible = false
|
||||
}
|
||||
next()
|
||||
},
|
||||
methods: {
|
||||
...mapGetters(['color']),
|
||||
handleListAttachments() {
|
||||
this.listLoading = true
|
||||
this.queryParam.page = this.pagination.page - 1
|
||||
this.queryParam.size = this.pagination.size
|
||||
this.queryParam.sort = this.pagination.sort
|
||||
attachmentApi
|
||||
.query(this.queryParam)
|
||||
.then(response => {
|
||||
this.attachments = response.data.data.content
|
||||
this.pagination.total = response.data.data.total
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
this.listLoading = false
|
||||
}, 200)
|
||||
})
|
||||
|
||||
/**
|
||||
* List attachments
|
||||
*/
|
||||
async handleListAttachments() {
|
||||
try {
|
||||
this.list.loading = true
|
||||
|
||||
const response = await attachmentApi.query(this.list.params)
|
||||
|
||||
this.list.data = response.data.data.content
|
||||
this.list.total = response.data.data.total
|
||||
this.list.hasNext = response.data.data.hasNext
|
||||
this.list.hasPrevious = response.data.data.hasPrevious
|
||||
} catch (error) {
|
||||
this.$log.error(error)
|
||||
} finally {
|
||||
this.list.loading = false
|
||||
}
|
||||
},
|
||||
handleListMediaTypes() {
|
||||
this.mediaTypesLoading = true
|
||||
attachmentApi
|
||||
.getMediaTypes()
|
||||
.then(response => {
|
||||
this.mediaTypes = response.data.data
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
this.mediaTypesLoading = false
|
||||
}, 200)
|
||||
})
|
||||
|
||||
/**
|
||||
* List attachment media types
|
||||
*/
|
||||
async handleListMediaTypes() {
|
||||
try {
|
||||
this.mediaTypes.loading = true
|
||||
|
||||
const response = await attachmentApi.getMediaTypes()
|
||||
|
||||
this.mediaTypes.data = response.data.data
|
||||
} catch (error) {
|
||||
this.$log.error(error)
|
||||
} finally {
|
||||
this.mediaTypes.loading = false
|
||||
}
|
||||
},
|
||||
handleListTypes() {
|
||||
this.typesLoading = true
|
||||
attachmentApi
|
||||
.getTypes()
|
||||
.then(response => {
|
||||
this.types = response.data.data
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
this.typesLoading = false
|
||||
}, 200)
|
||||
})
|
||||
|
||||
/**
|
||||
* List attachment upload types
|
||||
*/
|
||||
async handleListTypes() {
|
||||
try {
|
||||
this.types.loading = true
|
||||
|
||||
const response = await attachmentApi.getTypes()
|
||||
|
||||
this.types.data = response.data.data
|
||||
} catch (error) {
|
||||
this.$log.error(error)
|
||||
} finally {
|
||||
this.types.loading = false
|
||||
}
|
||||
},
|
||||
handleShowDetailDrawer(attachment) {
|
||||
this.selectAttachment = attachment
|
||||
this.drawerVisible = !this.supportMultipleSelection
|
||||
|
||||
/**
|
||||
* Handle open attachment detail modal event
|
||||
*/
|
||||
handleOpenDetail(attachment) {
|
||||
this.list.selected = attachment
|
||||
this.detailVisible = !this.supportMultipleSelection
|
||||
},
|
||||
|
||||
/**
|
||||
* Show context menu
|
||||
*/
|
||||
handleContextMenu(event, item) {
|
||||
this.$contextmenu({
|
||||
items: [
|
||||
{
|
||||
label: `${this.handleJudgeMediaType(item) ? '复制图片链接' : '复制文件链接'}`,
|
||||
label: `复制${this.isImage(item) ? '图片' : '文件'}链接`,
|
||||
onClick: () => {
|
||||
const text = `${encodeURI(item.path)}`
|
||||
this.$copyText(text)
|
||||
|
@ -277,7 +318,7 @@ export default {
|
|||
divided: true
|
||||
},
|
||||
{
|
||||
disabled: !this.handleJudgeMediaType(item),
|
||||
disabled: !this.isImage(item),
|
||||
label: '复制 Markdown 格式链接',
|
||||
onClick: () => {
|
||||
const text = `![${item.name}](${encodeURI(item.path)})`
|
||||
|
@ -290,6 +331,22 @@ export default {
|
|||
this.$log.debug('copy.err', err)
|
||||
this.$message.error('复制失败!')
|
||||
})
|
||||
},
|
||||
divided: true
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
onClick: () => {
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确定删除该附件?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
await attachmentApi.delete(item.id)
|
||||
this.handleListAttachments()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -298,54 +355,63 @@ export default {
|
|||
})
|
||||
return false
|
||||
},
|
||||
handlePaginationChange(page, size) {
|
||||
this.$log.debug(`Current: ${page}, PageSize: ${size}`)
|
||||
this.pagination.page = page
|
||||
this.pagination.size = size
|
||||
|
||||
/**
|
||||
* Handle page change
|
||||
*/
|
||||
handlePageChange(page = 1) {
|
||||
this.list.params.page = page - 1
|
||||
this.handleListAttachments()
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle page size change
|
||||
*/
|
||||
handlePageSizeChange(current, size) {
|
||||
this.$log.debug(`Current: ${current}, PageSize: ${size}`)
|
||||
this.list.params.page = 0
|
||||
this.list.params.size = size
|
||||
this.handleListAttachments()
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset query params
|
||||
*/
|
||||
handleResetParam() {
|
||||
this.queryParam.keyword = null
|
||||
this.queryParam.mediaType = null
|
||||
this.queryParam.attachmentType = null
|
||||
this.handlePaginationChange(1, this.pagination.size)
|
||||
this.list.params.keyword = null
|
||||
this.list.params.mediaType = null
|
||||
this.list.params.attachmentType = null
|
||||
this.handlePageChange()
|
||||
this.handleListMediaTypes()
|
||||
this.handleListTypes()
|
||||
},
|
||||
|
||||
/**
|
||||
* Search attachments
|
||||
*/
|
||||
handleQuery() {
|
||||
this.handlePaginationChange(1, this.pagination.size)
|
||||
this.handlePageChange()
|
||||
},
|
||||
onUploadClose() {
|
||||
this.$refs.upload.handleClearFileList()
|
||||
this.handlePaginationChange(1, this.pagination.size)
|
||||
this.handlePageChange()
|
||||
this.handleListMediaTypes()
|
||||
this.handleListTypes()
|
||||
},
|
||||
handleJudgeMediaType(attachment) {
|
||||
const mediaType = attachment.mediaType
|
||||
// 判断文件类型
|
||||
if (mediaType) {
|
||||
const prefix = mediaType.split('/')[0]
|
||||
|
||||
return prefix === 'image'
|
||||
}
|
||||
// 没有获取到文件返回false
|
||||
return false
|
||||
},
|
||||
getCheckStatus(key) {
|
||||
return this.selectedAttachmentCheckbox[key] || false
|
||||
},
|
||||
handleMultipleSelection() {
|
||||
this.supportMultipleSelection = true
|
||||
// 不允许附件详情抽屉显示
|
||||
this.drawerVisible = false
|
||||
this.attachments.forEach(item => {
|
||||
this.detailVisible = false
|
||||
this.list.data.forEach(item => {
|
||||
this.$set(this.selectedAttachmentCheckbox, item.id, false)
|
||||
})
|
||||
},
|
||||
handleCancelMultipleSelection() {
|
||||
this.supportMultipleSelection = false
|
||||
this.drawerVisible = false
|
||||
this.detailVisible = false
|
||||
this.batchSelectedAttachments = []
|
||||
for (const key in this.selectedCheckbox) {
|
||||
this.$set(this.selectedAttachmentCheckbox, key, false)
|
||||
|
@ -363,6 +429,10 @@ export default {
|
|||
this.batchSelectedAttachments.splice(index, 1)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes selected attachments
|
||||
*/
|
||||
handleDeleteAttachmentInBatch() {
|
||||
const that = this
|
||||
if (this.batchSelectedAttachments.length <= 0) {
|
||||
|
@ -385,6 +455,40 @@ export default {
|
|||
},
|
||||
onCancel() {}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Select previous attachment
|
||||
*/
|
||||
async handleSelectPrevious() {
|
||||
const index = this.list.data.findIndex(item => item.id === this.list.selected.id)
|
||||
if (index > 0) {
|
||||
this.list.selected = this.list.data[index - 1]
|
||||
return
|
||||
}
|
||||
if (index === 0 && this.list.hasPrevious) {
|
||||
this.list.params.page--
|
||||
await this.handleListAttachments()
|
||||
|
||||
this.list.selected = this.list.data[this.list.data.length - 1]
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Select next attachment
|
||||
*/
|
||||
async handleSelectNext() {
|
||||
const index = this.list.data.findIndex(item => item.id === this.list.selected.id)
|
||||
if (index < this.list.data.length - 1) {
|
||||
this.list.selected = this.list.data[index + 1]
|
||||
return
|
||||
}
|
||||
if (index === this.list.data.length - 1 && this.list.hasNext) {
|
||||
this.list.params.page++
|
||||
await this.handleListAttachments()
|
||||
|
||||
this.list.selected = this.list.data[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,294 +0,0 @@
|
|||
<template>
|
||||
<a-drawer
|
||||
title="附件详情"
|
||||
:width="isMobile() ? '100%' : '480'"
|
||||
closable
|
||||
:visible="visible"
|
||||
destroyOnClose
|
||||
@close="onClose"
|
||||
>
|
||||
<a-row type="flex" align="middle">
|
||||
<a-col :span="24">
|
||||
<div class="attach-detail-img">
|
||||
<div v-show="nonsupportPreviewVisible">此文件不支持预览</div>
|
||||
<a :href="attachment.path" target="_blank">
|
||||
<img :src="attachment.path" v-show="photoPreviewVisible" class="w-full" loading="lazy" />
|
||||
</a>
|
||||
<d-player ref="player" :options="videoOptions" v-show="videoPreviewVisible" class="w-full video-player-box">
|
||||
</d-player>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-divider />
|
||||
<a-col :span="24">
|
||||
<a-list itemLayout="horizontal">
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template slot="description" v-if="editable">
|
||||
<a-input ref="nameInput" v-model="attachment.name" @blur="doUpdateAttachment" />
|
||||
</template>
|
||||
<template slot="description" v-else>{{ attachment.name }}</template>
|
||||
<span slot="title">
|
||||
附件名:
|
||||
<a href="javascript:void(0);">
|
||||
<a-icon type="edit" @click="handleEditName" />
|
||||
</a>
|
||||
</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="attachment.mediaType">
|
||||
<span slot="title">附件类型:</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="attachment.typeProperty">
|
||||
<span slot="title">存储位置:</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template slot="description">
|
||||
{{ attachment.size | fileSizeFormat }}
|
||||
</template>
|
||||
<span slot="title">附件大小:</span>
|
||||
</a-list-item-meta>
|
||||
</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>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template slot="description">
|
||||
{{ attachment.createTime | moment }}
|
||||
</template>
|
||||
<span slot="title">上传日期:</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="attachment.path">
|
||||
<span slot="title">
|
||||
普通链接:
|
||||
<a href="javascript:void(0);" @click="handleCopyNormalLink">
|
||||
<a-icon type="copy" />
|
||||
</a>
|
||||
</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item v-if="photoPreviewVisible">
|
||||
<a-list-item-meta>
|
||||
<span slot="description">![{{ attachment.name }}]({{ attachment.path }})</span>
|
||||
<span slot="title">
|
||||
Markdown 格式:
|
||||
<a href="javascript:void(0);" @click="handleCopyMarkdownLink">
|
||||
<a-icon type="copy" />
|
||||
</a>
|
||||
</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-divider class="divider-transparent" />
|
||||
<div class="bottom-control">
|
||||
<a-space>
|
||||
<a-popconfirm
|
||||
title="你确定要添加到图库?"
|
||||
@confirm="handleAddToPhoto"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
v-if="addToPhoto"
|
||||
>
|
||||
<a-button type="dashed">添加到图库</a-button>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm title="你确定要删除该附件?" @confirm="handleDeleteAttachment" okText="确定" cancelText="取消">
|
||||
<ReactiveButton
|
||||
type="danger"
|
||||
@callback="handleDeletedCallback"
|
||||
:loading="deleting"
|
||||
:errored="deleteErrored"
|
||||
text="删除"
|
||||
loadedText="删除成功"
|
||||
erroredText="删除失败"
|
||||
></ReactiveButton>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
||||
import attachmentApi from '@/api/attachment'
|
||||
import photoApi from '@/api/photo'
|
||||
import 'vue-dplayer/dist/vue-dplayer.css'
|
||||
import VueDPlayer from 'vue-dplayer'
|
||||
import flvjs from 'flv.js'
|
||||
window.flvjs = flvjs
|
||||
|
||||
export default {
|
||||
name: 'AttachmentDetailDrawer',
|
||||
mixins: [mixin, mixinDevice],
|
||||
components: {
|
||||
'd-player': VueDPlayer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editable: false,
|
||||
photo: {},
|
||||
photoPreviewVisible: false,
|
||||
videoPreviewVisible: false,
|
||||
nonsupportPreviewVisible: false,
|
||||
player: {},
|
||||
deleting: false,
|
||||
deleteErrored: false,
|
||||
videoOptions: {
|
||||
lang: 'zh-cn',
|
||||
video: {
|
||||
url: '',
|
||||
type: 'auto'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
model: {
|
||||
prop: 'visible',
|
||||
event: 'close'
|
||||
},
|
||||
props: {
|
||||
attachment: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
addToPhoto: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.player = this.$refs.player
|
||||
},
|
||||
watch: {
|
||||
attachment(newValue) {
|
||||
if (newValue) {
|
||||
this.handleJudgeMediaType(newValue)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleDeleteAttachment() {
|
||||
this.deleting = true
|
||||
attachmentApi
|
||||
.delete(this.attachment.id)
|
||||
.catch(() => {
|
||||
this.deleteErrored = true
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
this.deleting = false
|
||||
}, 400)
|
||||
})
|
||||
},
|
||||
handleDeletedCallback() {
|
||||
this.$emit('delete', this.attachment)
|
||||
this.deleteErrored = false
|
||||
this.onClose()
|
||||
},
|
||||
handleEditName() {
|
||||
this.editable = !this.editable
|
||||
if (this.editable) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.nameInput.focus()
|
||||
})
|
||||
}
|
||||
},
|
||||
doUpdateAttachment() {
|
||||
if (!this.attachment.name) {
|
||||
this.$notification['error']({
|
||||
message: '提示',
|
||||
description: '附件名称不能为空!'
|
||||
})
|
||||
return
|
||||
}
|
||||
attachmentApi.update(this.attachment.id, this.attachment).then(response => {
|
||||
this.$log.debug('Updated attachment', response.data.data)
|
||||
})
|
||||
this.editable = false
|
||||
},
|
||||
handleCopyNormalLink() {
|
||||
const text = `${encodeURI(this.attachment.path)}`
|
||||
this.$copyText(text)
|
||||
.then(message => {
|
||||
this.$log.debug('copy', message)
|
||||
this.$message.success('复制成功!')
|
||||
})
|
||||
.catch(err => {
|
||||
this.$log.debug('copy.err', err)
|
||||
this.$message.error('复制失败!')
|
||||
})
|
||||
},
|
||||
handleCopyMarkdownLink() {
|
||||
const text = `![${this.attachment.name}](${encodeURI(this.attachment.path)})`
|
||||
this.$copyText(text)
|
||||
.then(message => {
|
||||
this.$log.debug('copy', message)
|
||||
this.$message.success('复制成功!')
|
||||
})
|
||||
.catch(err => {
|
||||
this.$log.debug('copy.err', err)
|
||||
this.$message.error('复制失败!')
|
||||
})
|
||||
},
|
||||
handleAddToPhoto() {
|
||||
this.photo['name'] = this.attachment.name
|
||||
this.photo['thumbnail'] = encodeURI(this.attachment.thumbPath)
|
||||
this.photo['url'] = encodeURI(this.attachment.path)
|
||||
this.photo['takeTime'] = new Date().getTime()
|
||||
photoApi.create(this.photo).then(() => {
|
||||
this.$message.success('添加成功!')
|
||||
this.photo = {}
|
||||
})
|
||||
},
|
||||
onClose() {
|
||||
this.$emit('close', false)
|
||||
},
|
||||
handleJudgeMediaType(attachment) {
|
||||
const mediaType = attachment.mediaType
|
||||
// 判断文件类型
|
||||
if (mediaType) {
|
||||
const prefix = mediaType.split('/')[0]
|
||||
|
||||
if (prefix === 'video' || prefix === 'flv') {
|
||||
// 控制各个组件的显示
|
||||
this.handlePreviewVisible(false, true, false)
|
||||
|
||||
// 去除视频地址后面的参数
|
||||
const lastIndex = attachment.path.lastIndexOf('?')
|
||||
const path = attachment.path.substring(0, lastIndex)
|
||||
|
||||
// 设置视频地址
|
||||
this.$set(this.videoOptions.video, 'url', path)
|
||||
this.$log.debug('video url', path)
|
||||
} else if (prefix === 'image') {
|
||||
this.handlePreviewVisible(true, false, false)
|
||||
} else {
|
||||
this.handlePreviewVisible(false, false, true)
|
||||
}
|
||||
}
|
||||
},
|
||||
handlePreviewVisible(photo, video, nonsupport) {
|
||||
// 为了更好的使vue监听到组件变化及时刷新,方式修改组件后需要刷新才能显示一下部分
|
||||
this.$set(this, 'photoPreviewVisible', photo)
|
||||
this.$set(this, 'videoPreviewVisible', video)
|
||||
this.$set(this, 'nonsupportPreviewVisible', nonsupport)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,230 @@
|
|||
<template>
|
||||
<a-modal title="附件详情" :width="isMobile() ? '100%' : '50%'" v-model="modalVisible">
|
||||
<a-row type="flex" :gutter="24">
|
||||
<a-col :xl="9" :lg="9" :md="24" :sm="24" :xs="24">
|
||||
<div class="attach-detail-img pb-3">
|
||||
<a v-if="isImage" :href="attachment.path" target="_blank">
|
||||
<img :src="attachment.path" class="w-full" loading="lazy" />
|
||||
</a>
|
||||
<div v-else>此文件不支持预览</div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :xl="15" :lg="15" :md="24" :sm="24" :xs="24">
|
||||
<a-list itemLayout="horizontal">
|
||||
<a-list-item style="padding-top: 0;">
|
||||
<a-list-item-meta>
|
||||
<template slot="description" v-if="editable">
|
||||
<a-input ref="nameInput" v-model="attachment.name" @blur="handleUpdateName" />
|
||||
</template>
|
||||
<template slot="description" v-else>{{ attachment.name }}</template>
|
||||
<span slot="title">
|
||||
附件名:
|
||||
<a href="javascript:void(0);">
|
||||
<a-icon type="edit" @click="handleEditName" />
|
||||
</a>
|
||||
</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="attachment.mediaType">
|
||||
<span slot="title">附件类型:</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="attachment.type | typeText">
|
||||
<span slot="title">存储位置:</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template slot="description">
|
||||
{{ attachment.size | fileSizeFormat }}
|
||||
</template>
|
||||
<span slot="title">附件大小:</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item v-if="isImage">
|
||||
<a-list-item-meta :description="attachment.height + 'x' + attachment.width">
|
||||
<span slot="title">图片尺寸:</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template slot="description">
|
||||
{{ attachment.createTime | moment }}
|
||||
</template>
|
||||
<span slot="title">上传日期:</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template #description>
|
||||
<a :href="attachment.path" target="_blank">{{ attachment.path }}</a>
|
||||
</template>
|
||||
<span slot="title">
|
||||
普通链接:
|
||||
<a href="javascript:void(0);" @click="handleCopyLink(`${encodeURI(attachment.path)}`)">
|
||||
<a-icon type="copy" />
|
||||
</a>
|
||||
</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item v-if="isImage">
|
||||
<a-list-item-meta>
|
||||
<span slot="description">![{{ attachment.name }}]({{ attachment.path }})</span>
|
||||
<span slot="title">
|
||||
Markdown 格式:
|
||||
<a
|
||||
href="javascript:void(0);"
|
||||
@click="handleCopyLink(`![${attachment.name}](${encodeURI(attachment.path)})`)"
|
||||
>
|
||||
<a-icon type="copy" />
|
||||
</a>
|
||||
</span>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<template #footer>
|
||||
<slot name="extraFooter" />
|
||||
<a-popconfirm title="你确定要删除该附件?" @confirm="handleDelete" okText="确定" cancelText="取消">
|
||||
<ReactiveButton
|
||||
type="danger"
|
||||
@callback="handleDeletedCallback"
|
||||
:loading="deleting"
|
||||
:errored="deleteErrored"
|
||||
text="删除"
|
||||
icon="delete"
|
||||
loadedText="删除成功"
|
||||
erroredText="删除失败"
|
||||
></ReactiveButton>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
||||
import attachmentApi from '@/api/attachment'
|
||||
|
||||
export default {
|
||||
name: 'AttachmentDetailModal',
|
||||
mixins: [mixin, mixinDevice],
|
||||
filters: {
|
||||
typeText(type) {
|
||||
return type ? attachmentApi.type[type].text : ''
|
||||
}
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
attachment: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editable: false,
|
||||
deleting: false,
|
||||
deleteErrored: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
modalVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:visible', value)
|
||||
}
|
||||
},
|
||||
isImage() {
|
||||
if (!this.attachment || !this.attachment.mediaType) {
|
||||
return false
|
||||
}
|
||||
return this.attachment.mediaType.startsWith('image')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Deletes the attachment
|
||||
*/
|
||||
async handleDelete() {
|
||||
try {
|
||||
this.deleting = true
|
||||
|
||||
await attachmentApi.delete(this.attachment.id)
|
||||
} catch (error) {
|
||||
this.$log.error(error)
|
||||
this.deleteErrored = true
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
this.deleting = false
|
||||
}, 400)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the deletion callback event
|
||||
*/
|
||||
handleDeletedCallback() {
|
||||
this.$emit('delete', this.attachment)
|
||||
this.deleteErrored = false
|
||||
this.modalVisible = false
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the edit name input
|
||||
*/
|
||||
handleEditName() {
|
||||
this.editable = !this.editable
|
||||
if (this.editable) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.nameInput.focus()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the attachment name
|
||||
*/
|
||||
async handleUpdateName() {
|
||||
if (!this.attachment.name) {
|
||||
this.$notification['error']({
|
||||
message: '提示',
|
||||
description: '附件名称不能为空!'
|
||||
})
|
||||
return
|
||||
}
|
||||
try {
|
||||
await attachmentApi.update(this.attachment.id, this.attachment)
|
||||
} catch (error) {
|
||||
this.$log.error(error)
|
||||
} finally {
|
||||
this.editable = false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the copy link event
|
||||
* @param {String} link
|
||||
*/
|
||||
handleCopyLink(link) {
|
||||
this.$copyText(link)
|
||||
.then(message => {
|
||||
this.$log.debug('copy', message)
|
||||
this.$message.success('复制成功!')
|
||||
})
|
||||
.catch(err => {
|
||||
this.$log.debug('copy.err', err)
|
||||
this.$message.error('复制失败!')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -47,12 +47,12 @@
|
|||
></a-pagination>
|
||||
</div>
|
||||
|
||||
<AttachmentDetailDrawer
|
||||
<!-- <AttachmentDetailDrawer
|
||||
v-model="detailVisible"
|
||||
v-if="selectedAttachment"
|
||||
:attachment="selectedAttachment"
|
||||
@delete="handleListAttachments"
|
||||
/>
|
||||
/> -->
|
||||
<a-divider class="divider-transparent" />
|
||||
<div class="bottom-control">
|
||||
<a-button @click="uploadVisible = true" type="primary">上传附件</a-button>
|
||||
|
@ -67,14 +67,14 @@
|
|||
|
||||
<script>
|
||||
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
||||
import AttachmentDetailDrawer from './AttachmentDetailDrawer'
|
||||
// import AttachmentDetailDrawer from './AttachmentDetailDrawer'
|
||||
import attachmentApi from '@/api/attachment'
|
||||
|
||||
export default {
|
||||
name: 'AttachmentDrawer',
|
||||
mixins: [mixin, mixinDevice],
|
||||
components: {
|
||||
AttachmentDetailDrawer
|
||||
// AttachmentDetailDrawer
|
||||
},
|
||||
model: {
|
||||
prop: 'visible',
|
||||
|
|
|
@ -574,9 +574,7 @@ export default {
|
|||
this.pagination.total = response.data.data.total
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
this.postsLoading = false
|
||||
}, 200)
|
||||
this.postsLoading = false
|
||||
})
|
||||
},
|
||||
handleListCategories() {
|
||||
|
|
Loading…
Reference in New Issue