⬆️ upgrade dependencies and optimize the image display list
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@@ -26,6 +26,7 @@ declare module 'vue' {
|
||||
AImage: typeof import('ant-design-vue/es')['Image']
|
||||
AImagePreviewGroup: typeof import('ant-design-vue/es')['ImagePreviewGroup']
|
||||
AInput: typeof import('ant-design-vue/es')['Input']
|
||||
AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
|
||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||
Alert: typeof import('./src/components/MyUI/Alert/Alert.vue')['default']
|
||||
AList: typeof import('ant-design-vue/es')['List']
|
||||
@@ -187,5 +188,7 @@ declare module 'vue' {
|
||||
Video: typeof import('./src/components/MyUI/Video/Video.vue')['default']
|
||||
VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default']
|
||||
WarningOutlined: typeof import('@ant-design/icons-vue')['WarningOutlined']
|
||||
Waterfall: typeof import('./src/views/Photograph/WaterfallList/Waterfall.vue')['default']
|
||||
WaterfallList: typeof import('./src/views/Photograph/WaterfallList/WaterfallList.vue')['default']
|
||||
}
|
||||
}
|
||||
|
16
package.json
16
package.json
@@ -32,7 +32,7 @@
|
||||
"@types/json-stringify-safe": "^5.0.3",
|
||||
"@types/node": "^22.13.1",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@vladmandic/face-api": "^1.7.14",
|
||||
"@vladmandic/face-api": "^1.7.15",
|
||||
"@vuepic/vue-datepicker": "^11.0.1",
|
||||
"@vueuse/core": "^12.5.0",
|
||||
"@vueuse/integrations": "^12.5.0",
|
||||
@@ -44,7 +44,7 @@
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^5.6.0",
|
||||
"eslint": "9.19.0",
|
||||
"eslint": "9.20.0",
|
||||
"exifr": "^7.1.3",
|
||||
"go-captcha-vue": "^2.0.5",
|
||||
"gsap": "^3.12.7",
|
||||
@@ -66,28 +66,28 @@
|
||||
"vite-plugin-node-polyfills": "^0.23.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-dompurify-html": "^5.2.0",
|
||||
"vue-i18n": "^11.1.0",
|
||||
"vue-i18n": "^11.1.1",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-waterfall-plugin-next": "^2.6.5",
|
||||
"vue3-justified-layout": "^0.0.6",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@eslint/js": "^9.20.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"globals": "^15.14.0",
|
||||
"sass": "^1.83.4",
|
||||
"sass": "^1.84.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.23.0",
|
||||
"unplugin-vue-components": "^28.0.0",
|
||||
"vite": "^6.0.11",
|
||||
"vite": "^6.1.0",
|
||||
"vite-plugin-bundle-obfuscator": "1.4.1",
|
||||
"vite-plugin-chunk-split": "^0.5.0",
|
||||
"vue-tsc": "2.2.0"
|
||||
},
|
||||
"overrides": {
|
||||
"vite-plugin-chunk-split": {
|
||||
"vite": "^6.0.11"
|
||||
"vite": "^6.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,16 @@
|
||||
import {service} from "@/utils/alova/service.ts";
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param formData
|
||||
*/
|
||||
export const uploadFile = (formData) => {
|
||||
return service.Post('/api/auth/storage/uploads', formData, {
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
}
|
||||
},
|
||||
name: "upload-file",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -24,7 +29,8 @@ export const getFaceSamplesList = (type: number) => {
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
}
|
||||
},
|
||||
hitSource: ["modify-face-sample-name", "modify-face-sample-type"],
|
||||
});
|
||||
};
|
||||
/**
|
||||
@@ -40,7 +46,8 @@ export const modifyFaceSampleName = (id: number, face_name: string) => {
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
}
|
||||
},
|
||||
name: "modify-face-sample-name",
|
||||
});
|
||||
};
|
||||
/**
|
||||
@@ -56,7 +63,101 @@ export const modifyFaceTypeBatch = (ids: number[], face_type: number) => {
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
}
|
||||
},
|
||||
name: "modify-face-sample-type"
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 创建相册
|
||||
* @param name
|
||||
*/
|
||||
export const createAlbumApi = (name: string) => {
|
||||
return service.Post('/api/auth/storage/album/create', {
|
||||
name: name,
|
||||
}, {
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
name: "create-album",
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取相册列表
|
||||
* @param type
|
||||
* @param sort
|
||||
*/
|
||||
export const albumListApi = (type: string, sort: boolean) => {
|
||||
return service.Post('/api/auth/storage/album/list', {
|
||||
type: type,
|
||||
sort: sort,
|
||||
}, {
|
||||
cacheFor: {
|
||||
expire: 60 * 60 * 24 * 7,
|
||||
mode: "restore",
|
||||
},
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
hitSource: ["create-album", "rename-album", "delete-album"],
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 重命名相册
|
||||
* @param id
|
||||
* @param name
|
||||
*/
|
||||
export const renameAlbumApi = (id: number, name: string) => {
|
||||
return service.Post('/api/auth/storage/album/rename', {
|
||||
id: id,
|
||||
name: name,
|
||||
}, {
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
name: "rename-album",
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 删除相册
|
||||
* @param id
|
||||
*/
|
||||
export const deleteAlbumApi = (id: number) => {
|
||||
return service.Post('/api/auth/storage/album/delete', {
|
||||
id: id,
|
||||
}, {
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
name: "delete-album",
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取所有照片列表
|
||||
* @param type
|
||||
* @param sort
|
||||
* @param provider
|
||||
* @param bucket
|
||||
*/
|
||||
export const queryAllImagesApi = (type: string, sort: boolean, provider: string, bucket: string) => {
|
||||
return service.Post('/api/auth/storage/image/all/list', {
|
||||
type: type,
|
||||
sort: sort,
|
||||
provider: provider,
|
||||
bucket: bucket,
|
||||
}, {
|
||||
cacheFor: {
|
||||
expire: 60 * 60 * 24 * 7,
|
||||
mode: "restore",
|
||||
},
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
hitSource: ["upload-file"],
|
||||
});
|
||||
};
|
||||
|
BIN
src/assets/images/default-cover.png
Normal file
BIN
src/assets/images/default-cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
@@ -89,7 +89,7 @@ function toggleSelection() {
|
||||
<style scoped lang="scss">
|
||||
.check-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
@@ -100,11 +100,16 @@ function toggleSelection() {
|
||||
.check-card.selected {
|
||||
border: 1px solid rgba(125, 167, 255, 0.68);
|
||||
box-shadow: 0 0 2px rgba(77, 167, 255, 0.89);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: inline-block;
|
||||
flex: 1;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.check-card.selected .card-content {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.card-selected-icon {
|
||||
|
@@ -368,10 +368,10 @@ function onPageSizeChange(pageSize: number) {
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: @themeColor;
|
||||
border-color: #00aced;
|
||||
|
||||
.arrow-svg {
|
||||
color: @themeColor;
|
||||
color: #00aced;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -383,8 +383,8 @@ function onPageSizeChange(pageSize: number) {
|
||||
.item-active {
|
||||
// 悬浮/选中样式
|
||||
font-weight: 600;
|
||||
color: @themeColor;
|
||||
border-color: @themeColor;
|
||||
color: #00aced;
|
||||
border-color: #00aced;
|
||||
}
|
||||
|
||||
.item-disabled {
|
||||
@@ -443,7 +443,7 @@ function onPageSizeChange(pageSize: number) {
|
||||
right: 0;
|
||||
margin: auto;
|
||||
display: inline-block;
|
||||
fill: @themeColor;
|
||||
fill: #000;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
opacity: 0;
|
||||
|
@@ -11,7 +11,8 @@
|
||||
</div>
|
||||
<!-- 上传按钮 -->
|
||||
<div class="button-wrapper">
|
||||
<AButton type="text" shape="circle" size="large" class="header-menu-item-btn">
|
||||
<AButton type="text" shape="circle" size="large" class="header-menu-item-btn"
|
||||
@click="uploadStore.openUploadDrawerFn">
|
||||
<template #icon>
|
||||
<AAvatar size="default" shape="circle" :src="upload"/>
|
||||
</template>
|
||||
@@ -116,6 +117,7 @@
|
||||
</template>
|
||||
</APopover>
|
||||
</AFlex>
|
||||
<ImageUpload/>
|
||||
</AFlex>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@@ -130,7 +132,9 @@ import personalCenter from "@/assets/svgs/personal-center.svg";
|
||||
import accountSetting from "@/assets/svgs/setting.svg";
|
||||
import logout from "@/assets/svgs/logout.svg";
|
||||
import useStore from "@/store";
|
||||
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||
|
||||
const uploadStore = useStore().upload;
|
||||
const user = useStore().user;
|
||||
</script>
|
||||
|
||||
|
@@ -3,10 +3,12 @@ import localforage from 'localforage';
|
||||
interface UploadPredictResult {
|
||||
isAnime: boolean;
|
||||
objectArray: string[] | unknown[];
|
||||
landscape: 'building' | 'forest' | 'glacier' | 'mountain' | 'sea' | 'street' | 'none';
|
||||
landscape: 'building' | 'forest' | 'glacier' | 'mountain' | 'sea' | 'street' | null;
|
||||
isScreenshot: boolean;
|
||||
topCategory: string | undefined;
|
||||
exif: object | null;
|
||||
exif: object | "";
|
||||
width: number | null;
|
||||
height: number | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +20,12 @@ export const useUploadStore = defineStore(
|
||||
const predictResult = reactive<UploadPredictResult>({
|
||||
isAnime: false,
|
||||
objectArray: [],
|
||||
landscape: 'none',
|
||||
landscape: null,
|
||||
isScreenshot: false,
|
||||
topCategory: '',
|
||||
exif: {}
|
||||
exif: "",
|
||||
width: null,
|
||||
height: null,
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -37,10 +41,10 @@ export const useUploadStore = defineStore(
|
||||
function clearPredictResult() {
|
||||
predictResult.isAnime = false;
|
||||
predictResult.objectArray = [];
|
||||
predictResult.landscape = 'none';
|
||||
predictResult.landscape = null;
|
||||
predictResult.isScreenshot = false;
|
||||
predictResult.topCategory = '';
|
||||
predictResult.exif = {};
|
||||
predictResult.exif = "";
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,12 +1,28 @@
|
||||
<template>
|
||||
<div class="phoalbum">
|
||||
<div class="phoalbum-header">
|
||||
<AButton type="primary" shape="round" size="middle">
|
||||
<template #icon>
|
||||
<PlusSquareOutlined/>
|
||||
<APopover placement="bottom" trigger="click">
|
||||
<AButton type="primary" shape="round" size="middle">
|
||||
<template #icon>
|
||||
<PlusSquareOutlined/>
|
||||
</template>
|
||||
创建相册
|
||||
</AButton>
|
||||
<template #content>
|
||||
<span style="color: #999; font-size: 15px;">创建相册</span>
|
||||
<AInputGroup compact style="display: flex;flex-direction: row;margin-top: 15px">
|
||||
<AInput placeholder="请填写相册名称" class="phoalbum-create-input"
|
||||
v-model:value="albumNameValue" size="large">
|
||||
<template #suffix>
|
||||
<AButton type="text" @click.prevent size="middle" style="color: #0e87cc" @click="createAlbumSubmit">
|
||||
确认
|
||||
</AButton>
|
||||
</template>
|
||||
</AInput>
|
||||
</AInputGroup>
|
||||
</template>
|
||||
创建相册
|
||||
</AButton>
|
||||
|
||||
</APopover>
|
||||
<ADropdown>
|
||||
<AButton type="default" shape="round" size="middle">
|
||||
<template #icon>
|
||||
@@ -15,9 +31,9 @@
|
||||
排序
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu>
|
||||
<AMenuItem key="1">按时间排序</AMenuItem>
|
||||
<AMenuItem key="2">按名称排序</AMenuItem>
|
||||
<AMenu selectable :selectedKeys="[selecetedKey]" @select="handleSelect">
|
||||
<AMenuItem :key="true">按时间排序</AMenuItem>
|
||||
<AMenuItem :key="false">按名称排序</AMenuItem>
|
||||
</AMenu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
@@ -33,42 +49,64 @@
|
||||
</div>
|
||||
<div class="phoalbum-content">
|
||||
<ATabs size="small" :tabBarGutter="50" type="line" tabPosition="top" :tabBarStyle="{position:'unset'}"
|
||||
style="width: 100%;">
|
||||
style="width: 100%;" @change="handleTabChange">
|
||||
<template #rightExtra>
|
||||
<span style="color: #999; font-size: 12px;">已全部加载,共 0 个相册</span>
|
||||
<span
|
||||
style="color: #999; font-size: 12px;">已全部加载,共 {{ albumList ? albumList.length : 0 }} 个相册</span>
|
||||
</template>
|
||||
<ATabPane key="1" tab="全部相册">
|
||||
<div class="phoalbum-item-container">
|
||||
<div class="phoalbum-item" @mouseover="isHovered = true" @mouseleave="isHovered = false">
|
||||
<PhotoStack :src="'/test/1.png'" default-src=""/>
|
||||
<div class="phoalbum-item-info">
|
||||
<span class="phoalbum-item-name">我的相册</span>
|
||||
<span class="phoalbum-item-date">2022-01-01</span>
|
||||
</div>
|
||||
<div class="phoalbum-item-operation" :class="{ 'fade-in': isHovered, 'fade-out': !isHovered }">
|
||||
<ADropdown trigger="click">
|
||||
<AButton type="text" shape="circle" size="small">
|
||||
<template #icon>
|
||||
<AAvatar shape="circle" size="small" :src="more"/>
|
||||
<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"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="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">
|
||||
<AButton type="text" shape="circle" size="small">
|
||||
<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>
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu>
|
||||
<AMenuItem key="1">重命名相册</AMenuItem>
|
||||
<AMenuItem key="2">分享相册</AMenuItem>
|
||||
<AMenuItem key="3">删除相册</AMenuItem>
|
||||
<AMenuItem key="4">下载相册</AMenuItem>
|
||||
</AMenu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
</ADropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ASpin>
|
||||
</ATabPane>
|
||||
<ATabPane key="2" tab="我的相册">
|
||||
<ATabPane key="1" tab="我的相册">
|
||||
|
||||
</ATabPane>
|
||||
<ATabPane key="3" tab="收藏相册">
|
||||
<ATabPane key="2" tab="收藏相册">
|
||||
|
||||
</ATabPane>
|
||||
</ATabs>
|
||||
</div>
|
||||
@@ -76,11 +114,103 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import more from "@/assets/svgs/more.svg";
|
||||
import {albumListApi, createAlbumApi, deleteAlbumApi, renameAlbumApi} from "@/api/storage";
|
||||
import {message} from "ant-design-vue";
|
||||
import default_cover from "@/assets/images/default-cover.png";
|
||||
|
||||
const isHovered = ref<number | null>(null);
|
||||
|
||||
const isHovered = ref<boolean>(false);
|
||||
const albumNameValue = ref<string>("未命名相册");
|
||||
const albumRenameValue = ref<string>("");
|
||||
|
||||
const selecetedKey = ref<boolean>(true);
|
||||
|
||||
const albumList = ref<any[]>([]);
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* 创建相册
|
||||
*/
|
||||
async function createAlbumSubmit() {
|
||||
if (albumNameValue.value.trim() === "") {
|
||||
message.error("相册名称不能为空");
|
||||
return;
|
||||
}
|
||||
const res: any = await createAlbumApi(albumNameValue.value);
|
||||
if (res && res.code === 200) {
|
||||
albumNameValue.value = "未命名相册";
|
||||
await getAlbumList("0", selecetedKey.value);
|
||||
} else {
|
||||
message.error("创建相册失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择排序方式
|
||||
* @param key
|
||||
*/
|
||||
async function handleSelect({key}) {
|
||||
selecetedKey.value = key;
|
||||
await getAlbumList("0", key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换相册列表
|
||||
* @param activeKey
|
||||
*/
|
||||
async function handleTabChange(activeKey: string) {
|
||||
await getAlbumList(activeKey, selecetedKey.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相册列表
|
||||
* @param type
|
||||
* @param sort
|
||||
*/
|
||||
async function getAlbumList(type: string = "0", sort: boolean = true) {
|
||||
albumList.value = [];
|
||||
loading.value = true;
|
||||
const res: any = await albumListApi(type, sort);
|
||||
if (res && res.code === 200) {
|
||||
albumList.value = res.data.albums;
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名相册
|
||||
* @param id
|
||||
* @param name
|
||||
*/
|
||||
async function renameAlbum(id: number, name: string) {
|
||||
if (name.trim() === "") {
|
||||
message.warning("相册名称不能为空");
|
||||
return;
|
||||
}
|
||||
const res: any = await renameAlbumApi(id, name);
|
||||
if (res && res.code === 200) {
|
||||
albumRenameValue.value = "";
|
||||
await getAlbumList("0", selecetedKey.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除相册
|
||||
* @param id
|
||||
*/
|
||||
async function deleteAlbum(id: number) {
|
||||
const res: any = await deleteAlbumApi(id);
|
||||
if (res && res.code === 200) {
|
||||
await getAlbumList("0", selecetedKey.value);
|
||||
} else {
|
||||
message.error("删除相册失败");
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getAlbumList("0", selecetedKey.value);
|
||||
});
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@@ -120,9 +250,10 @@ const isHovered = ref<boolean>(false);
|
||||
.phoalbum-item-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 20px;
|
||||
gap: 40px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
@@ -181,4 +312,9 @@ const isHovered = ref<boolean>(false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.phoalbum-create-popover-text {
|
||||
//font-size: 13px;
|
||||
color: rgba(102, 102, 102, 0.87);
|
||||
}
|
||||
</style>
|
||||
|
@@ -63,54 +63,44 @@
|
||||
<template #rightExtra>
|
||||
<ASwitch size="small" v-model:checked="switchValue"/>
|
||||
</template>
|
||||
<ATabPane key="1" tab="全部">
|
||||
<div style="width:100%;height:100%;" v-if="images.length !== 0">
|
||||
<span style="margin-left: 10px;font-size: 13px">2024年12月27日 星期日</span>
|
||||
<AImagePreviewGroup>
|
||||
<Waterfall :list="images"
|
||||
:backgroundColor="`transparent`"
|
||||
:width="400"
|
||||
:gutter="15"
|
||||
align="left"
|
||||
:lazyload="true"
|
||||
:animationDelay="300"
|
||||
:animationDuration="1000"
|
||||
:animationCancel="false"
|
||||
:hasAroundGutter="true"
|
||||
rowKey="id"
|
||||
:imgSelector="'src'"
|
||||
:loadProps="loadProps"
|
||||
:breakpoints="breakpoints">
|
||||
<template #default="{ item, url, index }">
|
||||
<CheckCard :key="index"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="url">
|
||||
<AImage :src="url"
|
||||
:alt="item.title"
|
||||
:key="index"
|
||||
:previewMask="false"
|
||||
loading="lazy"/>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Waterfall>
|
||||
</AImagePreviewGroup>
|
||||
<ATabPane key="image" tab="全部">
|
||||
<div style="width:100%;height:100%;">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 14px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
||||
<template #default="{ item }">
|
||||
<CheckCard :key="index"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.url"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
style="height: 200px"
|
||||
:previewMask="false"
|
||||
loading="lazy"/>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
</ATabPane>
|
||||
<ATabPane key="2" tab="视频">
|
||||
<ATabPane key="video" tab="视频">
|
||||
<div style="width:100%;height:100%;">
|
||||
|
||||
</div>
|
||||
</ATabPane>
|
||||
<ATabPane key="3" tab="动图">
|
||||
<ATabPane key="gif" tab="动图">
|
||||
|
||||
</ATabPane>
|
||||
<ATabPane key="4" tab="截图">
|
||||
<ATabPane key="screenshot" tab="截图">
|
||||
|
||||
</ATabPane>
|
||||
</ATabs>
|
||||
@@ -120,64 +110,36 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {Waterfall} from 'vue-waterfall-plugin-next';
|
||||
import 'vue-waterfall-plugin-next/dist/style.css';
|
||||
import loading from '@/assets/gif/loading.gif';
|
||||
import error from '@/assets/svgs/no-image.svg';
|
||||
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||
import useStore from "@/store";
|
||||
import {queryAllImagesApi} from "@/api/storage";
|
||||
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const switchValue = ref<boolean>(false);
|
||||
const upload = useStore().upload;
|
||||
const breakpoints = reactive({
|
||||
breakpoints: {
|
||||
1200: {
|
||||
// 当屏幕宽度小于等于1200
|
||||
rowPerView: 4,
|
||||
},
|
||||
800: {
|
||||
// 当屏幕宽度小于等于800
|
||||
rowPerView: 3,
|
||||
},
|
||||
500: {
|
||||
// 当屏幕宽度小于等于500
|
||||
rowPerView: 2,
|
||||
},
|
||||
},
|
||||
});
|
||||
const loadProps = reactive({
|
||||
loading,
|
||||
error,
|
||||
ratioCalculator: (_width: number, _height: number) => {
|
||||
// 我设置了最小宽高比
|
||||
const minRatio = 3 / 4;
|
||||
const maxRatio = 4 / 3;
|
||||
return Math.random() > 0.5 ? minRatio : maxRatio;
|
||||
},
|
||||
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
|
||||
const images = ref<any[]>([]);
|
||||
|
||||
function loadImages() {
|
||||
for (let i = 1; i < 10; i++) {
|
||||
images.value.push({
|
||||
title: `image-${i}`,
|
||||
link: '',
|
||||
src: `https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/${i}.jpg`,
|
||||
tag: '全部',
|
||||
date: '2022-01-01',
|
||||
});
|
||||
images.value.push({
|
||||
title: `image-${i}`,
|
||||
link: '',
|
||||
src: `/test/${i}.png`,
|
||||
});
|
||||
|
||||
async function getAllImages() {
|
||||
const res: any = await queryAllImagesApi("image", false, "ali", "schisandra-album");
|
||||
if (res && res.code === 200) {
|
||||
console.log(res);
|
||||
images.value = res.data.records;
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => { // 组件已完成响应式状态设置,但未创建DOM节点
|
||||
loadImages();
|
||||
|
||||
onMounted(() => {
|
||||
getAllImages();
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
@@ -104,10 +104,12 @@ async function beforeUpload(file: File, fileList: File[]) {
|
||||
const compressedFile = await imageCompression(file, options);
|
||||
// 创建图片对象
|
||||
image.src = URL.createObjectURL(compressedFile);
|
||||
|
||||
image.addEventListener('webglcontextlost', (_event) => {
|
||||
window.location.reload();
|
||||
});
|
||||
// 获取图片宽高
|
||||
image.onload = () => {
|
||||
const {width, height} = image;
|
||||
upload.predictResult.width = width;
|
||||
upload.predictResult.height = height;
|
||||
};
|
||||
|
||||
// 更新进度条函数,逐步增加
|
||||
const smoothUpdateProgress = async (targetPercent, duration) => {
|
||||
@@ -145,7 +147,7 @@ async function beforeUpload(file: File, fileList: File[]) {
|
||||
// 提取 EXIF 数据
|
||||
const exifData = await extractAllExifData(file);
|
||||
if (exifData) {
|
||||
upload.predictResult.exif = exifData;
|
||||
upload.predictResult.exif = exifData ? exifData : "";
|
||||
}
|
||||
|
||||
// 判断是否为截图
|
||||
@@ -183,7 +185,7 @@ async function beforeUpload(file: File, fileList: File[]) {
|
||||
const classSet = new Set(cocoResults.map(result => result.class));
|
||||
upload.predictResult.objectArray = Array.from(classSet);
|
||||
}
|
||||
upload.predictResult.landscape = landscape as 'building' | 'forest' | 'glacier' | 'mountain' | 'sea' | 'street' | 'none';
|
||||
upload.predictResult.landscape = landscape as 'building' | 'forest' | 'glacier' | 'mountain' | 'sea' | 'street' | null;
|
||||
|
||||
predicting.value = false;
|
||||
return true;
|
||||
@@ -193,8 +195,6 @@ async function beforeUpload(file: File, fileList: File[]) {
|
||||
predicting.value = false;
|
||||
progressPercent.value = 0; // 重置进度条
|
||||
return false;
|
||||
} finally {
|
||||
image.removeEventListener('webglcontextlost', () => void 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,8 @@ async function customUploadRequest(file: any) {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file.file);
|
||||
formData.append("data", JSON.stringify({
|
||||
provider: 'ali',
|
||||
bucket: 'schisandra',
|
||||
fileType: file.file.type,
|
||||
...upload.predictResult,
|
||||
}));
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="recent-upload">
|
||||
<div class="photo-header">
|
||||
<AButton type="primary" shape="round" size="middle">
|
||||
<AButton type="primary" shape="round" size="middle" @click="upload.openUploadDrawerFn()">
|
||||
<template #icon>
|
||||
<PlusOutlined/>
|
||||
</template>
|
||||
@@ -51,6 +51,7 @@
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<ImageUpload/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@@ -58,7 +59,10 @@ import {Waterfall} from 'vue-waterfall-plugin-next';
|
||||
import 'vue-waterfall-plugin-next/dist/style.css';
|
||||
import loading from '@/assets/gif/loading.gif';
|
||||
import error from '@/assets/svgs/no-image.svg';
|
||||
import useStore from "@/store";
|
||||
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||
|
||||
const upload = useStore().upload;
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const breakpoints = reactive({
|
||||
breakpoints: {
|
||||
|
Reference in New Issue
Block a user