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 state
pull/376/head^2
Ryan Wang 2021-11-16 22:22:41 +08:00 committed by GitHub
parent d3eb288327
commit 2939cbbfbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 499 additions and 528 deletions

View File

@ -31,7 +31,6 @@
"filepond": "^4.30.3", "filepond": "^4.30.3",
"filepond-plugin-file-validate-type": "^1.2.6", "filepond-plugin-file-validate-type": "^1.2.6",
"filepond-plugin-image-preview": "^4.6.10", "filepond-plugin-image-preview": "^4.6.10",
"flv.js": "^1.6.2",
"halo-editor": "^2.8.3", "halo-editor": "^2.8.3",
"marked": "^2.1.3", "marked": "^2.1.3",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@ -41,7 +40,6 @@
"vue-clipboard2": "^0.3.3", "vue-clipboard2": "^0.3.3",
"vue-contextmenujs": "^1.3.13", "vue-contextmenujs": "^1.3.13",
"vue-count-to": "^1.0.13", "vue-count-to": "^1.0.13",
"vue-dplayer": "0.0.10",
"vue-filepond": "^6.0.3", "vue-filepond": "^6.0.3",
"vue-ls": "^3.2.2", "vue-ls": "^3.2.2",
"vue-router": "^3.5.3", "vue-router": "^3.5.3",

View File

