✨ improve image sharing function
This commit is contained in:
10
components.d.ts
vendored
10
components.d.ts
vendored
@@ -2,6 +2,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
// Generated by unplugin-vue-components
|
// Generated by unplugin-vue-components
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
// biome-ignore lint: disable
|
||||||
export {}
|
export {}
|
||||||
|
|
||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
@@ -39,6 +40,7 @@ declare module 'vue' {
|
|||||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||||
AnimatedNature: typeof import('./src/components/AnimatedNature/AnimatedNature.vue')['default']
|
AnimatedNature: typeof import('./src/components/AnimatedNature/AnimatedNature.vue')['default']
|
||||||
APagination: typeof import('ant-design-vue/es')['Pagination']
|
APagination: typeof import('ant-design-vue/es')['Pagination']
|
||||||
|
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||||
APopover: typeof import('ant-design-vue/es')['Popover']
|
APopover: typeof import('ant-design-vue/es')['Popover']
|
||||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||||
AQrcode: typeof import('ant-design-vue/es')['QRCode']
|
AQrcode: typeof import('ant-design-vue/es')['QRCode']
|
||||||
@@ -64,6 +66,7 @@ declare module 'vue' {
|
|||||||
BoxDog: typeof import('./src/components/BoxDog/BoxDog.vue')['default']
|
BoxDog: typeof import('./src/components/BoxDog/BoxDog.vue')['default']
|
||||||
Card3D: typeof import('./src/components/Card3D/Card3D.vue')['default']
|
Card3D: typeof import('./src/components/Card3D/Card3D.vue')['default']
|
||||||
CheckCard: typeof import('./src/components/CheckCard/CheckCard.vue')['default']
|
CheckCard: typeof import('./src/components/CheckCard/CheckCard.vue')['default']
|
||||||
|
CheckCircleOutlined: typeof import('@ant-design/icons-vue')['CheckCircleOutlined']
|
||||||
CloseCircleOutlined: typeof import('@ant-design/icons-vue')['CloseCircleOutlined']
|
CloseCircleOutlined: typeof import('@ant-design/icons-vue')['CloseCircleOutlined']
|
||||||
CloseOutlined: typeof import('@ant-design/icons-vue')['CloseOutlined']
|
CloseOutlined: typeof import('@ant-design/icons-vue')['CloseOutlined']
|
||||||
Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default']
|
Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default']
|
||||||
@@ -71,6 +74,7 @@ declare module 'vue' {
|
|||||||
CommentList: typeof import('./src/components/CommentReply/src/CommentList/CommentList.vue')['default']
|
CommentList: typeof import('./src/components/CommentReply/src/CommentList/CommentList.vue')['default']
|
||||||
CommentReply: typeof import('./src/components/CommentReply/index.vue')['default']
|
CommentReply: typeof import('./src/components/CommentReply/index.vue')['default']
|
||||||
CompareImage: typeof import('./src/views/Upscale/CompareImage.vue')['default']
|
CompareImage: typeof import('./src/views/Upscale/CompareImage.vue')['default']
|
||||||
|
CopyOutlined: typeof import('@ant-design/icons-vue')['CopyOutlined']
|
||||||
DeleteOutlined: typeof import('@ant-design/icons-vue')['DeleteOutlined']
|
DeleteOutlined: typeof import('@ant-design/icons-vue')['DeleteOutlined']
|
||||||
DownloadOutlined: typeof import('@ant-design/icons-vue')['DownloadOutlined']
|
DownloadOutlined: typeof import('@ant-design/icons-vue')['DownloadOutlined']
|
||||||
DownOutlined: typeof import('@ant-design/icons-vue')['DownOutlined']
|
DownOutlined: typeof import('@ant-design/icons-vue')['DownOutlined']
|
||||||
@@ -87,6 +91,7 @@ declare module 'vue' {
|
|||||||
InboxOutlined: typeof import('@ant-design/icons-vue')['InboxOutlined']
|
InboxOutlined: typeof import('@ant-design/icons-vue')['InboxOutlined']
|
||||||
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
|
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
|
||||||
LeftOutlined: typeof import('@ant-design/icons-vue')['LeftOutlined']
|
LeftOutlined: typeof import('@ant-design/icons-vue')['LeftOutlined']
|
||||||
|
LinkOutlined: typeof import('@ant-design/icons-vue')['LinkOutlined']
|
||||||
LoadingGraphic: typeof import('./src/components/LoadingGraphic/LoadingGraphic.vue')['default']
|
LoadingGraphic: typeof import('./src/components/LoadingGraphic/LoadingGraphic.vue')['default']
|
||||||
LocationAlbum: typeof import('./src/views/Album/LocationAlbum/LocationAlbum.vue')['default']
|
LocationAlbum: typeof import('./src/views/Album/LocationAlbum/LocationAlbum.vue')['default']
|
||||||
LocationAlbumDetail: typeof import('./src/views/Album/LocationAlbum/LocationAlbumDetail.vue')['default']
|
LocationAlbumDetail: typeof import('./src/views/Album/LocationAlbum/LocationAlbumDetail.vue')['default']
|
||||||
@@ -105,7 +110,7 @@ declare module 'vue' {
|
|||||||
Phoalbum: typeof import('./src/views/Album/Phoalbum/Phoalbum.vue')['default']
|
Phoalbum: typeof import('./src/views/Album/Phoalbum/Phoalbum.vue')['default']
|
||||||
PhoalbumDetail: typeof import('./src/views/Album/Phoalbum/PhoalbumDetail.vue')['default']
|
PhoalbumDetail: typeof import('./src/views/Album/Phoalbum/PhoalbumDetail.vue')['default']
|
||||||
PhoalbumList: typeof import('./src/views/Album/Phoalbum/PhoalbumList.vue')['default']
|
PhoalbumList: typeof import('./src/views/Album/Phoalbum/PhoalbumList.vue')['default']
|
||||||
PhoneUpload: typeof import('./src/views/PhoneUpload/PhoneUpload.vue')['default']
|
PhoneUpload: typeof import('./src/views/Phone/UpscalePhoneUpload/PhoneUpload.vue')['default']
|
||||||
PhotoStack: typeof import('./src/components/PhotoStack/PhotoStack.vue')['default']
|
PhotoStack: typeof import('./src/components/PhotoStack/PhotoStack.vue')['default']
|
||||||
PlusOutlined: typeof import('@ant-design/icons-vue')['PlusOutlined']
|
PlusOutlined: typeof import('@ant-design/icons-vue')['PlusOutlined']
|
||||||
PlusSquareOutlined: typeof import('@ant-design/icons-vue')['PlusSquareOutlined']
|
PlusSquareOutlined: typeof import('@ant-design/icons-vue')['PlusSquareOutlined']
|
||||||
@@ -123,6 +128,8 @@ declare module 'vue' {
|
|||||||
SearchOutlined: typeof import('@ant-design/icons-vue')['SearchOutlined']
|
SearchOutlined: typeof import('@ant-design/icons-vue')['SearchOutlined']
|
||||||
SendOutlined: typeof import('@ant-design/icons-vue')['SendOutlined']
|
SendOutlined: typeof import('@ant-design/icons-vue')['SendOutlined']
|
||||||
ShareAltOutlined: typeof import('@ant-design/icons-vue')['ShareAltOutlined']
|
ShareAltOutlined: typeof import('@ant-design/icons-vue')['ShareAltOutlined']
|
||||||
|
SharePhoneUpload: typeof import('./src/views/Phone/SharePhoneUpload/SharePhoneUpload.vue')['default']
|
||||||
|
ShareUpload: typeof import('./src/views/ImageShare/ShareUpload.vue')['default']
|
||||||
Spin: typeof import('./src/components/MyUI/Spin/Spin.vue')['default']
|
Spin: typeof import('./src/components/MyUI/Spin/Spin.vue')['default']
|
||||||
StarButton: typeof import('./src/components/StarButton/StarButton.vue')['default']
|
StarButton: typeof import('./src/components/StarButton/StarButton.vue')['default']
|
||||||
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
|
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
|
||||||
@@ -132,6 +139,7 @@ declare module 'vue' {
|
|||||||
Tooltip: typeof import('./src/components/MyUI/Tooltip/Tooltip.vue')['default']
|
Tooltip: typeof import('./src/components/MyUI/Tooltip/Tooltip.vue')['default']
|
||||||
UploadImage: typeof import('./src/views/Upscale/UploadImage.vue')['default']
|
UploadImage: typeof import('./src/views/Upscale/UploadImage.vue')['default']
|
||||||
Upscale: typeof import('./src/views/Upscale/index.vue')['default']
|
Upscale: typeof import('./src/views/Upscale/index.vue')['default']
|
||||||
|
UpscalePhoneUpload: typeof import('./src/views/Phone/UpscalePhoneUpload/UpscalePhoneUpload.vue')['default']
|
||||||
UserInfoCard: typeof import('./src/components/CommentReply/src/UserInfoCard/UserInfoCard.vue')['default']
|
UserInfoCard: typeof import('./src/components/CommentReply/src/UserInfoCard/UserInfoCard.vue')['default']
|
||||||
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
|
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
|
||||||
VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default']
|
VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default']
|
||||||
|
@@ -52,6 +52,7 @@
|
|||||||
"json-stringify-safe": "^5.0.1",
|
"json-stringify-safe": "^5.0.1",
|
||||||
"less": "^4.2.2",
|
"less": "^4.2.2",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
|
"moment": "^2.30.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"nsfwjs": "^4.2.1",
|
"nsfwjs": "^4.2.1",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
@@ -79,15 +80,15 @@
|
|||||||
"sass": "^1.85.0",
|
"sass": "^1.85.0",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.24.1",
|
"typescript-eslint": "^8.24.1",
|
||||||
"unplugin-vue-components": "^28.2.0",
|
"unplugin-vue-components": "^28.4.0",
|
||||||
"vite": "^6.1.0",
|
"vite": "^6.1.1",
|
||||||
"vite-plugin-bundle-obfuscator": "1.4.1",
|
"vite-plugin-bundle-obfuscator": "1.4.1",
|
||||||
"vite-plugin-chunk-split": "^0.5.0",
|
"vite-plugin-chunk-split": "^0.5.0",
|
||||||
"vue-tsc": "2.2.2"
|
"vue-tsc": "2.2.2"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"vite-plugin-chunk-split": {
|
"vite-plugin-chunk-split": {
|
||||||
"vite": "^6.1.0"
|
"vite": "^6.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
43
src/api/share/index.ts
Normal file
43
src/api/share/index.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import {service} from "@/utils/alova/service.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传分享图片
|
||||||
|
* @param formData
|
||||||
|
*/
|
||||||
|
export const shareImageUploadApi = (formData) => {
|
||||||
|
return service.Post('/api/auth/share/upload', {...formData}, {
|
||||||
|
meta: {
|
||||||
|
ignoreToken: false,
|
||||||
|
signature: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 查询分享图片列表
|
||||||
|
* @param share_code
|
||||||
|
* @param access_password
|
||||||
|
*/
|
||||||
|
export const queryShareImageApi = (share_code: string, access_password: string) => {
|
||||||
|
return service.Post('/api/auth/share/image/list', {
|
||||||
|
share_code: share_code,
|
||||||
|
access_password: access_password,
|
||||||
|
}, {
|
||||||
|
meta: {
|
||||||
|
ignoreToken: false,
|
||||||
|
signature: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 查询分享记录列表
|
||||||
|
*/
|
||||||
|
export const queryShareRecordListApi = (dataRequest: string[]) => {
|
||||||
|
return service.Post('/api/auth/share/record/list', {
|
||||||
|
date_range: dataRequest,
|
||||||
|
}, {
|
||||||
|
meta: {
|
||||||
|
ignoreToken: false,
|
||||||
|
signature: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
@@ -110,7 +110,7 @@ export const createAlbumApi = (name: string) => {
|
|||||||
* @param type
|
* @param type
|
||||||
* @param sort
|
* @param sort
|
||||||
*/
|
*/
|
||||||
export const albumListApi = (type: string, sort: boolean) => {
|
export const albumListApi = (type: number, sort: boolean) => {
|
||||||
return service.Post('/api/auth/storage/album/list', {
|
return service.Post('/api/auth/storage/album/list', {
|
||||||
type: type,
|
type: type,
|
||||||
sort: sort,
|
sort: sort,
|
||||||
@@ -344,3 +344,5 @@ export const getStorageConfigListApi = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: '/upscale/app',
|
path: '/upscale/app',
|
||||||
name: 'upscaleApp',
|
name: 'upscaleApp',
|
||||||
component: () => import('@/views/PhoneUpload/PhoneUpload.vue'),
|
component: () => import('@/views/Phone/UpscalePhoneUpload/UpscalePhoneUpload.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
title: '手机上传'
|
title: '手机上传'
|
||||||
|
@@ -62,7 +62,7 @@ const options = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function getImageList(id: number) {
|
async function getImageList(id: number) {
|
||||||
const res: any = await queryLocationDetailListApi(id, upload.storageSelected[0], upload.storageSelected[1]);
|
const res: any = await queryLocationDetailListApi(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||||
console.log(res);
|
console.log(res);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
albumList.value = res.data.records;
|
albumList.value = res.data.records;
|
||||||
|
@@ -44,7 +44,7 @@ async function getLocationAlbums(provider: string, bucket: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getLocationAlbums(upload.storageSelected[0], upload.storageSelected[1]);
|
getLocationAlbums(upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@@ -72,7 +72,7 @@ const options = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function getAlbumList(id: number) {
|
async function getAlbumList(id: number) {
|
||||||
const res: any = await getFaceSamplesDetailList(id, upload.storageSelected[0], upload.storageSelected[1]);
|
const res: any = await getFaceSamplesDetailList(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
albumList.value = res.data.records;
|
albumList.value = res.data.records;
|
||||||
}
|
}
|
||||||
|
@@ -98,7 +98,7 @@ const upload = useStore().upload;
|
|||||||
|
|
||||||
|
|
||||||
async function getAlbumList(id: number) {
|
async function getAlbumList(id: number) {
|
||||||
const res: any = await queryAlbumDetailListApi(id, upload.storageSelected[0], upload.storageSelected[1]);
|
const res: any = await queryAlbumDetailListApi(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
albumList.value = res.data.records;
|
albumList.value = res.data.records;
|
||||||
}
|
}
|
||||||
|
@@ -54,7 +54,7 @@
|
|||||||
<span
|
<span
|
||||||
style="color: #999; font-size: 12px;">已全部加载,共 {{ albumList ? albumList.length : 0 }} 个相册</span>
|
style="color: #999; font-size: 12px;">已全部加载,共 {{ albumList ? albumList.length : 0 }} 个相册</span>
|
||||||
</template>
|
</template>
|
||||||
<ATabPane key="0" tab="全部相册">
|
<ATabPane key="-1" tab="全部相册">
|
||||||
<ASpin tip="Loading..." :spinning="loading" size="large">
|
<ASpin tip="Loading..." :spinning="loading" size="large">
|
||||||
<div class="phoalbum-item-container">
|
<div class="phoalbum-item-container">
|
||||||
<div class="phoalbum-item"
|
<div class="phoalbum-item"
|
||||||
@@ -63,7 +63,8 @@
|
|||||||
@click.prevent="handleClick(album.id)"
|
@click.prevent="handleClick(album.id)"
|
||||||
@mouseover="isHovered = index"
|
@mouseover="isHovered = index"
|
||||||
@mouseleave="isHovered = null">
|
@mouseleave="isHovered = null">
|
||||||
<PhotoStack :src="album.cover_image" :default-src="default_cover"/>
|
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||||
|
:default-src="default_cover"/>
|
||||||
<div class="phoalbum-item-info">
|
<div class="phoalbum-item-info">
|
||||||
<span class="phoalbum-item-name">{{ album.name }}</span>
|
<span class="phoalbum-item-name">{{ album.name }}</span>
|
||||||
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
||||||
@@ -103,11 +104,155 @@
|
|||||||
</div>
|
</div>
|
||||||
</ASpin>
|
</ASpin>
|
||||||
</ATabPane>
|
</ATabPane>
|
||||||
<ATabPane key="1" tab="我的相册">
|
<ATabPane key="0" tab="我的相册">
|
||||||
|
<ASpin tip="Loading..." :spinning="loading" size="large">
|
||||||
|
<div class="phoalbum-item-container">
|
||||||
|
<div class="phoalbum-item"
|
||||||
|
v-for="(album, index) in albumList"
|
||||||
|
:key="album.id"
|
||||||
|
@click.prevent="handleClick(album.id)"
|
||||||
|
@mouseover="isHovered = index"
|
||||||
|
@mouseleave="isHovered = null">
|
||||||
|
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||||
|
:default-src="default_cover"/>
|
||||||
|
<div class="phoalbum-item-info">
|
||||||
|
<span class="phoalbum-item-name">{{ album.name }}</span>
|
||||||
|
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="phoalbum-item-operation"
|
||||||
|
:class="{ 'fade-in': isHovered === index, 'fade-out': isHovered !== index }">
|
||||||
|
<ADropdown trigger="click" @click.stop>
|
||||||
|
<AButton type="text" shape="circle" size="small" @click.prevent>
|
||||||
|
<template #icon>
|
||||||
|
<AAvatar shape="circle" size="small" :src="more"/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
<template #overlay>
|
||||||
|
<AMenu>
|
||||||
|
<APopover placement="left" trigger="click">
|
||||||
|
<AMenuItem key="1">重命名相册</AMenuItem>
|
||||||
|
<template #content>
|
||||||
|
<AInput :placeholder="album.name" class="phoalbum-create-input"
|
||||||
|
v-model:value="albumRenameValue">
|
||||||
|
<template #suffix>
|
||||||
|
<AButton type="text" @click.prevent size="small" style="color: #0e87cc"
|
||||||
|
@click="renameAlbum(album.id, albumRenameValue)">
|
||||||
|
确认
|
||||||
|
</AButton>
|
||||||
|
</template>
|
||||||
|
</AInput>
|
||||||
|
</template>
|
||||||
|
</APopover>
|
||||||
|
<AMenuItem key="2">分享相册</AMenuItem>
|
||||||
|
<AMenuItem key="3" @click.prevent="deleteAlbum(album.id)">删除相册</AMenuItem>
|
||||||
|
<AMenuItem key="4">下载相册</AMenuItem>
|
||||||
|
</AMenu>
|
||||||
|
</template>
|
||||||
|
</ADropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ASpin>
|
||||||
|
</ATabPane>
|
||||||
|
<ATabPane key="1" tab="我的分享">
|
||||||
|
<ASpin tip="Loading..." :spinning="loading" size="large">
|
||||||
|
<div class="phoalbum-item-container">
|
||||||
|
<div class="phoalbum-item"
|
||||||
|
v-for="(album, index) in albumList"
|
||||||
|
:key="album.id"
|
||||||
|
@click.prevent="handleClick(album.id)"
|
||||||
|
@mouseover="isHovered = index"
|
||||||
|
@mouseleave="isHovered = null">
|
||||||
|
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||||
|
:default-src="default_cover"/>
|
||||||
|
<div class="phoalbum-item-info">
|
||||||
|
<span class="phoalbum-item-name">{{ album.name }}</span>
|
||||||
|
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="phoalbum-item-operation"
|
||||||
|
:class="{ 'fade-in': isHovered === index, 'fade-out': isHovered !== index }">
|
||||||
|
<ADropdown trigger="click" @click.stop>
|
||||||
|
<AButton type="text" shape="circle" size="small" @click.prevent>
|
||||||
|
<template #icon>
|
||||||
|
<AAvatar shape="circle" size="small" :src="more"/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
<template #overlay>
|
||||||
|
<AMenu>
|
||||||
|
<APopover placement="left" trigger="click">
|
||||||
|
<AMenuItem key="1">重命名相册</AMenuItem>
|
||||||
|
<template #content>
|
||||||
|
<AInput :placeholder="album.name" class="phoalbum-create-input"
|
||||||
|
v-model:value="albumRenameValue">
|
||||||
|
<template #suffix>
|
||||||
|
<AButton type="text" @click.prevent size="small" style="color: #0e87cc"
|
||||||
|
@click="renameAlbum(album.id, albumRenameValue)">
|
||||||
|
确认
|
||||||
|
</AButton>
|
||||||
|
</template>
|
||||||
|
</AInput>
|
||||||
|
</template>
|
||||||
|
</APopover>
|
||||||
|
<AMenuItem key="2">分享相册</AMenuItem>
|
||||||
|
<AMenuItem key="3" @click.prevent="deleteAlbum(album.id)">删除相册</AMenuItem>
|
||||||
|
<AMenuItem key="4">下载相册</AMenuItem>
|
||||||
|
</AMenu>
|
||||||
|
</template>
|
||||||
|
</ADropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ASpin>
|
||||||
</ATabPane>
|
</ATabPane>
|
||||||
<ATabPane key="2" tab="收藏相册">
|
<ATabPane key="2" tab="收藏相册">
|
||||||
|
<ASpin tip="Loading..." :spinning="loading" size="large">
|
||||||
|
<div class="phoalbum-item-container">
|
||||||
|
<div class="phoalbum-item"
|
||||||
|
v-for="(album, index) in albumList"
|
||||||
|
:key="album.id"
|
||||||
|
@click.prevent="handleClick(album.id)"
|
||||||
|
@mouseover="isHovered = index"
|
||||||
|
@mouseleave="isHovered = null">
|
||||||
|
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||||
|
:default-src="default_cover"/>
|
||||||
|
<div class="phoalbum-item-info">
|
||||||
|
<span class="phoalbum-item-name">{{ album.name }}</span>
|
||||||
|
<span class="phoalbum-item-date">{{ album.created_at }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="phoalbum-item-operation"
|
||||||
|
:class="{ 'fade-in': isHovered === index, 'fade-out': isHovered !== index }">
|
||||||
|
<ADropdown trigger="click" @click.stop>
|
||||||
|
<AButton type="text" shape="circle" size="small" @click.prevent>
|
||||||
|
<template #icon>
|
||||||
|
<AAvatar shape="circle" size="small" :src="more"/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
<template #overlay>
|
||||||
|
<AMenu>
|
||||||
|
<APopover placement="left" trigger="click">
|
||||||
|
<AMenuItem key="1">重命名相册</AMenuItem>
|
||||||
|
<template #content>
|
||||||
|
<AInput :placeholder="album.name" class="phoalbum-create-input"
|
||||||
|
v-model:value="albumRenameValue">
|
||||||
|
<template #suffix>
|
||||||
|
<AButton type="text" @click.prevent size="small" style="color: #0e87cc"
|
||||||
|
@click="renameAlbum(album.id, albumRenameValue)">
|
||||||
|
确认
|
||||||
|
</AButton>
|
||||||
|
</template>
|
||||||
|
</AInput>
|
||||||
|
</template>
|
||||||
|
</APopover>
|
||||||
|
<AMenuItem key="2">分享相册</AMenuItem>
|
||||||
|
<AMenuItem key="3" @click.prevent="deleteAlbum(album.id)">删除相册</AMenuItem>
|
||||||
|
<AMenuItem key="4">下载相册</AMenuItem>
|
||||||
|
</AMenu>
|
||||||
|
</template>
|
||||||
|
</ADropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ASpin>
|
||||||
</ATabPane>
|
</ATabPane>
|
||||||
</ATabs>
|
</ATabs>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,7 +286,7 @@ async function createAlbumSubmit() {
|
|||||||
const res: any = await createAlbumApi(albumNameValue.value);
|
const res: any = await createAlbumApi(albumNameValue.value);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
albumNameValue.value = "未命名相册";
|
albumNameValue.value = "未命名相册";
|
||||||
await getAlbumList("0", selecetedKey.value);
|
await getAlbumList(0, selecetedKey.value);
|
||||||
} else {
|
} else {
|
||||||
message.error("创建相册失败");
|
message.error("创建相册失败");
|
||||||
}
|
}
|
||||||
@@ -153,7 +298,7 @@ async function createAlbumSubmit() {
|
|||||||
*/
|
*/
|
||||||
async function handleSelect({key}) {
|
async function handleSelect({key}) {
|
||||||
selecetedKey.value = key;
|
selecetedKey.value = key;
|
||||||
await getAlbumList("0", key);
|
await getAlbumList(0, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,7 +306,7 @@ async function handleSelect({key}) {
|
|||||||
* @param activeKey
|
* @param activeKey
|
||||||
*/
|
*/
|
||||||
async function handleTabChange(activeKey: string) {
|
async function handleTabChange(activeKey: string) {
|
||||||
await getAlbumList(activeKey, selecetedKey.value);
|
await getAlbumList(parseInt(activeKey), selecetedKey.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,7 +314,7 @@ async function handleTabChange(activeKey: string) {
|
|||||||
* @param type
|
* @param type
|
||||||
* @param sort
|
* @param sort
|
||||||
*/
|
*/
|
||||||
async function getAlbumList(type: string = "0", sort: boolean = true) {
|
async function getAlbumList(type: number = 0, sort: boolean = true) {
|
||||||
albumList.value = [];
|
albumList.value = [];
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res: any = await albumListApi(type, sort);
|
const res: any = await albumListApi(type, sort);
|
||||||
@@ -192,7 +337,7 @@ async function renameAlbum(id: number, name: string) {
|
|||||||
const res: any = await renameAlbumApi(id, name);
|
const res: any = await renameAlbumApi(id, name);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
albumRenameValue.value = "";
|
albumRenameValue.value = "";
|
||||||
await getAlbumList("0", selecetedKey.value);
|
await getAlbumList(0, selecetedKey.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +348,7 @@ async function renameAlbum(id: number, name: string) {
|
|||||||
async function deleteAlbum(id: number) {
|
async function deleteAlbum(id: number) {
|
||||||
const res: any = await deleteAlbumApi(id);
|
const res: any = await deleteAlbumApi(id);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
await getAlbumList("0", selecetedKey.value);
|
await getAlbumList(0, selecetedKey.value);
|
||||||
} else {
|
} else {
|
||||||
message.error("删除相册失败");
|
message.error("删除相册失败");
|
||||||
}
|
}
|
||||||
@@ -221,7 +366,7 @@ function handleClick(id: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getAlbumList("0", selecetedKey.value);
|
getAlbumList(0, selecetedKey.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@@ -52,11 +52,12 @@ import Vue3JustifiedLayout from "vue3-justified-layout";
|
|||||||
import 'vue3-justified-layout/dist/style.css';
|
import 'vue3-justified-layout/dist/style.css';
|
||||||
import {queryThingDetailListApi} from "@/api/storage";
|
import {queryThingDetailListApi} from "@/api/storage";
|
||||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||||
|
import useStore from "@/store";
|
||||||
|
|
||||||
|
|
||||||
const selected = ref<(string | number)[]>([]);
|
const selected = ref<(string | number)[]>([]);
|
||||||
const albumList = ref<any[]>([]);
|
const albumList = ref<any[]>([]);
|
||||||
|
const upload = useStore().upload;
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const options = reactive({
|
const options = reactive({
|
||||||
@@ -64,7 +65,7 @@ const options = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function getImageList(tag_name: string) {
|
async function getImageList(tag_name: string) {
|
||||||
const res: any = await queryThingDetailListApi(tag_name, "ali", "schisandra-album");
|
const res: any = await queryThingDetailListApi(tag_name, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
albumList.value = res.data.records;
|
albumList.value = res.data.records;
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,6 @@ const thingAlbumList = ref<any[]>([]);
|
|||||||
|
|
||||||
async function getThingAlbumList(provider: string, bucket: string) {
|
async function getThingAlbumList(provider: string, bucket: string) {
|
||||||
const res: any = await queryThingAlbumApi(provider, bucket);
|
const res: any = await queryThingAlbumApi(provider, bucket);
|
||||||
console.log(res);
|
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
thingAlbumList.value = res.data.records;
|
thingAlbumList.value = res.data.records;
|
||||||
}
|
}
|
||||||
@@ -53,7 +52,7 @@ function handleClick(id: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getThingAlbumList(upload.storageSelected[0], upload.storageSelected[1]);
|
getThingAlbumList(upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@@ -45,8 +45,9 @@
|
|||||||
<div class="image-share-left-bottom-title">
|
<div class="image-share-left-bottom-title">
|
||||||
<h3>快传管理</h3>
|
<h3>快传管理</h3>
|
||||||
<ARangePicker
|
<ARangePicker
|
||||||
:value="hackValue || value"
|
:value="selectedDateRange"
|
||||||
:disabled-date="disabledDate"
|
:disabled-date="disabledDate"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
@openChange="onOpenChange"
|
@openChange="onOpenChange"
|
||||||
@calendarChange="onCalendarChange"
|
@calendarChange="onCalendarChange"
|
||||||
@@ -55,167 +56,82 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-share-left-bottom-content">
|
<div class="image-share-left-bottom-content">
|
||||||
<ACard style="width: 100%;height: 100%;" :bodyStyle="{padding: 0}">
|
<ACard style="width: 100%;height: 100%;"
|
||||||
<ATable :columns="columns" size="large" style="width: 100%;height: 100%;" :bordered="false">
|
:bodyStyle="{padding: 0, overflow: 'auto', display: 'flex', flexDirection: 'column'}">
|
||||||
|
<ATable :columns="columns" :data-source="dataSources" size="large"
|
||||||
|
style="flex: 1"
|
||||||
|
:pagination="false"
|
||||||
|
:loading="loading"
|
||||||
|
:scroll="{ y: '40vh',x:true }"
|
||||||
|
:bordered="false" @resizeColumn="handleResizeColumn">
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'cover_image'">
|
||||||
|
<AAvatar shape="square" size="large" :src="`data:image/png;base64,`+record.cover_image"/>
|
||||||
|
</template>
|
||||||
|
<!-- 访问密码 -->
|
||||||
|
<template v-else-if="column.key === 'access_password'">
|
||||||
|
<AInputPassword
|
||||||
|
v-if="record.access_password"
|
||||||
|
v-model:value="record.access_password"
|
||||||
|
type="password"
|
||||||
|
:visibilityToggle="true"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
readOnly
|
||||||
|
style="width: 100px;"
|
||||||
|
/>
|
||||||
|
<p v-else style="color: #999">无密码</p>
|
||||||
|
</template>
|
||||||
|
<!-- 有效期 -->
|
||||||
|
<template v-else-if="column.key === 'validity_period'">
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'action'">
|
||||||
|
<ATooltip title="复制分享链接">
|
||||||
|
<AButton type="text" size="small" @click="copyToClipboard(record.share_code)">
|
||||||
|
<LinkOutlined />
|
||||||
|
</AButton>
|
||||||
|
</ATooltip>
|
||||||
|
<ATooltip title="删除快传记录">
|
||||||
|
<APopconfirm
|
||||||
|
title="确定删除该快传记录?"
|
||||||
|
ok-text="确定"
|
||||||
|
cancel-text="取消"
|
||||||
|
>
|
||||||
|
<AButton type="text" size="small" danger>
|
||||||
|
<DeleteOutlined/>
|
||||||
|
</AButton>
|
||||||
|
</APopconfirm>
|
||||||
|
</ATooltip>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
</ATable>
|
</ATable>
|
||||||
</ACard>
|
</ACard>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-share-right">
|
<ShareUpload/>
|
||||||
<div class="image-share-right-top">
|
|
||||||
<h3>照片快传</h3>
|
|
||||||
</div>
|
|
||||||
<div class="image-share-right-bottom">
|
|
||||||
<div class="image-share-right-bottom-content">
|
|
||||||
<div class="image-share-right-bottom-upload" ref="qrContainer" v-if="fileList.length<=0">
|
|
||||||
<AUploadDragger
|
|
||||||
name="file"
|
|
||||||
:multiple="true"
|
|
||||||
:showUploadList="false"
|
|
||||||
:beforeUpload="beforeUpload"
|
|
||||||
v-model:fileList="fileList"
|
|
||||||
class="image-share-right-upload"
|
|
||||||
>
|
|
||||||
<div class="image-share-right-upload-item">
|
|
||||||
<p class="ant-upload-drag-icon">
|
|
||||||
<ABadge :offset="[-15, 20]" :count="fileList.length">
|
|
||||||
<AAvatar shape="square" :size="folderIconSize" :src="folder"/>
|
|
||||||
</ABadge>
|
|
||||||
</p>
|
|
||||||
<p class="ant-upload-text" style="font-size: 2.6vh;font-weight: bolder">单击或拖动文件到此区域以上传</p>
|
|
||||||
<AButton type="primary" size="large" shape="round" style="width: 70%">上 传 照 片</AButton>
|
|
||||||
|
|
||||||
<div class="qr">
|
|
||||||
<AQrcode :bordered="false" color="rgba(126, 126, 135, 0.48)"
|
|
||||||
:size="qrcodeSize"
|
|
||||||
:value="`git.landaiqing.cneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjI1MTEyMjE3MzQyMDIxIiwidHlwZSI6ImFjY2VzcyIsImV4cCI6MTczOTg3ODIyOCwibmJmIjoxNzM5ODcxMDI4LCJpYXQiOjE3Mzk4NzEwMjh9.EUiZsVjhGqHx1V5o90S3W5li6nIqucxy9eEY9LWgqXY`"
|
|
||||||
:icon="phone"
|
|
||||||
:iconSize="iconSize"
|
|
||||||
:status="`active`"
|
|
||||||
/>
|
|
||||||
<span style="font-size: 2vh;color: #999999">手机扫码上传</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AUploadDragger>
|
|
||||||
</div>
|
|
||||||
<div class="image-share-right-bottom-container" v-else>
|
|
||||||
<div class="image-share-right-bottom-container-header">
|
|
||||||
<AInput v-model:value="titleName" :bordered="false" size="large" placeholder="给快传起个标题"/>
|
|
||||||
<ADropdown placement="bottomLeft" :trigger="['click']">
|
|
||||||
<template #overlay>
|
|
||||||
<AMenu>
|
|
||||||
<AMenuItem key="1">
|
|
||||||
<AUpload
|
|
||||||
name="file"
|
|
||||||
:multiple="true"
|
|
||||||
:showUploadList="false"
|
|
||||||
:beforeUpload="beforeUpload"
|
|
||||||
v-model:fileList="fileList"
|
|
||||||
>
|
|
||||||
上传文件
|
|
||||||
</AUpload>
|
|
||||||
</AMenuItem>
|
|
||||||
<AMenuItem key="2">
|
|
||||||
<APopover placement="bottomLeft" trigger="hover">
|
|
||||||
<template #content>
|
|
||||||
<AQrcode :bordered="false" color="rgba(126, 126, 135, 0.48)"
|
|
||||||
:size="qrcodeSize"
|
|
||||||
:value="`git.landaiqing.cneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjI1MTEyMjE3MzQyMDIxIiwidHlwZSI6ImFjY2VzcyIsImV4cCI6MTczOTg3ODIyOCwibmJmIjoxNzM5ODcxMDI4LCJpYXQiOjE3Mzk4NzEwMjh9.EUiZsVjhGqHx1V5o90S3W5li6nIqucxy9eEY9LWgqXY`"
|
|
||||||
:icon="phone"
|
|
||||||
:iconSize="iconSize"
|
|
||||||
:status="`active`"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
手机上传
|
|
||||||
</APopover>
|
|
||||||
</AMenuItem>
|
|
||||||
</AMenu>
|
|
||||||
</template>
|
|
||||||
<AButton size="middle" shape="circle">
|
|
||||||
<template #icon>
|
|
||||||
<PlusOutlined/>
|
|
||||||
</template>
|
|
||||||
</AButton>
|
|
||||||
</ADropdown>
|
|
||||||
</div>
|
|
||||||
<div class="image-share-right-bottom-content-list">
|
|
||||||
<p style="font-size: 2.0vh;color: #999999;cursor: default">共{{ fileList.length }}个文件
|
|
||||||
{{ calculateTotalSize(fileList) }}</p>
|
|
||||||
<div class="image-share-right-bottom-content-list-wrapper">
|
|
||||||
<div class="image-share-right-bottom-content-list-item" v-for="(item, index) in fileList" :key="index">
|
|
||||||
<div class="file-thumbnail">
|
|
||||||
<AImage :width="50" :height="50" :src="convertFileToUrl(item.originFileObj)">
|
|
||||||
<template #previewMask>
|
|
||||||
</template>
|
|
||||||
</AImage>
|
|
||||||
</div>
|
|
||||||
<div class="file-info">
|
|
||||||
<p style="font-size: 2.0vh;color: #333333;cursor: default;font-weight: bold">{{ item.name }}</p>
|
|
||||||
<p style="font-size: 1.5vh;color: #999999;cursor: default">{{
|
|
||||||
formatByteSize(item.size)
|
|
||||||
}}</p>
|
|
||||||
</div>
|
|
||||||
<div class="file-operation">
|
|
||||||
<AButton size="middle" shape="circle" type="text" @click="removeBase64Image(index)">
|
|
||||||
<template #icon>
|
|
||||||
<CloseOutlined/>
|
|
||||||
</template>
|
|
||||||
</AButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ADivider/>
|
|
||||||
<div class="image-share-right-bottom-operation">
|
|
||||||
<div class="image-share-right-operation-select">
|
|
||||||
<div class="image-share-right-operation-item">
|
|
||||||
<span class="label-text">访问时效</span>
|
|
||||||
<ASelect style="width: 50%">
|
|
||||||
<ASelectOption value="1">1天</ASelectOption>
|
|
||||||
<ASelectOption value="3">3天</ASelectOption>
|
|
||||||
<ASelectOption value="7">7天</ASelectOption>
|
|
||||||
<ASelectOption value="15">15天</ASelectOption>
|
|
||||||
<ASelectOption value="30">30天</ASelectOption>
|
|
||||||
<ASelectOption value="0">永久</ASelectOption>
|
|
||||||
</ASelect>
|
|
||||||
</div>
|
|
||||||
<div class="image-share-right-operation-item">
|
|
||||||
<span class="label-text">访问密码</span>
|
|
||||||
<AInputPassword style="width: 50%"></AInputPassword>
|
|
||||||
</div>
|
|
||||||
<div class="image-share-right-operation-item">
|
|
||||||
<span class="label-text">访问限制</span>
|
|
||||||
<AInputNumber style="width: 50%" :defaultValue="100" :min="1"></AInputNumber>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="image-share-right-bottom-operation-btn">
|
|
||||||
<AButton type="primary" size="large" shape="default" style="width: 100%">开始上传</AButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {Dayjs} from 'dayjs';
|
import {Dayjs} from 'dayjs';
|
||||||
import folder from "@/assets/svgs/folder.svg";
|
import dayjs from 'dayjs';
|
||||||
import {NSFWJS} from "nsfwjs";
|
import ShareUpload from "@/views/ImageShare/ShareUpload.vue";
|
||||||
import {initNSFWJs, predictNSFW} from "@/utils/tfjs/nsfw.ts";
|
import {queryShareRecordListApi} from "@/api/share";
|
||||||
import {message} from "ant-design-vue";
|
import {message} from "ant-design-vue";
|
||||||
import i18n from "@/locales";
|
import 'dayjs/locale/zh-cn';
|
||||||
import phone from "@/assets/svgs/qr-phone.svg";
|
|
||||||
|
dayjs.locale('zh-cn');
|
||||||
|
|
||||||
|
|
||||||
type RangeValue = [Dayjs, Dayjs];
|
type RangeValue = [Dayjs, Dayjs];
|
||||||
const dates = ref<RangeValue>();
|
const dates = ref<RangeValue>();
|
||||||
const value = ref<RangeValue>();
|
const selectedDateRange = ref<RangeValue>();
|
||||||
const hackValue = ref<RangeValue>();
|
const hackValue = ref<RangeValue>();
|
||||||
const titleName = ref<string>("");
|
|
||||||
|
|
||||||
const qrContainer = ref<HTMLDivElement | null>(null);
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
const disabledDate = (current: Dayjs) => {
|
const disabledDate = (current: Dayjs) => {
|
||||||
@@ -236,81 +152,65 @@ const onOpenChange = (open: boolean) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChange = (val: RangeValue) => {
|
const onChange = async (val: RangeValue) => {
|
||||||
value.value = val;
|
selectedDateRange.value = val;
|
||||||
|
|
||||||
|
// 将日期范围处理成一个数组
|
||||||
|
if (val && val.length === 2) {
|
||||||
|
const startDate = val[0].format('YYYY-MM-DD'); // 格式化开始日期
|
||||||
|
const endDate = val[1].format('YYYY-MM-DD'); // 格式化结束日期
|
||||||
|
const dateRangeArray = [startDate, endDate]; // 将日期范围存入数组
|
||||||
|
await getShareRecords(dateRangeArray);
|
||||||
|
} else {
|
||||||
|
console.log('No date range selected');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCalendarChange = (val: RangeValue) => {
|
const onCalendarChange = (val: RangeValue) => {
|
||||||
dates.value = val;
|
dates.value = val;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 格式化字节大小
|
|
||||||
* @param bytes
|
|
||||||
*/
|
|
||||||
function formatByteSize(bytes) {
|
|
||||||
if (bytes < 1024) {
|
|
||||||
return `${bytes} Bytes`;
|
|
||||||
} else if (bytes < 1024 * 1024) {
|
|
||||||
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
||||||
} else if (bytes < 1024 * 1024 * 1024) {
|
|
||||||
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
||||||
} else {
|
|
||||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 格式化字节大小
|
|
||||||
* @param bytes
|
|
||||||
* @param decimals
|
|
||||||
*/
|
|
||||||
function formatBytes(bytes: number, decimals: number = 2): string {
|
|
||||||
if (bytes === 0) return '0 Bytes';
|
|
||||||
const k = 1024;
|
|
||||||
const dm = decimals < 0 ? 0 : decimals;
|
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
||||||
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
||||||
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算文件总大小
|
|
||||||
* @param fileDataArray
|
|
||||||
*/
|
|
||||||
function calculateTotalSize(fileDataArray: { size: number }[]): string {
|
|
||||||
const totalSize = fileDataArray.reduce((acc, file) => acc + file.size, 0);
|
|
||||||
return formatBytes(totalSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '快传记录',
|
title: '快传记录',
|
||||||
dataIndex: 'name',
|
dataIndex: 'cover_image',
|
||||||
key: 'name',
|
key: 'cover_image',
|
||||||
|
ellipsis: true,
|
||||||
|
width: 90,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '上传时间',
|
title: '上传时间',
|
||||||
dataIndex: 'created_at',
|
dataIndex: 'created_at',
|
||||||
key: 'created_at',
|
key: 'created_at',
|
||||||
|
ellipsis: true,
|
||||||
|
customRender: ({text}) => {
|
||||||
|
return dayjs(text).format('YYYY-MM-DD'); // 格式化时间
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '浏览次数',
|
title: '访问密码',
|
||||||
dataIndex: 'views',
|
key: 'access_password',
|
||||||
key: 'views',
|
dataIndex: 'access_password',
|
||||||
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '浏览人数',
|
title: '访问限制',
|
||||||
key: 'viewers',
|
key: 'visit_limit',
|
||||||
dataIndex: 'viewers',
|
dataIndex: 'visit_limit',
|
||||||
|
ellipsis: true,
|
||||||
|
customRender: ({text}) => {
|
||||||
|
return `${text}次`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '传输状态',
|
title: '有效期',
|
||||||
key: 'status',
|
key: 'validity_period',
|
||||||
dataIndex: 'status',
|
dataIndex: 'validity_period',
|
||||||
|
ellipsis: true,
|
||||||
|
customRender: ({text}) => {
|
||||||
|
return formatValidityPeriod(text);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
@@ -319,339 +219,44 @@ const columns = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const qrcodeSize = ref<number>(220);
|
function handleResizeColumn(w, col) {
|
||||||
const iconSize = ref<number>(30);
|
col.width = w;
|
||||||
const folderIconSize = ref<number>(100);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新二维码大小
|
|
||||||
*/
|
|
||||||
const updateQrcodeSize = () => {
|
|
||||||
if (qrContainer.value) {
|
|
||||||
// 设置 QRCode 大小
|
|
||||||
const containerWidth = qrContainer.value.clientWidth;
|
|
||||||
qrcodeSize.value = containerWidth * 0.5; // 设置 QRCode 为父盒子宽度的80%
|
|
||||||
folderIconSize.value = containerWidth * 0.3; // 设置文件夹图标大小为父盒子宽度的10%
|
|
||||||
iconSize.value = Math.min(containerWidth * 0.1, 40); // 设置 icon 大小为父盒子宽度的10%
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatValidityPeriod = (period: number) => {
|
||||||
|
return period === 0 ? '永久' : `${period}天`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fileList = ref<any[]>([]);
|
// 复制功能
|
||||||
|
function copyToClipboard(text: string) {
|
||||||
/**
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
* 上传文件前置
|
message.success('复制成功');
|
||||||
* @param file
|
}).catch(() => {
|
||||||
*/
|
message.error('复制失败');
|
||||||
async function beforeUpload(file: any) {
|
|
||||||
if (!window.FileReader) return false; // 判断是否支持FileReader
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsDataURL(file); // 文件转换
|
|
||||||
reader.onloadend = async function () {
|
|
||||||
const img: HTMLImageElement = document.createElement('img');
|
|
||||||
img.src = reader.result as string;
|
|
||||||
img.onload = async () => {
|
|
||||||
// 图片 NSFW 检测
|
|
||||||
const nsfw: NSFWJS = await initNSFWJs();
|
|
||||||
const isNSFW: boolean = await predictNSFW(nsfw, img);
|
|
||||||
if (isNSFW) {
|
|
||||||
message.error(i18n.global.t('comment.illegalImage'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除 base64 图片
|
|
||||||
* @param index
|
|
||||||
*/
|
|
||||||
async function removeBase64Image(index: number) {
|
|
||||||
fileList.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 转换文件为 URL
|
|
||||||
* @param file
|
|
||||||
*/
|
|
||||||
function convertFileToUrl(file: any) {
|
|
||||||
return URL.createObjectURL(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener("resize", updateQrcodeSize);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataSources = ref<any[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分享记录
|
||||||
|
*/
|
||||||
|
async function getShareRecords(dateRange: string[]) {
|
||||||
|
loading.value = true;
|
||||||
|
const res: any = await queryShareRecordListApi(dateRange);
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
dataSources.value = res.data.records;
|
||||||
|
}
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const endDate = dayjs().format('YYYY-MM-DD'); // 当前日期
|
||||||
|
const startDate = dayjs().subtract(30, 'day').format('YYYY-MM-DD'); // 30 天前的日期
|
||||||
|
await getShareRecords([startDate, endDate]);
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss" src="./index.scss">
|
||||||
.image-share {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
gap: 20px;
|
|
||||||
|
|
||||||
.image-share-left {
|
|
||||||
height: 100%;
|
|
||||||
width: 65%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
.image-share-left-top {
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.image-share-left-title {
|
|
||||||
width: 100%;
|
|
||||||
height: 20%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-share-left-content {
|
|
||||||
width: 100%;
|
|
||||||
height: 80%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.image-share-left-content-item {
|
|
||||||
height: 100%;
|
|
||||||
width: 30%;
|
|
||||||
color: #fff;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.image-share-left-item-content {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-start;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-share-left-bottom {
|
|
||||||
width: 100%;
|
|
||||||
height: 70%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.image-share-left-bottom-title {
|
|
||||||
width: 100%;
|
|
||||||
height: 20%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-share-left-bottom-content {
|
|
||||||
width: 100%;
|
|
||||||
height: 80%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-share-right {
|
|
||||||
height: 100%;
|
|
||||||
width: 35%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.image-share-right-top {
|
|
||||||
width: 100%;
|
|
||||||
height: 6%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-share-right-bottom {
|
|
||||||
width: 100%;
|
|
||||||
height: 94%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.image-share-right-bottom-content {
|
|
||||||
width: 90%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 10px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.image-share-right-bottom-upload {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.image-share-right-upload {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-share-right-bottom-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.image-share-right-bottom-container-header {
|
|
||||||
width: 100%;
|
|
||||||
height: 10%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-share-right-bottom-content-list {
|
|
||||||
width: 95%;
|
|
||||||
height: 40%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-content: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 15px;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
padding: 10px;
|
|
||||||
overflow: auto;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border-radius: 10px;
|
|
||||||
|
|
||||||
.image-share-right-bottom-content-list-wrapper {
|
|
||||||
width: 100%;
|
|
||||||
height: 27vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-content: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
overflow: auto;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.image-share-right-bottom-content-list-item {
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.file-thumbnail {
|
|
||||||
height: 100%;
|
|
||||||
width: 17%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-info {
|
|
||||||
height: 100%;
|
|
||||||
width: 63%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-operation {
|
|
||||||
height: 100%;
|
|
||||||
width: 20%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.image-share-right-bottom-operation {
|
|
||||||
width: 100%;
|
|
||||||
height: 40%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.image-share-right-operation-select {
|
|
||||||
width: 100%;
|
|
||||||
height: 75%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: space-between;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
|
|
||||||
.image-share-right-operation-item {
|
|
||||||
width: 100%;
|
|
||||||
height: 5vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: baseline;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.label-text {
|
|
||||||
width: 50%;
|
|
||||||
color: #999999;
|
|
||||||
font-size: 2.2vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-share-right-bottom-operation-btn {
|
|
||||||
width: 100%;
|
|
||||||
height: 35%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.image-share-right-upload-item {
|
|
||||||
//width: 100% !important;
|
|
||||||
//height: 100% !important;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
//justify-content: center;
|
|
||||||
gap: 2vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
679
src/views/ImageShare/ShareUpload.vue
Normal file
679
src/views/ImageShare/ShareUpload.vue
Normal file
@@ -0,0 +1,679 @@
|
|||||||
|
<template>
|
||||||
|
<div class="image-share-right">
|
||||||
|
<div class="image-share-right-top">
|
||||||
|
<h3>照片快传</h3>
|
||||||
|
</div>
|
||||||
|
<div class="image-share-right-bottom">
|
||||||
|
<div class="image-share-right-bottom-content">
|
||||||
|
<div class="image-share-right-bottom-upload" ref="qrContainer"
|
||||||
|
v-if="fileList.length <= 0 && !uploadSuccess">
|
||||||
|
<AUploadDragger
|
||||||
|
name="file"
|
||||||
|
:multiple="true"
|
||||||
|
:showUploadList="false"
|
||||||
|
:beforeUpload="beforeUpload"
|
||||||
|
v-model:fileList="fileList"
|
||||||
|
:customRequest="customRequest"
|
||||||
|
class="image-share-right-upload"
|
||||||
|
>
|
||||||
|
<div class="image-share-right-upload-item">
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
<ABadge :offset="[-15, 20]" :count="fileList.length">
|
||||||
|
<AAvatar shape="square" :size="folderIconSize" :src="folder"/>
|
||||||
|
</ABadge>
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-text" style="font-size: 2.6vh;font-weight: bolder">单击或拖动文件到此区域以上传</p>
|
||||||
|
<AButton type="primary" size="large" shape="round" style="width: 70%">上 传 照 片</AButton>
|
||||||
|
|
||||||
|
<div class="qr">
|
||||||
|
<AQrcode :bordered="false" color="rgba(126, 126, 135, 0.48)"
|
||||||
|
:size="qrcodeSize"
|
||||||
|
:value="`git.landaiqing.cneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjI1MTEyMjE3MzQyMDIxIiwidHlwZSI6ImFjY2VzcyIsImV4cCI6MTczOTg3ODIyOCwibmJmIjoxNzM5ODcxMDI4LCJpYXQiOjE3Mzk4NzEwMjh9.EUiZsVjhGqHx1V5o90S3W5li6nIqucxy9eEY9LWgqXY`"
|
||||||
|
:icon="phone"
|
||||||
|
:iconSize="iconSize"
|
||||||
|
:status="`active`"
|
||||||
|
/>
|
||||||
|
<span style="font-size: 2vh;color: #999999">手机扫码上传</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AUploadDragger>
|
||||||
|
</div>
|
||||||
|
<div class="image-share-right-bottom-container" v-if="fileList.length > 0 && !loading">
|
||||||
|
<div class="image-share-right-bottom-container-header">
|
||||||
|
<AInput v-model:value="titleName" :bordered="false" size="large" placeholder="给快传起个标题"/>
|
||||||
|
<ADropdown placement="bottomLeft" :trigger="['click']">
|
||||||
|
<template #overlay>
|
||||||
|
<AMenu>
|
||||||
|
<AMenuItem key="1">
|
||||||
|
<AUpload
|
||||||
|
name="file"
|
||||||
|
:multiple="true"
|
||||||
|
:showUploadList="false"
|
||||||
|
:beforeUpload="beforeUpload"
|
||||||
|
v-model:fileList="fileList"
|
||||||
|
>
|
||||||
|
上传文件
|
||||||
|
</AUpload>
|
||||||
|
</AMenuItem>
|
||||||
|
<AMenuItem key="2">
|
||||||
|
<APopover placement="bottomLeft" trigger="hover">
|
||||||
|
<template #content>
|
||||||
|
<AQrcode :bordered="false" color="rgba(126, 126, 135, 0.48)"
|
||||||
|
:size="qrcodeSize"
|
||||||
|
:value="`git.landaiqing.cneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjI1MTEyMjE3MzQyMDIxIiwidHlwZSI6ImFjY2VzcyIsImV4cCI6MTczOTg3ODIyOCwibmJmIjoxNzM5ODcxMDI4LCJpYXQiOjE3Mzk4NzEwMjh9.EUiZsVjhGqHx1V5o90S3W5li6nIqucxy9eEY9LWgqXY`"
|
||||||
|
:icon="phone"
|
||||||
|
:iconSize="iconSize"
|
||||||
|
:status="`active`"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
手机上传
|
||||||
|
</APopover>
|
||||||
|
</AMenuItem>
|
||||||
|
</AMenu>
|
||||||
|
</template>
|
||||||
|
<AButton size="middle" shape="circle">
|
||||||
|
<template #icon>
|
||||||
|
<PlusOutlined/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
</ADropdown>
|
||||||
|
</div>
|
||||||
|
<div class="image-share-right-bottom-content-list">
|
||||||
|
<p style="font-size: 2.0vh;color: #999999;cursor: default">共{{ fileList.length }}个文件
|
||||||
|
{{ calculateTotalSize(fileList) }}</p>
|
||||||
|
<div class="image-share-right-bottom-content-list-wrapper">
|
||||||
|
<div class="image-share-right-bottom-content-list-item"
|
||||||
|
v-for="(item, index) in fileList" :key="index">
|
||||||
|
<div class="file-thumbnail" ref="fileContainer">
|
||||||
|
<AImage :width="50" :height="50"
|
||||||
|
:src="convertFileToUrl(item.originFileObj)">
|
||||||
|
<template #previewMask>
|
||||||
|
</template>
|
||||||
|
</AImage>
|
||||||
|
</div>
|
||||||
|
<div class="file-info">
|
||||||
|
<p style="font-size: 2.0vh;color: #333333;cursor: default;font-weight: bold">{{ item.name }}</p>
|
||||||
|
<p style="font-size: 1.5vh;color: #999999;cursor: default">{{
|
||||||
|
formatByteSize(item.size)
|
||||||
|
}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="file-operation">
|
||||||
|
<AButton size="middle" shape="circle" type="text" @click="removeImage(index)">
|
||||||
|
<template #icon>
|
||||||
|
<CloseOutlined/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ADivider/>
|
||||||
|
<div class="image-share-right-bottom-operation">
|
||||||
|
<div class="image-share-right-operation-select">
|
||||||
|
<div class="image-share-right-operation-item">
|
||||||
|
<span class="label-text">访问时效</span>
|
||||||
|
<ASelect style="width: 50%" placeholder="请选择" :defaultValue="1" v-model:value="expire_date">
|
||||||
|
<ASelectOption value="1">1天</ASelectOption>
|
||||||
|
<ASelectOption value="3">3天</ASelectOption>
|
||||||
|
<ASelectOption value="7">7天</ASelectOption>
|
||||||
|
<ASelectOption value="15">15天</ASelectOption>
|
||||||
|
<ASelectOption value="30">30天</ASelectOption>
|
||||||
|
<ASelectOption value="0">永久</ASelectOption>
|
||||||
|
</ASelect>
|
||||||
|
</div>
|
||||||
|
<div class="image-share-right-operation-item">
|
||||||
|
<span class="label-text">访问密码</span>
|
||||||
|
<AInputPassword style="width: 50%" v-model:value="access_password" :maxlength="10"
|
||||||
|
:showCount="true"></AInputPassword>
|
||||||
|
</div>
|
||||||
|
<div class="image-share-right-operation-item">
|
||||||
|
<span class="label-text">访问限制</span>
|
||||||
|
<AInputNumber style="width: 50%" :defaultValue="100" :min="1"
|
||||||
|
v-model:value="access_limit"></AInputNumber>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="image-share-right-bottom-operation-btn">
|
||||||
|
<AButton type="primary" size="middle" shape="round" style="width: 100%"
|
||||||
|
:loading="loading"
|
||||||
|
@click="customUploader">
|
||||||
|
开始上传
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="loading && !uploadSuccess" class="image-share-right-bottom-loading">
|
||||||
|
<div class="image-share-right-bottom-loading-content">
|
||||||
|
<a-progress
|
||||||
|
type="circle"
|
||||||
|
:stroke-color="{
|
||||||
|
'0%': '#108ee9',
|
||||||
|
'100%': '#87d068',
|
||||||
|
}"
|
||||||
|
:percent="percent"
|
||||||
|
:size="180"
|
||||||
|
/>
|
||||||
|
<p>{{ fileList.length }} 个文件 / {{ calculateTotalSize(fileList) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="image-share-right-bottom-loading-footer">
|
||||||
|
<AButton type="primary" size="large" shape="round" style="width: 80%" @click="abort">
|
||||||
|
取 消
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="uploadSuccess" class="image-share-right-bottom-success">
|
||||||
|
<div class="image-share-right-bottom-success-header">
|
||||||
|
<div class="image-share-right-bottom-success-header-title">
|
||||||
|
<CheckCircleOutlined style="font-size: 3vh;color: #52c41a"/>
|
||||||
|
<h3>上 传 成 功</h3>
|
||||||
|
</div>
|
||||||
|
<div class="image-share-right-bottom-success-header-close">
|
||||||
|
<AButton type="text" size="middle" shape="circle" @click="uploadSuccess = false">
|
||||||
|
<CloseOutlined/>
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="image-share-right-bottom-success-content">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import folder from "@/assets/svgs/folder.svg";
|
||||||
|
import phone from "@/assets/svgs/qr-phone.svg";
|
||||||
|
import useStore from "@/store";
|
||||||
|
import {NSFWJS} from "nsfwjs";
|
||||||
|
import {initNSFWJs, predictNSFW} from "@/utils/tfjs/nsfw.ts";
|
||||||
|
import {message} from "ant-design-vue";
|
||||||
|
import i18n from "@/locales";
|
||||||
|
import {useRequest} from "alova/client";
|
||||||
|
import {shareImageUploadApi} from "@/api/share";
|
||||||
|
import imageCompression from "browser-image-compression";
|
||||||
|
import {generateThumbnail} from "@/utils/imageUtils/generateThumb.ts";
|
||||||
|
|
||||||
|
const titleName = ref<string>("");
|
||||||
|
|
||||||
|
const upload = useStore().upload;
|
||||||
|
const percent = ref<number>(0);
|
||||||
|
const uploadSuccess = ref<boolean>(false);
|
||||||
|
const qrContainer = ref<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化字节大小
|
||||||
|
* @param bytes
|
||||||
|
*/
|
||||||
|
function formatByteSize(bytes: number) {
|
||||||
|
if (bytes < 1024) {
|
||||||
|
return `${bytes} Bytes`;
|
||||||
|
} else if (bytes < 1024 * 1024) {
|
||||||
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
||||||
|
} else if (bytes < 1024 * 1024 * 1024) {
|
||||||
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
||||||
|
} else {
|
||||||
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化字节大小
|
||||||
|
* @param bytes
|
||||||
|
* @param decimals
|
||||||
|
*/
|
||||||
|
function formatBytes(bytes: number, decimals: number = 2): string {
|
||||||
|
if (bytes === 0) return '0 Bytes';
|
||||||
|
const k = 1024;
|
||||||
|
const dm = decimals < 0 ? 0 : decimals;
|
||||||
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算文件总大小
|
||||||
|
* @param fileDataArray
|
||||||
|
*/
|
||||||
|
function calculateTotalSize(fileDataArray: { size: number }[]): string {
|
||||||
|
const totalSize = fileDataArray.reduce((acc, file) => acc + file.size, 0);
|
||||||
|
return formatBytes(totalSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
const qrcodeSize = ref<number>(220);
|
||||||
|
const iconSize = ref<number>(30);
|
||||||
|
const folderIconSize = ref<number>(100);
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新二维码大小
|
||||||
|
*/
|
||||||
|
const updateQrcodeSize = () => {
|
||||||
|
if (qrContainer.value) {
|
||||||
|
// 设置 QRCode 大小
|
||||||
|
const containerWidth = qrContainer.value.clientWidth;
|
||||||
|
qrcodeSize.value = containerWidth * 0.5; // 设置 QRCode 为父盒子宽度的80%
|
||||||
|
folderIconSize.value = containerWidth * 0.3; // 设置文件夹图标大小为父盒子宽度的10%
|
||||||
|
iconSize.value = Math.min(containerWidth * 0.1, 40); // 设置 icon 大小为父盒子宽度的10%
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileList = ref<any[]>([]);
|
||||||
|
|
||||||
|
const expire_date = ref<string>("1");
|
||||||
|
|
||||||
|
const access_limit = ref<number>(100);
|
||||||
|
|
||||||
|
const access_password = ref<string>("");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件前置
|
||||||
|
* @param file
|
||||||
|
*/
|
||||||
|
async function beforeUpload(file: any) {
|
||||||
|
if (!window.FileReader) return false; // 判断是否支持FileReader
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file); // 文件转换
|
||||||
|
reader.onloadend = async function () {
|
||||||
|
const img: HTMLImageElement = document.createElement('img');
|
||||||
|
img.src = reader.result as string;
|
||||||
|
img.onload = async () => {
|
||||||
|
// 图片 NSFW 检测
|
||||||
|
const nsfw: NSFWJS = await initNSFWJs();
|
||||||
|
const isNSFW: boolean = await predictNSFW(nsfw, img);
|
||||||
|
if (isNSFW) {
|
||||||
|
message.error(i18n.global.t('comment.illegalImage'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义请求
|
||||||
|
* @param _file
|
||||||
|
* @param _fileList
|
||||||
|
*/
|
||||||
|
function customRequest(_file: any, _fileList: any) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {uploading, send: submitFile, abort} = useRequest(shareImageUploadApi, {
|
||||||
|
immediate: false,
|
||||||
|
debounce: 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义上传器
|
||||||
|
*/
|
||||||
|
async function customUploader() {
|
||||||
|
if (fileList.value.length <= 0) return;
|
||||||
|
loading.value = true;
|
||||||
|
uploadSuccess.value = false;
|
||||||
|
// 存储所有图片信息的数组
|
||||||
|
const images: any[] = [];
|
||||||
|
for (const file of fileList.value) {
|
||||||
|
// 压缩图片
|
||||||
|
const compressedFile = await imageCompression(file.originFileObj, {
|
||||||
|
maxSizeMB: 0.1,
|
||||||
|
maxWidthOrHeight: 750,
|
||||||
|
maxIteration: 10,
|
||||||
|
useWebWorker: true,
|
||||||
|
initialQuality: 0.6,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 生成缩略图
|
||||||
|
const {binaryData, width, height, size} = await generateThumbnail(compressedFile);
|
||||||
|
// 将文件转换为 Base64 编码
|
||||||
|
const base64Thumbnail = binaryData ? await toBase64(binaryData) : '';
|
||||||
|
const base64Image = await toBase64(file.originFileObj);
|
||||||
|
// 创建文件的元数据对象
|
||||||
|
const fileObj = {
|
||||||
|
file_name: file.name, // 文件名
|
||||||
|
origin_image: base64Image, // 原始图片的 base64 数据
|
||||||
|
file_type: file.type, // 文件类型
|
||||||
|
thumbnail: base64Thumbnail, // 缩略图的 base64 数据
|
||||||
|
thumb_w: width, // 缩略图宽度
|
||||||
|
thumb_h: height, // 缩略图高度
|
||||||
|
thumb_size: size, // 缩略图文件大小
|
||||||
|
};
|
||||||
|
|
||||||
|
// 将文件对象添加到图片数组
|
||||||
|
images.push(fileObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 准备发送给后端的请求数据
|
||||||
|
const requestData = {
|
||||||
|
title: titleName.value,
|
||||||
|
expire_date: expire_date.value,
|
||||||
|
access_limit: access_limit.value,
|
||||||
|
access_password: access_password.value,
|
||||||
|
provider: upload.storageSelected?.[0],
|
||||||
|
bucket: upload.storageSelected?.[1],
|
||||||
|
images: images,
|
||||||
|
};
|
||||||
|
watch(
|
||||||
|
() => uploading.value,
|
||||||
|
(upload) => {
|
||||||
|
if (upload && upload.loaded && upload.total) {
|
||||||
|
percent.value = Number(Math.round((upload.loaded / upload.total) * 100).toFixed(2));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const res: any = await submitFile(requestData);
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
uploadSuccess.value = true;
|
||||||
|
fileList.value = [];
|
||||||
|
}
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将文件转换为 Base64 编码
|
||||||
|
* @param file
|
||||||
|
*/
|
||||||
|
function toBase64(file: Blob) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onloadend = () => {
|
||||||
|
// 确保 reader.result 是字符串类型
|
||||||
|
if (typeof reader.result === 'string') {
|
||||||
|
resolve(reader.result.split(',')[1]); // 只获取 base64 部分
|
||||||
|
} else {
|
||||||
|
reject(new Error('FileReader result is not a string.'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.onerror = reject;
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除图片
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
async function removeImage(index: number) {
|
||||||
|
fileList.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换文件为 URL
|
||||||
|
* @param file
|
||||||
|
*/
|
||||||
|
function convertFileToUrl(file: any) {
|
||||||
|
return URL.createObjectURL(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = useStore().user;
|
||||||
|
const websocket = useStore().websocket;
|
||||||
|
const wsOptions = {
|
||||||
|
url: import.meta.env.VITE_FILE_SOCKET_URL + "?user_id=" + user.user.uid,
|
||||||
|
protocols: [user.token.accessToken],
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener("resize", updateQrcodeSize);
|
||||||
|
websocket.initialize(wsOptions);
|
||||||
|
websocket.on("message", async (res: any) => {
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
const {data} = res;
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.image-share-right {
|
||||||
|
height: 100%;
|
||||||
|
width: 35%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.image-share-right-top {
|
||||||
|
width: 100%;
|
||||||
|
height: 6%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-bottom {
|
||||||
|
width: 100%;
|
||||||
|
height: 94%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.image-share-right-bottom-content {
|
||||||
|
width: 90%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.image-share-right-bottom-upload {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.image-share-right-upload {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-bottom-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.image-share-right-bottom-container-header {
|
||||||
|
width: 100%;
|
||||||
|
height: 10%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-bottom-content-list {
|
||||||
|
width: 95%;
|
||||||
|
height: 40%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 15px;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
padding: 10px;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
.image-share-right-bottom-content-list-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 27vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
overflow: auto;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.image-share-right-bottom-content-list-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.file-thumbnail {
|
||||||
|
height: 100%;
|
||||||
|
width: 17%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
height: 100%;
|
||||||
|
width: 63%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-operation {
|
||||||
|
height: 100%;
|
||||||
|
width: 20%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.image-share-right-bottom-operation {
|
||||||
|
width: 100%;
|
||||||
|
height: 40%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.image-share-right-operation-select {
|
||||||
|
width: 100%;
|
||||||
|
height: 75%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
.image-share-right-operation-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 5vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.label-text {
|
||||||
|
width: 50%;
|
||||||
|
color: #999999;
|
||||||
|
font-size: 2.2vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-bottom-operation-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 35%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-bottom-loading {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.image-share-right-bottom-loading-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 80%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-bottom-loading-footer {
|
||||||
|
width: 100%;
|
||||||
|
height: 20%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-bottom-success {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.image-share-right-bottom-success-header {
|
||||||
|
width: 100%;
|
||||||
|
height: 10%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.image-share-right-bottom-success-header-title {
|
||||||
|
height: 100%;
|
||||||
|
width: 90%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-bottom-success-header-close {
|
||||||
|
height: 100%;
|
||||||
|
width: 10%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-bottom-success-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 90%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-right-upload-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2vh;
|
||||||
|
}
|
||||||
|
</style>
|
100
src/views/ImageShare/index.scss
Normal file
100
src/views/ImageShare/index.scss
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
.image-share {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
.image-share-left {
|
||||||
|
height: 100%;
|
||||||
|
width: 65%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.image-share-left-top {
|
||||||
|
width: 100%;
|
||||||
|
height: 30%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.image-share-left-title {
|
||||||
|
width: 100%;
|
||||||
|
height: 20%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-left-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 80%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.image-share-left-content-item {
|
||||||
|
height: 100%;
|
||||||
|
width: 30%;
|
||||||
|
color: #fff;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.image-share-left-item-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-left-bottom {
|
||||||
|
width: 100%;
|
||||||
|
height: 70%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.image-share-left-bottom-title {
|
||||||
|
width: 100%;
|
||||||
|
height: 20%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-share-left-bottom-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 80%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.ant-card {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.ant-table {
|
||||||
|
flex: 1; // 让 ATable 填满剩余空间
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
9
src/views/Phone/SharePhoneUpload/SharePhoneUpload.vue
Normal file
9
src/views/Phone/SharePhoneUpload/SharePhoneUpload.vue
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
@@ -96,7 +96,7 @@ const images = ref<any[]>([]);
|
|||||||
* 获取所有图片
|
* 获取所有图片
|
||||||
*/
|
*/
|
||||||
async function getAllImages() {
|
async function getAllImages() {
|
||||||
const res: any = await queryAllImagesApi("image", false, upload.storageSelected[0], upload.storageSelected[1]);
|
const res: any = await queryAllImagesApi("image", false, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
images.value = res.data.records;
|
images.value = res.data.records;
|
||||||
}
|
}
|
||||||
|
@@ -224,8 +224,8 @@ async function customUploadRequest(file: any) {
|
|||||||
formData.append("thumbnail", binaryData);
|
formData.append("thumbnail", binaryData);
|
||||||
}
|
}
|
||||||
formData.append("data", JSON.stringify({
|
formData.append("data", JSON.stringify({
|
||||||
provider: upload.storageSelected[0],
|
provider: upload.storageSelected?.[0],
|
||||||
bucket: upload.storageSelected[1],
|
bucket: upload.storageSelected?.[1],
|
||||||
fileType: file.file.type,
|
fileType: file.file.type,
|
||||||
...upload.predictResult,
|
...upload.predictResult,
|
||||||
}));
|
}));
|
||||||
|
@@ -38,6 +38,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||||
import 'vue3-justified-layout/dist/style.css';
|
import 'vue3-justified-layout/dist/style.css';
|
||||||
|
import {queryShareImageApi} from "@/api/share";
|
||||||
|
|
||||||
|
|
||||||
const selected = ref<(string | number)[]>([]);
|
const selected = ref<(string | number)[]>([]);
|
||||||
@@ -47,6 +48,13 @@ const options = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
async function getImages() {
|
||||||
|
const res = await queryShareImageApi("c09e3c571303448798c878095fbaa521", "123456");
|
||||||
|
console.log(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
getImages();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.recycling-bin {
|
.recycling-bin {
|
||||||
|
Reference in New Issue
Block a user