@ -26,7 +26,6 @@ specifiers:
filepond: ^4.30.3 filepond: ^4.30.3
filepond-plugin-file-validate-type: ^1.2.6 filepond-plugin-file-validate-type: ^1.2.6
filepond-plugin-image-preview: ^4.6.10 filepond-plugin-image-preview: ^4.6.10
flv.js: ^1.6.2
halo-editor: ^2.8.3 halo-editor: ^2.8.3
husky: ^6.0.0 husky: ^6.0.0
less: ^3.13.1 less: ^3.13.1
@ -42,7 +41,6 @@ specifiers:
vue-clipboard2: ^0.3.3 vue-clipboard2: ^0.3.3
vue-contextmenujs: ^1.3.13 vue-contextmenujs: ^1.3.13
vue-count-to: ^1.0.13 vue-count-to: ^1.0.13
vue-dplayer: 0.0.10
vue-filepond: ^6.0.3 vue-filepond: ^6.0.3
vue-ls: ^3.2.2 vue-ls: ^3.2.2
vue-router: ^3.5.3 vue-router: ^3.5.3
@ -62,7 +60,6 @@ dependencies:
filepond: 4.30.3 filepond: 4.30.3
filepond-plugin-file-validate-type: 1.2.6_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 filepond-plugin-image-preview: 4.6.10_filepond@4.30.3
flv.js: 1.6.2
halo-editor: 2.8.3 halo-editor: 2.8.3
marked: 2.1.3 marked: 2.1.3
nprogress: 0.2.0 nprogress: 0.2.0
@ -72,7 +69,6 @@ dependencies:
vue-clipboard2: 0.3.3 vue-clipboard2: 0.3.3
vue-contextmenujs: 1.3.13 vue-contextmenujs: 1.3.13
vue-count-to: 1.0.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-filepond: 6.0.3_filepond@4.30.3+vue@2.6.14
vue-ls: 3.2.2 vue-ls: 3.2.2
vue-router: 3.5.3 vue-router: 3.5.3
@ -2820,13 +2816,6 @@ packages:
resolution: {integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==} resolution: {integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==}
dev: true 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: /axios/0.21.4:
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
dependencies: dependencies:
@ -3142,10 +3131,6 @@ packages:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true dev: true
/balloon-css/1.2.0:
resolution: {integrity: sha512-urXwkHgwp6GsXVF+it01485Z2Cj4pnW02ICnM0TemOlkKmCNnDLmyy+ZZiRXBpwldUXO+aRNr7Hdia4CBvXJ5A==}
dev: false
/base/0.11.2: /base/0.11.2:
resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -4383,12 +4368,6 @@ packages:
ms: 2.0.0 ms: 2.0.0
dev: true dev: true
/debug/3.1.0:
resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==}
dependencies:
ms: 2.0.0
dev: false
/debug/3.2.7: /debug/3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
dependencies: dependencies:
@ -4714,14 +4693,6 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dev: true 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: /duplexer/0.1.2:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
dev: true dev: true
@ -4893,10 +4864,6 @@ packages:
is-symbol: 1.0.4 is-symbol: 1.0.4
dev: true dev: true
/es6-promise/4.2.8:
resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
dev: false
/escalade/3.1.1: /escalade/3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -5537,13 +5504,6 @@ packages:
readable-stream: 2.3.7 readable-stream: 2.3.7
dev: true 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: /follow-redirects/1.14.4:
resolution: {integrity: sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==} resolution: {integrity: sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==}
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
@ -5553,13 +5513,6 @@ packages:
debug: debug:
optional: true 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: /for-each/0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies: dependencies:
@ -8055,6 +8008,7 @@ packages:
/ms/2.0.0: /ms/2.0.0:
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
dev: true
/ms/2.1.1: /ms/2.1.1:
resolution: {integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==} resolution: {integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==}
@ -9318,10 +9272,6 @@ packages:
resolution: {integrity: sha1-mEcocL8igTL8vdhoEputEsPAKeM=} resolution: {integrity: sha1-mEcocL8igTL8vdhoEputEsPAKeM=}
dev: true dev: true
/promise-polyfill/8.1.3:
resolution: {integrity: sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==}
dev: false
/prompts/2.4.1: /prompts/2.4.1:
resolution: {integrity: sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==} resolution: {integrity: sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@ -11222,13 +11172,6 @@ packages:
resolution: {integrity: sha512-6R4OVBVNtQTlcbXu6SJ8ENR35M2/CdWt3Jmv57jOUM+1ojiFmjVGvZPH8DfHpMDSA+ITs+EW5V6qthADxeyYOQ==} resolution: {integrity: sha512-6R4OVBVNtQTlcbXu6SJ8ENR35M2/CdWt3Jmv57jOUM+1ojiFmjVGvZPH8DfHpMDSA+ITs+EW5V6qthADxeyYOQ==}
dev: false 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: /vue-eslint-parser/7.11.0_eslint@6.8.0:
resolution: {integrity: sha512-qh3VhDLeh773wjgNTl7ss0VejY9bMMa0GoDG2fQVyDzRFdiU3L7fw74tWZDHNQXdZqxO3EveQroa9ct39D2nqg==} resolution: {integrity: sha512-qh3VhDLeh773wjgNTl7ss0VejY9bMMa0GoDG2fQVyDzRFdiU3L7fw74tWZDHNQXdZqxO3EveQroa9ct39D2nqg==}
engines: {node: '>=8.10'} engines: {node: '>=8.10'}
@ -11257,10 +11200,6 @@ packages:
vue: 2.6.14 vue: 2.6.14
dev: false dev: false
/vue-github-badge/1.0.1:
resolution: {integrity: sha1-3/fOBzIOZKIY7fEGsVpDF27AYQY=}
dev: false
/vue-hot-reload-api/2.3.4: /vue-hot-reload-api/2.3.4:
resolution: {integrity: sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==} resolution: {integrity: sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==}
dev: true dev: true
@ -11607,10 +11546,6 @@ packages:
engines: {node: '>=0.8.0'} engines: {node: '>=0.8.0'}
dev: true dev: true
/webworkify-webpack/2.1.5:
resolution: {integrity: sha512-2akF8FIyUvbiBBdD+RoHpoTbHMQF2HwjcxfDvgztAX5YwbZNyrtfUMgvfgFVsgDhDPVTlkbb5vyasqDHfIDPQw==}
dev: false
/whatwg-encoding/1.0.5: /whatwg-encoding/1.0.5:
resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
dependencies: dependencies:

View File

@ -1,41 +1,41 @@
<template> <template>
<page-view> <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-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"> <div class="table-page-search-wrapper">
<a-form layout="inline"> <a-form layout="inline">
<a-row :gutter="48"> <a-row :gutter="48">
<a-col :md="6" :sm="24"> <a-col :md="6" :sm="24">
<a-form-item label="关键词:"> <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-form-item>
</a-col> </a-col>
<a-col :md="6" :sm="24"> <a-col :md="6" :sm="24">
<a-form-item label="存储位置:"> <a-form-item label="存储位置:">
<a-select <a-select
v-model="queryParam.attachmentType" v-model="list.params.attachmentType"
@change="handleQuery()" :loading="types.loading"
:loading="typesLoading"
allowClear allowClear
@change="handleQuery()"
> >
<a-select-option v-for="item in types" :key="item" :value="item">{{ <a-select-option v-for="item in types.data" :key="item" :value="item">
attachmentType[item].text {{ item | typeText }}
}}</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :md="6" :sm="24"> <a-col :md="6" :sm="24">
<a-form-item label="文件类型:"> <a-form-item label="文件类型:">
<a-select <a-select
v-model="queryParam.mediaType" v-model="list.params.mediaType"
@change="handleQuery()" :loading="mediaTypes.loading"
:loading="mediaTypesLoading"
allowClear allowClear
@change="handleQuery()"
> >
<a-select-option v-for="(item, index) in mediaTypes" :key="index" :value="item">{{ <a-select-option v-for="(item, index) in mediaTypes.data" :key="index" :value="item"
item >{{ item }}
}}</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
@ -51,19 +51,19 @@
</a-form> </a-form>
</div> </div>
<div class="mb-0 table-operator"> <div class="mb-0 table-operator">
<a-button type="primary" icon="cloud-upload" @click="() => (uploadVisible = true)">上传</a-button> <a-button icon="cloud-upload" type="primary" @click="upload.visible = true">上传</a-button>
<a-button icon="select" v-show="!supportMultipleSelection" @click="handleMultipleSelection"> <a-button v-show="!supportMultipleSelection" icon="select" @click="handleMultipleSelection">
批量操作 批量操作
</a-button> </a-button>
<a-button <a-button
type="danger"
icon="delete"
v-show="supportMultipleSelection" v-show="supportMultipleSelection"
icon="delete"
type="danger"
@click="handleDeleteAttachmentInBatch" @click="handleDeleteAttachmentInBatch"
> >
删除 删除
</a-button> </a-button>
<a-button icon="close" v-show="supportMultipleSelection" @click="handleCancelMultipleSelection"> <a-button v-show="supportMultipleSelection" icon="close" @click="handleCancelMultipleSelection">
取消 取消
</a-button> </a-button>
</div> </div>
@ -71,36 +71,36 @@
</a-col> </a-col>
<a-col :span="24"> <a-col :span="24">
<a-list <a-list
class="attachments-group" :dataSource="list.data"
:grid="{ gutter: 12, xs: 2, sm: 2, md: 4, lg: 6, xl: 6, xxl: 6 }" :grid="{ gutter: 12, xs: 2, sm: 2, md: 4, lg: 6, xl: 6, xxl: 6 }"
:dataSource="formattedDatas" :loading="list.loading"
:loading="listLoading" 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 <a-card
:bodyStyle="{ padding: 0 }" :bodyStyle="{ padding: 0 }"
hoverable hoverable
@click="handleShowDetailDrawer(item)" @click="handleOpenDetail(item)"
@contextmenu.prevent="handleContextMenu($event, item)" @contextmenu.prevent="handleContextMenu($event, item)"
> >
<div class="attach-thumb attachments-group-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 <span
v-else v-else
class="attachments-group-item-img"
:style="`background-image:url(${item.thumbPath})`" :style="`background-image:url(${item.thumbPath})`"
class="attachments-group-item-img"
loading="lazy" loading="lazy"
/> />
</div> </div>
<a-card-meta class="p-3"> <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-card-meta>
<a-checkbox <a-checkbox
class="select-attachment-checkbox"
:style="getCheckStatus(item.id) ? selectedAttachmentStyle : ''"
:checked="getCheckStatus(item.id)"
@click="handleAttachmentSelectionChanged($event, item)"
v-show="supportMultipleSelection" v-show="supportMultipleSelection"
:checked="getCheckStatus(item.id)"
:style="getCheckStatus(item.id) ? selectedAttachmentStyle : ''"
class="select-attachment-checkbox"
@click="handleAttachmentSelectionChanged($event, item)"
></a-checkbox> ></a-checkbox>
</a-card> </a-card>
</a-list-item> </a-list-item>
@ -109,87 +109,120 @@
</a-row> </a-row>
<div class="page-wrapper"> <div class="page-wrapper">
<a-pagination <a-pagination
class="pagination"
:current="pagination.page" :current="pagination.page"
:total="pagination.total"
:defaultPageSize="pagination.size" :defaultPageSize="pagination.size"
:pageSizeOptions="['18', '36', '54', '72', '90', '108']" :pageSizeOptions="['18', '36', '54', '72', '90', '108']"
showSizeChanger :total="pagination.total"
@change="handlePaginationChange" class="pagination"
@showSizeChange="handlePaginationChange"
showLessItems showLessItems
showSizeChanger
@change="handlePageChange"
@showSizeChange="handlePageSizeChange"
/> />
</div> </div>
<a-modal title="上传附件" v-model="uploadVisible" :footer="null" :afterClose="onUploadClose" destroyOnClose> <a-modal v-model="upload.visible" :afterClose="onUploadClose" :footer="null" destroyOnClose title="上传附件">
<FilePondUpload ref="upload" :uploadHandler="uploadHandler"></FilePondUpload> <FilePondUpload ref="upload" :uploadHandler="upload.handler"></FilePondUpload>
</a-modal> </a-modal>
<AttachmentDetailDrawer <AttachmentDetailModal
v-model="drawerVisible"
v-if="selectAttachment"
:attachment="selectAttachment"
:addToPhoto="true" :addToPhoto="true"
:attachment="list.selected"
:visible.sync="detailVisible"
@delete="handleListAttachments()" @delete="handleListAttachments()"
/> >
<template #extraFooter>
<a-button :disabled="selectPreviousButtonDisabled" @click="handleSelectPrevious"></a-button>
<a-button :disabled="selectNextButtonDisabled" @click="handleSelectNext"></a-button>
</template>
</AttachmentDetailModal>
</page-view> </page-view>
</template> </template>
<script> <script>
import { mixin, mixinDevice } from '@/mixins/mixin.js' import { mixin, mixinDevice } from '@/mixins/mixin.js'
import { PageView } from '@/layouts' import { PageView } from '@/layouts'
import AttachmentDetailDrawer from './components/AttachmentDetailDrawer' import AttachmentDetailModal from './components/AttachmentDetailModal.vue'
import attachmentApi from '@/api/attachment' import attachmentApi from '@/api/attachment'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
export default { export default {
components: { components: {
PageView, PageView,
AttachmentDetailDrawer AttachmentDetailModal
}, },
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
filters: {
typeText(type) {
return attachmentApi.type[type].text
}
},
data() { data() {
return { return {
attachmentType: attachmentApi.type, list: {
listLoading: true, data: [],
uploadVisible: false, 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, supportMultipleSelection: false,
selectedAttachmentCheckbox: {}, selectedAttachmentCheckbox: {},
batchSelectedAttachments: [], 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
} }
}, },
computed: { computed: {
formattedDatas() {
return this.attachments.map(attachment => {
attachment.typeProperty = this.attachmentType[attachment.type]
return attachment
})
},
selectedAttachmentStyle() { selectedAttachmentStyle() {
return { return {
border: `2px solid ${this.color()}` 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() { created() {
@ -197,71 +230,79 @@ export default {
this.handleListMediaTypes() this.handleListMediaTypes()
this.handleListTypes() this.handleListTypes()
}, },
destroyed: function() {
if (this.drawerVisible) {
this.drawerVisible = false
}
},
beforeRouteLeave(to, from, next) {
if (this.drawerVisible) {
this.drawerVisible = false
}
next()
},
methods: { methods: {
...mapGetters(['color']), ...mapGetters(['color']),
handleListAttachments() {
this.listLoading = true /**
this.queryParam.page = this.pagination.page - 1 * List attachments
this.queryParam.size = this.pagination.size */
this.queryParam.sort = this.pagination.sort async handleListAttachments() {
attachmentApi try {
.query(this.queryParam) this.list.loading = true
.then(response => {
this.attachments = response.data.data.content const response = await attachmentApi.query(this.list.params)
this.pagination.total = response.data.data.total
}) this.list.data = response.data.data.content
.finally(() => { this.list.total = response.data.data.total
setTimeout(() => { this.list.hasNext = response.data.data.hasNext
this.listLoading = false this.list.hasPrevious = response.data.data.hasPrevious
}, 200) } catch (error) {
}) this.$log.error(error)
} finally {
this.list.loading = false
}
}, },
handleListMediaTypes() {
this.mediaTypesLoading = true /**
attachmentApi * List attachment media types
.getMediaTypes() */
.then(response => { async handleListMediaTypes() {
this.mediaTypes = response.data.data try {
}) this.mediaTypes.loading = true
.finally(() => {
setTimeout(() => { const response = await attachmentApi.getMediaTypes()
this.mediaTypesLoading = false
}, 200) this.mediaTypes.data = response.data.data
}) } catch (error) {
this.$log.error(error)
} finally {
this.mediaTypes.loading = false
}
}, },
handleListTypes() {
this.typesLoading = true /**
attachmentApi * List attachment upload types
.getTypes() */
.then(response => { async handleListTypes() {
this.types = response.data.data try {
}) this.types.loading = true
.finally(() => {
setTimeout(() => { const response = await attachmentApi.getTypes()
this.typesLoading = false
}, 200) 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) { handleContextMenu(event, item) {
this.$contextmenu({ this.$contextmenu({
items: [ items: [
{ {
label: `${this.handleJudgeMediaType(item) ? '复制图片链接' : '复制文件链接'}`, label: `复制${this.isImage(item) ? '图片' : '文件'}链接`,
onClick: () => { onClick: () => {
const text = `${encodeURI(item.path)}` const text = `${encodeURI(item.path)}`
this.$copyText(text) this.$copyText(text)
@ -277,7 +318,7 @@ export default {
divided: true divided: true
}, },
{ {
disabled: !this.handleJudgeMediaType(item), disabled: !this.isImage(item),
label: '复制 Markdown 格式链接', label: '复制 Markdown 格式链接',
onClick: () => { onClick: () => {
const text = `![${item.name}](${encodeURI(item.path)})` const text = `![${item.name}](${encodeURI(item.path)})`
@ -290,6 +331,22 @@ export default {
this.$log.debug('copy.err', err) this.$log.debug('copy.err', err)
this.$message.error('复制失败!') 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 return false
}, },
handlePaginationChange(page, size) {
this.$log.debug(`Current: ${page}, PageSize: ${size}`) /**
this.pagination.page = page * Handle page change
this.pagination.size = size */
handlePageChange(page = 1) {
this.list.params.page = page - 1
this.handleListAttachments() 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() { handleResetParam() {
this.queryParam.keyword = null this.list.params.keyword = null
this.queryParam.mediaType = null this.list.params.mediaType = null
this.queryParam.attachmentType = null this.list.params.attachmentType = null
this.handlePaginationChange(1, this.pagination.size) this.handlePageChange()
this.handleListMediaTypes() this.handleListMediaTypes()
this.handleListTypes() this.handleListTypes()
}, },
/**
* Search attachments
*/
handleQuery() { handleQuery() {
this.handlePaginationChange(1, this.pagination.size) this.handlePageChange()
}, },
onUploadClose() { onUploadClose() {
this.$refs.upload.handleClearFileList() this.$refs.upload.handleClearFileList()
this.handlePaginationChange(1, this.pagination.size) this.handlePageChange()
this.handleListMediaTypes() this.handleListMediaTypes()
this.handleListTypes() this.handleListTypes()
}, },
handleJudgeMediaType(attachment) {
const mediaType = attachment.mediaType
//
if (mediaType) {
const prefix = mediaType.split('/')[0]
return prefix === 'image'
}
// false
return false
},
getCheckStatus(key) { getCheckStatus(key) {
return this.selectedAttachmentCheckbox[key] || false return this.selectedAttachmentCheckbox[key] || false
}, },
handleMultipleSelection() { handleMultipleSelection() {
this.supportMultipleSelection = true this.supportMultipleSelection = true
// //
this.drawerVisible = false this.detailVisible = false
this.attachments.forEach(item => { this.list.data.forEach(item => {
this.$set(this.selectedAttachmentCheckbox, item.id, false) this.$set(this.selectedAttachmentCheckbox, item.id, false)
}) })
}, },
handleCancelMultipleSelection() { handleCancelMultipleSelection() {
this.supportMultipleSelection = false this.supportMultipleSelection = false
this.drawerVisible = false this.detailVisible = false
this.batchSelectedAttachments = [] this.batchSelectedAttachments = []
for (const key in this.selectedCheckbox) { for (const key in this.selectedCheckbox) {
this.$set(this.selectedAttachmentCheckbox, key, false) this.$set(this.selectedAttachmentCheckbox, key, false)
@ -363,6 +429,10 @@ export default {
this.batchSelectedAttachments.splice(index, 1) this.batchSelectedAttachments.splice(index, 1)
} }
}, },
/**
* Deletes selected attachments
*/
handleDeleteAttachmentInBatch() { handleDeleteAttachmentInBatch() {
const that = this const that = this
if (this.batchSelectedAttachments.length <= 0) { if (this.batchSelectedAttachments.length <= 0) {
@ -385,6 +455,40 @@ export default {
}, },
onCancel() {} 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]
}
} }
} }
} }

View File

@ -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>

View File

@ -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>

View File

@ -47,12 +47,12 @@
></a-pagination> ></a-pagination>
</div> </div>
<AttachmentDetailDrawer <!-- <AttachmentDetailDrawer
v-model="detailVisible" v-model="detailVisible"
v-if="selectedAttachment" v-if="selectedAttachment"
:attachment="selectedAttachment" :attachment="selectedAttachment"
@delete="handleListAttachments" @delete="handleListAttachments"
/> /> -->
<a-divider class="divider-transparent" /> <a-divider class="divider-transparent" />
<div class="bottom-control"> <div class="bottom-control">
<a-button @click="uploadVisible = true" type="primary">上传附件</a-button> <a-button @click="uploadVisible = true" type="primary">上传附件</a-button>
@ -67,14 +67,14 @@
<script> <script>
import { mixin, mixinDevice } from '@/mixins/mixin.js' import { mixin, mixinDevice } from '@/mixins/mixin.js'
import AttachmentDetailDrawer from './AttachmentDetailDrawer' // import AttachmentDetailDrawer from './AttachmentDetailDrawer'
import attachmentApi from '@/api/attachment' import attachmentApi from '@/api/attachment'
export default { export default {
name: 'AttachmentDrawer', name: 'AttachmentDrawer',
mixins: [mixin, mixinDevice], mixins: [mixin, mixinDevice],
components: { components: {
AttachmentDetailDrawer // AttachmentDetailDrawer
}, },
model: { model: {
prop: 'visible', prop: 'visible',

View File

@ -574,9 +574,7 @@ export default {
this.pagination.total = response.data.data.total this.pagination.total = response.data.data.total
}) })
.finally(() => { .finally(() => {
setTimeout(() => { this.postsLoading = false
this.postsLoading = false
}, 200)
}) })
}, },
handleListCategories() { handleListCategories() {