✨ add apis
This commit is contained in:
26
components.d.ts
vendored
26
components.d.ts
vendored
@@ -31,33 +31,25 @@ declare module 'vue' {
|
||||
AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
|
||||
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||
AList: typeof import('ant-design-vue/es')['List']
|
||||
AListItem: typeof import('ant-design-vue/es')['ListItem']
|
||||
AllPhoto: typeof import('./src/views/Photograph/AllPhoto/AllPhoto.vue')['default']
|
||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||
AMenuItemGroup: typeof import('ant-design-vue/es')['MenuItemGroup']
|
||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||
AnimatedNature: typeof import('./src/components/AnimatedNature/AnimatedNature.vue')['default']
|
||||
APagination: typeof import('ant-design-vue/es')['Pagination']
|
||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||
APopover: typeof import('ant-design-vue/es')['Popover']
|
||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||
AQrcode: typeof import('ant-design-vue/es')['QRCode']
|
||||
ARadio: typeof import('ant-design-vue/es')['Radio']
|
||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||
ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
|
||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||
ASkeleton: typeof import('ant-design-vue/es')['Skeleton']
|
||||
ASpace: typeof import('ant-design-vue/es')['Space']
|
||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
||||
ATable: typeof import('ant-design-vue/es')['Table']
|
||||
ATabPane: typeof import('ant-design-vue/es')['TabPane']
|
||||
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
||||
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||
AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
|
||||
@@ -67,28 +59,25 @@ declare module 'vue' {
|
||||
Card3D: typeof import('./src/components/Card3D/Card3D.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']
|
||||
CloseOutlined: typeof import('@ant-design/icons-vue')['CloseOutlined']
|
||||
Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default']
|
||||
CloudUploadOutlined: typeof import('@ant-design/icons-vue')['CloudUploadOutlined']
|
||||
CommentInput: typeof import('./src/components/CommentReply/src/CommentInput/CommentInput.vue')['default']
|
||||
CommentList: typeof import('./src/components/CommentReply/src/CommentList/CommentList.vue')['default']
|
||||
CommentReply: typeof import('./src/components/CommentReply/index.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']
|
||||
DownloadOutlined: typeof import('@ant-design/icons-vue')['DownloadOutlined']
|
||||
DownOutlined: typeof import('@ant-design/icons-vue')['DownOutlined']
|
||||
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
|
||||
EyeInvisibleOutlined: typeof import('@ant-design/icons-vue')['EyeInvisibleOutlined']
|
||||
EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined']
|
||||
FileImageOutlined: typeof import('@ant-design/icons-vue')['FileImageOutlined']
|
||||
Folder: typeof import('./src/components/Folder/Folder.vue')['default']
|
||||
ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default']
|
||||
GradientText: typeof import('./src/components/MyUI/GradientText/GradientText.vue')['default']
|
||||
ImageShare: typeof import('./src/views/ImageShare/ImageShare.vue')['default']
|
||||
ImageShare: typeof import('./src/views/Share/ImageShare/ImageShare.vue')['default']
|
||||
ImageToolbar: typeof import('./src/views/Photograph/ImageToolbar/ImageToolbar.vue')['default']
|
||||
ImageUpload: typeof import('./src/views/Photograph/ImageUpload/ImageUpload.vue')['default']
|
||||
InboxOutlined: typeof import('@ant-design/icons-vue')['InboxOutlined']
|
||||
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
|
||||
LeftOutlined: typeof import('@ant-design/icons-vue')['LeftOutlined']
|
||||
LinkOutlined: typeof import('@ant-design/icons-vue')['LinkOutlined']
|
||||
@@ -110,12 +99,14 @@ declare module 'vue' {
|
||||
Phoalbum: typeof import('./src/views/Album/Phoalbum/Phoalbum.vue')['default']
|
||||
PhoalbumDetail: typeof import('./src/views/Album/Phoalbum/PhoalbumDetail.vue')['default']
|
||||
PhoalbumList: typeof import('./src/views/Album/Phoalbum/PhoalbumList.vue')['default']
|
||||
PhoneUpload: typeof import('./src/views/Phone/UpscalePhoneUpload/PhoneUpload.vue')['default']
|
||||
PhoneOutlined: typeof import('@ant-design/icons-vue')['PhoneOutlined']
|
||||
PhotoStack: typeof import('./src/components/PhotoStack/PhotoStack.vue')['default']
|
||||
PlusOutlined: typeof import('@ant-design/icons-vue')['PlusOutlined']
|
||||
PlusSquareOutlined: typeof import('@ant-design/icons-vue')['PlusSquareOutlined']
|
||||
QrcodeOutlined: typeof import('@ant-design/icons-vue')['QrcodeOutlined']
|
||||
QRLogin: typeof import('./src/views/QRLogin/QRLogin.vue')['default']
|
||||
QRLoginFooter: typeof import('./src/views/QRLogin/QRLoginFooter.vue')['default']
|
||||
QuestionCircleOutlined: typeof import('@ant-design/icons-vue')['QuestionCircleOutlined']
|
||||
Rate: typeof import('./src/components/MyUI/Rate/Rate.vue')['default']
|
||||
RecentUpload: typeof import('./src/views/Photograph/RecentUpload/RecentUpload.vue')['default']
|
||||
RecyclingBin: typeof import('./src/views/RecyclingBin/RecyclingBin.vue')['default']
|
||||
@@ -126,10 +117,11 @@ declare module 'vue' {
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SafetyOutlined: typeof import('@ant-design/icons-vue')['SafetyOutlined']
|
||||
SearchOutlined: typeof import('@ant-design/icons-vue')['SearchOutlined']
|
||||
SendOutlined: typeof import('@ant-design/icons-vue')['SendOutlined']
|
||||
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']
|
||||
ShareSidebar: typeof import('./src/views/Share/ShareViewList/ShareSidebar.vue')['default']
|
||||
ShareUpload: typeof import('./src/views/Share/ImageShare/ShareUpload.vue')['default']
|
||||
ShareViewList: typeof import('./src/views/Share/ShareViewList/index.vue')['default']
|
||||
Spin: typeof import('./src/components/MyUI/Spin/Spin.vue')['default']
|
||||
StarButton: typeof import('./src/components/StarButton/StarButton.vue')['default']
|
||||
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
|
||||
@@ -138,11 +130,11 @@ declare module 'vue' {
|
||||
ThingAlbumList: typeof import('./src/views/Album/ThingAlbum/ThingAlbumList.vue')['default']
|
||||
Tooltip: typeof import('./src/components/MyUI/Tooltip/Tooltip.vue')['default']
|
||||
UploadImage: typeof import('./src/views/Upscale/UploadImage.vue')['default']
|
||||
UploadOutlined: typeof import('@ant-design/icons-vue')['UploadOutlined']
|
||||
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']
|
||||
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
|
||||
VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default']
|
||||
WarningOutlined: typeof import('@ant-design/icons-vue')['WarningOutlined']
|
||||
}
|
||||
}
|
||||
|
14
package.json
14
package.json
@@ -10,7 +10,7 @@
|
||||
"docker-build": "docker build -t schisandra/schisandra-cloud-album-front ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@alova/adapter-axios": "^2.0.12",
|
||||
"@alova/adapter-axios": "^2.0.13",
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@intlify/eslint-plugin-vue-i18n": "^3.2.0",
|
||||
"@mediapipe/face_detection": "^0.4.1646425229",
|
||||
@@ -27,10 +27,10 @@
|
||||
"@tensorflow/tfjs-backend-webgpu": "^4.22.0",
|
||||
"@tensorflow/tfjs-converter": "^4.22.0",
|
||||
"@tensorflow/tfjs-core": "^4.22.0",
|
||||
"@types/animejs": "^3.1.12",
|
||||
"@types/animejs": "^3.1.13",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/json-stringify-safe": "^5.0.3",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/node": "^22.13.5",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@vladmandic/face-api": "^1.7.15",
|
||||
"@vuepic/vue-datepicker": "^11.0.1",
|
||||
@@ -44,7 +44,7 @@
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^5.6.0",
|
||||
"eslint": "9.20.1",
|
||||
"eslint": "9.21.0",
|
||||
"exifr": "^7.1.3",
|
||||
"go-captcha-vue": "^2.0.6",
|
||||
"gsap": "^3.12.7",
|
||||
@@ -70,13 +70,13 @@
|
||||
"vue-i18n": "^11.1.1",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue3-justified-layout": "^0.0.6",
|
||||
"ws": "^8.18.0"
|
||||
"ws": "^8.18.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.20.0",
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"globals": "^15.15.0",
|
||||
"globals": "^16.0.0",
|
||||
"sass": "^1.85.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
|
@@ -14,12 +14,12 @@ export const shareImageUploadApi = (formData) => {
|
||||
};
|
||||
/**
|
||||
* 查询分享图片列表
|
||||
* @param share_code
|
||||
* @param invite_code
|
||||
* @param access_password
|
||||
*/
|
||||
export const queryShareImageApi = (share_code: string, access_password: string) => {
|
||||
export const queryShareImageApi = (invite_code: string, access_password: string) => {
|
||||
return service.Post('/api/auth/share/image/list', {
|
||||
share_code: share_code,
|
||||
invite_code: invite_code,
|
||||
access_password: access_password,
|
||||
}, {
|
||||
meta: {
|
||||
@@ -41,3 +41,36 @@ export const queryShareRecordListApi = (dataRequest: string[]) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 查询分享信息
|
||||
* @param invite_code
|
||||
*/
|
||||
export const queryShareInfoApi = (invite_code: string) => {
|
||||
return service.Post('/api/auth/share/info', {
|
||||
invite_code: invite_code,
|
||||
}, {
|
||||
cacheFor: {
|
||||
expire: 60, //60 * 60 * 24 * 7
|
||||
mode: "restore",
|
||||
},
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 查询分享概览
|
||||
*/
|
||||
export const queryShareOverviewApi = () => {
|
||||
return service.Post('/api/auth/share/overview', {}, {
|
||||
cacheFor: {
|
||||
expire: 60, //60 * 60 * 24 * 7
|
||||
mode: "restore",
|
||||
},
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@@ -30,7 +30,7 @@ export const getFaceSamplesList = (type: number) => {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
hitSource: ["modify-face-sample-name", "modify-face-sample-type"],
|
||||
hitSource: ["modify-face-sample-name", "modify-face-sample-type", "delete-images"],
|
||||
});
|
||||
};
|
||||
/**
|
||||
@@ -146,6 +146,8 @@ export const queryAlbumDetailListApi = (id: number, provider: string, bucket: st
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
name: "album-detail-list",
|
||||
hitSource: ["upload-file", "delete-images"],
|
||||
});
|
||||
};
|
||||
|
||||
@@ -203,7 +205,7 @@ export const queryAllImagesApi = (type: string, sort: boolean, provider: string,
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
hitSource: ["upload-file"],
|
||||
hitSource: ["upload-file", "delete-images"],
|
||||
});
|
||||
};
|
||||
|
||||
@@ -221,7 +223,7 @@ export const queryRecentImagesApi = () => {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
hitSource: ["upload-file"],
|
||||
hitSource: ["upload-file", "delete-images"],
|
||||
});
|
||||
};
|
||||
/**
|
||||
@@ -240,7 +242,7 @@ export const queryLocationAlbumApi = (provider: string, bucket: string) => {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
hitSource: ["upload-file"],
|
||||
hitSource: ["upload-file", "delete-images"],
|
||||
});
|
||||
};
|
||||
/**
|
||||
@@ -263,7 +265,7 @@ export const queryLocationDetailListApi = (id: number, provider: string, bucket:
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
hitSource: ["upload-file"],
|
||||
hitSource: ["upload-file", "delete-images"],
|
||||
});
|
||||
};
|
||||
|
||||
@@ -283,7 +285,7 @@ export const queryThingAlbumApi = (provider: string, bucket: string) => {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
hitSource: ["upload-file"],
|
||||
hitSource: ["upload-file", "delete-images"],
|
||||
});
|
||||
};
|
||||
|
||||
@@ -307,7 +309,7 @@ export const queryThingDetailListApi = (tag_name: string, provider: string, buck
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
hitSource: ["upload-file"],
|
||||
hitSource: ["upload-file", "delete-images"],
|
||||
});
|
||||
};
|
||||
|
||||
@@ -327,6 +329,7 @@ export const getSingleImageApi = (id: number) => {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
name: "single-image-url",
|
||||
});
|
||||
};
|
||||
/**
|
||||
@@ -342,7 +345,68 @@ export const getStorageConfigListApi = () => {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
name: "storage-config-list",
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 查询删除记录
|
||||
* @param provider
|
||||
* @param bucket
|
||||
*/
|
||||
export const getDeletedRecordApi = (provider: string, bucket: string) => {
|
||||
return service.Post('/api/auth/storage/delete/record', {
|
||||
provider: provider,
|
||||
bucket: bucket,
|
||||
}, {
|
||||
cacheFor: {
|
||||
expire: 60 * 60 * 24 * 7,
|
||||
mode: "restore",
|
||||
},
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
name: "deleted-record",
|
||||
hitSource: ["upload-file", "delete-images"],
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 删除照片
|
||||
* @param ids
|
||||
* @param provider
|
||||
* @param bucket
|
||||
*/
|
||||
export const deletedImagesApi = (ids: number[], provider: string, bucket: string) => {
|
||||
return service.Post('/api/auth/storage/image/delete', {
|
||||
ids: ids,
|
||||
provider: provider,
|
||||
bucket: bucket,
|
||||
}, {
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
name: "delete-images",
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取存储桶容量
|
||||
* @param provider
|
||||
* @param bucket
|
||||
*/
|
||||
export const getBucketCapacityApi = (provider: string, bucket: string) => {
|
||||
return service.Post('/api/auth/storage/bucket/capacity', {
|
||||
provider: provider,
|
||||
bucket: bucket,
|
||||
}, {
|
||||
cacheFor: {
|
||||
expire: 60 * 60 * 24,
|
||||
mode: "restore",
|
||||
},
|
||||
meta: {
|
||||
ignoreToken: false,
|
||||
signature: false,
|
||||
},
|
||||
name: "delete-images",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
BIN
src/assets/images/original.png
Normal file
BIN
src/assets/images/original.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 5.8 KiB |
@@ -74,11 +74,15 @@
|
||||
<div class="sidebar-bottom">
|
||||
<ACard :bordered="false" style="box-shadow: none">
|
||||
<Folder/>
|
||||
<span class="sidebar-folder-text-title">30% In-used</span>
|
||||
<AProgress :percent="30" size="small" :showInfo="false" style="width: 150px"/>
|
||||
<span class="sidebar-folder-text-title">{{ bucketCapacityInfo?.percentage }}% In-used
|
||||
<ATooltip placement="right" title="每天同步一次" :color="'rgba(191,189,189,0.83)'">
|
||||
<QuestionCircleOutlined/>
|
||||
</ATooltip>
|
||||
</span>
|
||||
<AProgress :percent="bucketCapacityInfo?.percentage" size="small" :showInfo="false" style="width: 150px"/>
|
||||
<AFlex :vertical="false" align="center" justify="space-between" style="width: 150px">
|
||||
<span class="sidebar-folder-info-text1">500G</span>
|
||||
<span class="sidebar-folder-info-text2">500G</span>
|
||||
<span class="sidebar-folder-info-text1">{{ bucketCapacityInfo?.used }}</span>
|
||||
<span class="sidebar-folder-info-text2">{{ bucketCapacityInfo?.capacity }}</span>
|
||||
</AFlex>
|
||||
</ACard>
|
||||
</div>
|
||||
@@ -97,11 +101,13 @@ import Folder from "@/components/Folder/Folder.vue";
|
||||
import ai from '@/assets/svgs/ai.svg';
|
||||
import share from '@/assets/svgs/share.svg';
|
||||
import useStore from "@/store";
|
||||
import {getBucketCapacityApi} from "@/api/storage";
|
||||
|
||||
const {t} = useI18n();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const menu = useStore().menu;
|
||||
const upload = useStore().upload;
|
||||
|
||||
/**
|
||||
* handle click event of menu item
|
||||
@@ -117,6 +123,14 @@ const menuCSSStyle: any = reactive({
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
const bucketCapacityInfo = ref<any>();
|
||||
|
||||
async function getBucketCapacity() {
|
||||
const res: any = await getBucketCapacityApi(upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
bucketCapacityInfo.value = res.data;
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
@@ -135,9 +149,10 @@ function scrollToSelectedMenuItem() {
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
menu.currentMenu = route.path.replace('/main', '').split('/').slice(0, 3).join('/').substring(1);
|
||||
scrollToSelectedMenuItem();
|
||||
await getBucketCapacity();
|
||||
});
|
||||
router.afterEach((_to) => {
|
||||
menu.currentMenu = route.path.replace('/main', '').split('/').slice(0, 3).join('/').substring(1);
|
||||
|
@@ -1,12 +1,14 @@
|
||||
import photo from "@/router/modules/photos.ts";
|
||||
import albums from "@/router/modules/albums.ts";
|
||||
import recycling_bin from "@/router/modules/recycling_bin.ts";
|
||||
import share from "@/router/modules/share.ts";
|
||||
import upscale from "@/router/modules/upscale.ts";
|
||||
|
||||
export default [
|
||||
{
|
||||
path: '/main',
|
||||
name: 'main',
|
||||
redirect: '/main/photos',
|
||||
redirect: '/main/photo/all',
|
||||
component: () => import('@/views/Main/MainPage.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
@@ -16,25 +18,18 @@ export default [
|
||||
...photo,
|
||||
...albums,
|
||||
...recycling_bin,
|
||||
{
|
||||
path: '/main/photo/upscale',
|
||||
name: 'upscale',
|
||||
component: () => import('@/views/Upscale/index.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '图像修复'
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/main/photo/share',
|
||||
name: 'share',
|
||||
component: () => import('@/views/ImageShare/ImageShare.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '快传'
|
||||
}
|
||||
}
|
||||
...share,
|
||||
...upscale,
|
||||
|
||||
]
|
||||
}, {
|
||||
path: '/main/share/list/:id',
|
||||
name: 'share-list',
|
||||
component: () => import('@/views/Share/ShareViewList/index.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '分享列表'
|
||||
},
|
||||
}
|
||||
|
||||
];
|
||||
|
11
src/router/modules/share.ts
Normal file
11
src/router/modules/share.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export default [
|
||||
{
|
||||
path: '/main/photo/share',
|
||||
name: 'share',
|
||||
component: () => import('@/views/Share/ImageShare/ImageShare.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '快传'
|
||||
}
|
||||
}
|
||||
];
|
11
src/router/modules/upscale.ts
Normal file
11
src/router/modules/upscale.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export default [
|
||||
{
|
||||
path: '/main/photo/upscale',
|
||||
name: 'upscale',
|
||||
component: () => import('@/views/Upscale/index.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '图像修复'
|
||||
},
|
||||
},
|
||||
];
|
@@ -6,6 +6,7 @@ import {useWebSocketStore} from "@/store/modules/websocketStore.ts";
|
||||
import {useUpscaleStore} from "@/store/modules/upscaleStore.ts";
|
||||
import {useMenuStore} from "@/store/modules/menuStore.ts";
|
||||
import {useUploadStore} from "@/store/modules/uploadStore.ts";
|
||||
import {useImageStore} from "@/store/modules/imageStore.ts";
|
||||
|
||||
export default function useStore() {
|
||||
return {
|
||||
@@ -17,5 +18,6 @@ export default function useStore() {
|
||||
upscale: useUpscaleStore(),
|
||||
menu: useMenuStore(),
|
||||
upload: useUploadStore(),
|
||||
image: useImageStore(),
|
||||
};
|
||||
}
|
||||
|
52
src/store/modules/imageStore.ts
Normal file
52
src/store/modules/imageStore.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import {ImageList} from "@/types/image";
|
||||
|
||||
export const useImageStore = defineStore(
|
||||
'image',
|
||||
() => {
|
||||
const selected = ref<number[]>([]);
|
||||
const tabActiveKey = ref<string>("-1");
|
||||
const tabMap = reactive({
|
||||
"-1": "全部相册",
|
||||
"0": "我的相册",
|
||||
"1": "我的分享",
|
||||
"2": "我的收藏",
|
||||
});
|
||||
const homeTabActiveKey = ref<string>("all");
|
||||
const homeTabMap = reactive({
|
||||
"all": "全部",
|
||||
"video": "视频",
|
||||
"gif": "动图",
|
||||
"screenshot": "截图",
|
||||
});
|
||||
|
||||
/**
|
||||
* 统计图片总数
|
||||
* @param imageList 图片列表数据
|
||||
* @returns 图片总数
|
||||
*/
|
||||
function countTotalImages(imageList: ImageList): number {
|
||||
if (!imageList) {
|
||||
return 0;
|
||||
}
|
||||
return imageList.reduce((total, record) => total + record.list.length, 0);
|
||||
}
|
||||
|
||||
return {
|
||||
selected,
|
||||
tabActiveKey,
|
||||
tabMap,
|
||||
homeTabMap,
|
||||
homeTabActiveKey,
|
||||
countTotalImages
|
||||
};
|
||||
},
|
||||
{
|
||||
// 开启数据持久化
|
||||
persistedState: {
|
||||
persist: true,
|
||||
storage: localStorage,
|
||||
key: 'image',
|
||||
includePaths: ["tabActiveKey", "tabMap", "homeTabActiveKey", "homeTabMap"],
|
||||
}
|
||||
}
|
||||
);
|
@@ -84,7 +84,13 @@ export const useAuthStore = defineStore(
|
||||
message.success(t('login.loginSuccess'));
|
||||
window.removeEventListener("message", messageHandler);
|
||||
setTimeout(() => {
|
||||
router.push('/main/photo/all');
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const redirect = currentUrl.searchParams.get('redirect');
|
||||
if (redirect) {
|
||||
router.push(redirect);
|
||||
} else {
|
||||
router.push('/main/photo/all');
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
message.error(t('login.loginError'));
|
||||
|
16
src/types/image.d.ts
vendored
Normal file
16
src/types/image.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
export interface Image {
|
||||
id: number;
|
||||
file_name: string;
|
||||
url: string;
|
||||
width: number;
|
||||
height: number;
|
||||
created_at: string;
|
||||
thumbnail: string;
|
||||
}
|
||||
|
||||
export interface ImageRecord {
|
||||
date: string;
|
||||
list: Image[];
|
||||
}
|
||||
|
||||
export type ImageList = ImageRecord[];
|
@@ -4,14 +4,15 @@
|
||||
<div class="location-detail-content-nav">
|
||||
<AButton size="large" type="text" class="location-detail-content-nav-title" @click="goBack">地点</AButton>
|
||||
<span class="location-detail-content-nav-separator"> > </span>
|
||||
<span class="location-detail-content-nav-name">乌鲁木齐</span>
|
||||
<span class="location-detail-content-nav-name">{{ route.query.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ImageToolbar :selected="imageStore.selected" :imageList="albumList"/>
|
||||
<div class="location-album-detail-info">
|
||||
<span style="font-size: 14px;color: #999999">共12张照片</span>
|
||||
<span style="font-size: 14px;color: #999999">共{{ imageStore.countTotalImages(albumList) }}张照片</span>
|
||||
</div>
|
||||
<div class="location-album-detail-list">
|
||||
<div style="width:100%;height:100%;">
|
||||
<div style="width:100%;height:100%;" v-if="albumList.length != 0">
|
||||
<div v-for="(itemList, index) in albumList" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
@@ -21,7 +22,7 @@
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
@@ -41,6 +42,15 @@
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<AEmpty :image="empty">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
暂无照片,快去上传吧
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -50,8 +60,10 @@ import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import {queryLocationDetailListApi} from "@/api/storage";
|
||||
import useStore from "@/store";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const imageStore = useStore().image;
|
||||
const albumList = ref<any[]>([]);
|
||||
|
||||
const route = useRoute();
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<div class="location-album-content-item" v-for="(item, index) in locationAlbums" :key="index">
|
||||
<span class="location-album-description">{{ item.location }}</span>
|
||||
<div class="location-album-location-list">
|
||||
<div class="location-album-container" @click="handleClick(itemList.id)"
|
||||
<div class="location-album-container" @click="handleClick(itemList.id,itemList.city)"
|
||||
v-for="(itemList, indexItem) in item.list" :key="indexItem">
|
||||
<img class="background-image" :src="itemList.cover_image" :alt="itemList.city"/>
|
||||
<div class="overlay">
|
||||
@@ -30,8 +30,8 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
const upload = useStore().upload;
|
||||
|
||||
function handleClick(id: number) {
|
||||
router.push({path: route.path + `/${id}`});
|
||||
function handleClick(id: number,name: string) {
|
||||
router.push({path: route.path + `/${id}`, query: {name: name}});
|
||||
}
|
||||
|
||||
const locationAlbums = ref<any[]>([]);
|
||||
|
@@ -11,16 +11,16 @@
|
||||
</div>
|
||||
<div class="people-album-detail-toolbar">
|
||||
<AAvatar shape="circle" size="default"></AAvatar>
|
||||
<span style="font-size: 14px;color: #333333">张皓扬</span>
|
||||
<span style="font-size: 14px;color: #333333">{{ route.query.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ImageToolbar :selected="selected"/>
|
||||
<ImageToolbar :selected="imageStore.selected" :imageList="images"/>
|
||||
<div class="people-album-detail-info">
|
||||
<span style="font-size: 14px;color: #999999">共12张照片</span>
|
||||
<span style="font-size: 14px;color: #999999">共{{ imageStore.countTotalImages(images) }}张照片</span>
|
||||
</div>
|
||||
<div class="people-album-detail-list">
|
||||
<div style="width:100%;height:100%;">
|
||||
<div v-for="(itemList, index) in albumList" :key="index">
|
||||
<div style="width:100%;height:100%;" v-if="images.length !== 0">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
||||
@@ -29,7 +29,7 @@
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
@@ -49,6 +49,15 @@
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<AEmpty :image="empty">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
暂无照片,快去上传吧
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -58,10 +67,11 @@ import 'vue3-justified-layout/dist/style.css';
|
||||
import {getFaceSamplesDetailList} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import useStore from "@/store";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const albumList = ref<any[]>([]);
|
||||
const imageStore = useStore().image;
|
||||
const images = ref<any[]>([]);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -74,7 +84,7 @@ const options = reactive({
|
||||
async function getAlbumList(id: number) {
|
||||
const res: any = await getFaceSamplesDetailList(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
albumList.value = res.data.records;
|
||||
images.value = res.data.records;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,11 +3,11 @@
|
||||
<div class="people-album-header">
|
||||
<ADropdown trigger="click">
|
||||
<AButton type="text" size="large" class="people-album-button">
|
||||
{{ selecetedKey === '0' ? '人 物' : '已隐藏' }}
|
||||
{{ selectedKey === '0' ? '人 物' : '已隐藏' }}
|
||||
<DownOutlined class="people-album-icon"/>
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu selectable :selectedKeys="[selecetedKey]" @select="handleSelect">
|
||||
<AMenu selectable :selectedKeys="[selectedKey]" @select="handleSelect">
|
||||
<AMenuItem key="0">人 物</AMenuItem>
|
||||
<AMenuItem key="1">已隐藏</AMenuItem>
|
||||
</AMenu>
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="people-album-toolbar-right">
|
||||
|
||||
<AButton type="text" shape="default" size="middle" class="people-album-toolbar-btn"
|
||||
:disabled="selected.length !== 2" v-if="selecetedKey === '0'">
|
||||
:disabled="selected.length !== 2" v-if="selectedKey === '0'">
|
||||
<template #icon>
|
||||
<BlockOutlined class="people-album-toolbar-icon"/>
|
||||
</template>
|
||||
@@ -44,7 +44,7 @@
|
||||
<template #icon>
|
||||
<EyeInvisibleOutlined class="people-album-toolbar-icon"/>
|
||||
</template>
|
||||
{{ selecetedKey === '0' ? '隐藏人物' : '取消隐藏' }}
|
||||
{{ selectedKey === '0' ? '隐藏人物' : '取消隐藏' }}
|
||||
</AButton>
|
||||
</div>
|
||||
</div>
|
||||
@@ -55,7 +55,7 @@
|
||||
<CheckCard
|
||||
v-for="(item, index) in faceList"
|
||||
:key="index"
|
||||
@click="handleClick(item.id)"
|
||||
@click="handleClick(item.id, item.face_name)"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
@@ -125,7 +125,7 @@ import {getFaceSamplesList, modifyFaceSampleName, modifyFaceTypeBatch} from "@/a
|
||||
|
||||
const faceList = ref<any[]>([]);
|
||||
const addNameInputValue = ref<string>('');
|
||||
const selecetedKey = ref<string>('0');
|
||||
const selectedKey = ref<string>('0');
|
||||
const loading = ref<boolean>(false);
|
||||
const selected = ref<any[]>([]);
|
||||
|
||||
@@ -180,7 +180,7 @@ async function modifyFaceName(id: number, index: number) {
|
||||
* @param key
|
||||
*/
|
||||
function handleSelect({key}) {
|
||||
selecetedKey.value = key;
|
||||
selectedKey.value = key;
|
||||
getFaceList(parseInt(key));
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ function cancelSelectPeople() {
|
||||
*/
|
||||
async function hiddenFace() {
|
||||
if (selected.value.length === 0) return;
|
||||
const res: any = await modifyFaceTypeBatch(selected.value, selecetedKey.value === '0' ? 1 : 0);
|
||||
const res: any = await modifyFaceTypeBatch(selected.value, selectedKey.value === '0' ? 1 : 0);
|
||||
if (res && res.code === 200) {
|
||||
await getFaceList();
|
||||
selected.value = [];
|
||||
@@ -216,9 +216,10 @@ const router = useRouter();
|
||||
/**
|
||||
* 点击人物跳转到详情页
|
||||
* @param id
|
||||
* @param name
|
||||
*/
|
||||
function handleClick(id: number) {
|
||||
router.push({path: route.path + `/${id}`});
|
||||
function handleClick(id: number, name: string | null) {
|
||||
router.push({path: route.path + `/${id}`, query: {name: name}});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@@ -26,17 +26,18 @@
|
||||
下载相册
|
||||
</AButton>
|
||||
</div>
|
||||
<ImageToolbar :selected="selected"/>
|
||||
<ImageToolbar :selected="imageStore.selected" :image-list="albumList"/>
|
||||
<div class="phoalbum-detail-content">
|
||||
<div class="phoalbum-detail-content-nav">
|
||||
<div class="phoalbum-detail-content-nav-left">
|
||||
<AButton type="text" size="large" class="phoalbum-detail-content-nav-title" @click="goBack()">全部相册
|
||||
<AButton type="text" size="large" class="phoalbum-detail-content-nav-title" @click="goBack()">
|
||||
{{ imageStore.tabMap[imageStore.tabActiveKey] }}
|
||||
</AButton>
|
||||
<span class="phoalbum-detail-content-nav-separator"> > </span>
|
||||
<span class="phoalbum-detail-content-nav-name">网盘导入</span>
|
||||
<span class="phoalbum-detail-content-nav-name">{{ route.query.name }}</span>
|
||||
</div>
|
||||
<div class="phoalbum-detail-content-nav-right">
|
||||
<span class="phoalbum-detail-content-nav-date">共15张照片,1个视频,创建于2025年1月1日</span>
|
||||
<span class="phoalbum-detail-content-nav-date">共 {{ imageStore.countTotalImages(albumList) }} 张照片</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="phoalbum-detail-content-desc">
|
||||
@@ -44,7 +45,7 @@
|
||||
<span>相册描述</span>
|
||||
</div>
|
||||
<div class="phoalbum-detail-content-list">
|
||||
<div style="width:100%;height:100%;">
|
||||
<div style="width:100%;height:100%;" v-if="albumList.length !== 0">
|
||||
<div v-for="(itemList, index) in albumList" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
@@ -54,7 +55,7 @@
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
@@ -74,6 +75,15 @@
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<AEmpty :image="empty">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
暂无照片,快去上传吧
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -84,9 +94,10 @@ import 'vue3-justified-layout/dist/style.css';
|
||||
import {queryAlbumDetailListApi} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import useStore from "@/store";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const imageStore = useStore().image;
|
||||
const albumList = ref<any[]>([]);
|
||||
|
||||
const route = useRoute();
|
||||
|
@@ -31,7 +31,7 @@
|
||||
排序
|
||||
</AButton>
|
||||
<template #overlay>
|
||||
<AMenu selectable :selectedKeys="[selecetedKey]" @select="handleSelect">
|
||||
<AMenu selectable :selectedKeys="[selectedKey]" @select="handleSelect">
|
||||
<AMenuItem :key="true">按时间排序</AMenuItem>
|
||||
<AMenuItem :key="false">按名称排序</AMenuItem>
|
||||
</AMenu>
|
||||
@@ -49,18 +49,20 @@
|
||||
</div>
|
||||
<div class="phoalbum-content">
|
||||
<ATabs size="small" :tabBarGutter="50" type="line" tabPosition="top" :tabBarStyle="{position:'unset'}"
|
||||
style="width: 100%;" @change="handleTabChange">
|
||||
style="width: 100%;"
|
||||
v-model:activeKey="imageStore.tabActiveKey"
|
||||
@change="handleTabChange">
|
||||
<template #rightExtra>
|
||||
<span
|
||||
style="color: #999; font-size: 12px;">已全部加载,共 {{ albumList ? albumList.length : 0 }} 个相册</span>
|
||||
</template>
|
||||
<ATabPane key="-1" tab="全部相册">
|
||||
<ATabPane key="-1" :tab="imageStore.tabMap[-1]">
|
||||
<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)"
|
||||
@click.prevent="handleClick(album.id,album.name)"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||
@@ -104,13 +106,13 @@
|
||||
</div>
|
||||
</ASpin>
|
||||
</ATabPane>
|
||||
<ATabPane key="0" tab="我的相册">
|
||||
<ATabPane key="0" :tab="imageStore.tabMap[0]">
|
||||
<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)"
|
||||
@click.prevent="handleClick(album.id,album.name)"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||
@@ -154,13 +156,13 @@
|
||||
</div>
|
||||
</ASpin>
|
||||
</ATabPane>
|
||||
<ATabPane key="1" tab="我的分享">
|
||||
<ATabPane key="1" :tab="imageStore.tabMap[1]">
|
||||
<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)"
|
||||
@click.prevent="handleClick(album.id,album.name)"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||
@@ -204,13 +206,13 @@
|
||||
</div>
|
||||
</ASpin>
|
||||
</ATabPane>
|
||||
<ATabPane key="2" tab="收藏相册">
|
||||
<ATabPane key="2" :tab="imageStore.tabMap[2]">
|
||||
<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)"
|
||||
@click.prevent="handleClick(album.id,album.name)"
|
||||
@mouseover="isHovered = index"
|
||||
@mouseleave="isHovered = null">
|
||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||
@@ -263,18 +265,21 @@ 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";
|
||||
import useStore from "@/store";
|
||||
|
||||
const isHovered = ref<number | null>(null);
|
||||
|
||||
const albumNameValue = ref<string>("未命名相册");
|
||||
const albumRenameValue = ref<string>("");
|
||||
|
||||
const selecetedKey = ref<boolean>(true);
|
||||
const selectedKey = ref<boolean>(true);
|
||||
|
||||
const albumList = ref<any[]>([]);
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
const imageStore = useStore().image;
|
||||
|
||||
/**
|
||||
* 创建相册
|
||||
*/
|
||||
@@ -286,7 +291,7 @@ async function createAlbumSubmit() {
|
||||
const res: any = await createAlbumApi(albumNameValue.value);
|
||||
if (res && res.code === 200) {
|
||||
albumNameValue.value = "未命名相册";
|
||||
await getAlbumList(0, selecetedKey.value);
|
||||
await getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||
} else {
|
||||
message.error("创建相册失败");
|
||||
}
|
||||
@@ -297,8 +302,8 @@ async function createAlbumSubmit() {
|
||||
* @param key
|
||||
*/
|
||||
async function handleSelect({key}) {
|
||||
selecetedKey.value = key;
|
||||
await getAlbumList(0, key);
|
||||
selectedKey.value = key;
|
||||
await getAlbumList(parseInt(imageStore.tabActiveKey), key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,7 +311,8 @@ async function handleSelect({key}) {
|
||||
* @param activeKey
|
||||
*/
|
||||
async function handleTabChange(activeKey: string) {
|
||||
await getAlbumList(parseInt(activeKey), selecetedKey.value);
|
||||
imageStore.tabActiveKey = activeKey;
|
||||
await getAlbumList(parseInt(activeKey), selectedKey.value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,7 +343,7 @@ async function renameAlbum(id: number, name: string) {
|
||||
const res: any = await renameAlbumApi(id, name);
|
||||
if (res && res.code === 200) {
|
||||
albumRenameValue.value = "";
|
||||
await getAlbumList(0, selecetedKey.value);
|
||||
await getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +354,7 @@ async function renameAlbum(id: number, name: string) {
|
||||
async function deleteAlbum(id: number) {
|
||||
const res: any = await deleteAlbumApi(id);
|
||||
if (res && res.code === 200) {
|
||||
await getAlbumList(0, selecetedKey.value);
|
||||
await getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||
} else {
|
||||
message.error("删除相册失败");
|
||||
}
|
||||
@@ -360,13 +366,16 @@ const router = useRouter();
|
||||
/**
|
||||
* 点击相册跳转到详情页
|
||||
* @param id
|
||||
* @param albumName
|
||||
*/
|
||||
function handleClick(id: number) {
|
||||
router.push({path: route.path + `/${id}`});
|
||||
function handleClick(id: number, albumName: string) {
|
||||
router.push({
|
||||
path: route.path + `/${id}`, query: {name: albumName}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getAlbumList(0, selecetedKey.value);
|
||||
getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
@@ -2,14 +2,16 @@
|
||||
<div class="thing-album-detail">
|
||||
<div class="thing-album-detail-header">
|
||||
<div class="thing-detail-content-nav">
|
||||
<AButton size="large" type="text" class="thing-detail-content-nav-title" @click="goBack">人物</AButton>
|
||||
<AButton size="large" type="text" class="thing-detail-content-nav-title" @click="goBack">
|
||||
{{ getZhCategoryNameByEnName(route.query.category as string) }}
|
||||
</AButton>
|
||||
<span class="thing-detail-content-nav-separator"> > </span>
|
||||
<span class="thing-detail-content-nav-name">人物</span>
|
||||
<span class="thing-detail-content-nav-name">{{ getZhLabelNameByEnName(route.query.tag as string) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ImageToolbar :selected="selected"/>
|
||||
<ImageToolbar :selected="imageStore.selected" :image-list="albumList"/>
|
||||
<div class="thing-album-detail-info">
|
||||
<span style="font-size: 14px;color: #999999">共12张照片</span>
|
||||
<span style="font-size: 14px;color: #999999">共{{ imageStore.countTotalImages(albumList) }}张照片</span>
|
||||
</div>
|
||||
<div class="thing-album-detail-list">
|
||||
<div style="width:100%;height:100%;">
|
||||
@@ -22,7 +24,7 @@
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
@@ -53,9 +55,10 @@ import 'vue3-justified-layout/dist/style.css';
|
||||
import {queryThingDetailListApi} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import useStore from "@/store";
|
||||
import {getZhCategoryNameByEnName, getZhLabelNameByEnName} from "@/constant/coco_ssd_label_category.ts";
|
||||
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const imageStore = useStore().image;
|
||||
const albumList = ref<any[]>([]);
|
||||
const upload = useStore().upload;
|
||||
const route = useRoute();
|
||||
@@ -113,7 +116,7 @@ function goBack(): void {
|
||||
justify-content: flex-start;
|
||||
width: 1000%;
|
||||
height: 100%;
|
||||
gap: 10px;
|
||||
gap: 5px;
|
||||
|
||||
.thing-detail-content-nav-title {
|
||||
font-size: 20px;
|
||||
@@ -121,6 +124,7 @@ function goBack(): void {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
.thing-detail-content-nav-separator {
|
||||
@@ -141,13 +145,13 @@ function goBack(): void {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-left: 30px;
|
||||
margin-left: 15px;
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.thing-album-detail-list {
|
||||
width: 99%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
//margin-left: 5px;
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<span class="thing-album-title">{{ getZhCategoryNameByEnName(item.category) }}</span>
|
||||
<div class="thing-album-wrapper">
|
||||
<div class="thing-album-container" v-for="(tags, indexList) in item.list" :key="indexList"
|
||||
@click="handleClick(tags.tag_name)">
|
||||
@click="handleClick(tags.tag_name,item.category,tags.tag_name)">
|
||||
<img class="background-image" :src="tags.cover_image" :alt="tags.tag_name"/>
|
||||
<div class="overlay">
|
||||
<span>{{ getZhLabelNameByEnName(tags.tag_name) }}</span>
|
||||
@@ -46,9 +46,11 @@ const upload = useStore().upload;
|
||||
/**
|
||||
* 点击事件
|
||||
* @param id
|
||||
* @param tag
|
||||
* @param category
|
||||
*/
|
||||
function handleClick(id: string) {
|
||||
router.push({path: route.path + `/${id}`});
|
||||
function handleClick(id: string, category: string, tag: string) {
|
||||
router.push({path: route.path + `/${id}`, query: {category: category, tag: tag}});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@@ -1,100 +0,0 @@
|
||||
.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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -383,7 +383,13 @@ async function phoneLoginSubmit() {
|
||||
message.success(t('login.loginSuccess'));
|
||||
loginLoading.value = false;
|
||||
setTimeout(() => {
|
||||
router.push('/main/photo/all');
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const redirect = currentUrl.searchParams.get('redirect');
|
||||
if (redirect) {
|
||||
router.push(redirect);
|
||||
} else {
|
||||
router.push('/main/photo/all');
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
loginLoading.value = false;
|
||||
@@ -472,7 +478,13 @@ async function checkAccountLoginCaptcha(angle: number) {
|
||||
loginLoading.value = false;
|
||||
showAccountRotateCaptcha.value = false;
|
||||
setTimeout(() => {
|
||||
router.push('/main/photo/all');
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const redirect = currentUrl.searchParams.get('redirect');
|
||||
if (redirect) {
|
||||
router.push(redirect);
|
||||
} else {
|
||||
router.push('/main/photo/all');
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
showAccountRotateCaptcha.value = false;
|
||||
|
@@ -14,57 +14,216 @@
|
||||
创建相册
|
||||
</AButton>
|
||||
</div>
|
||||
<image-toolbar :selected="selected"/>
|
||||
<image-toolbar :selected="imageStore.selected" :image-list="images"/>
|
||||
<div class="photo-list">
|
||||
<ATabs size="small" :tabBarGutter="50" type="line" tabPosition="top" :tabBarStyle="{position:'unset'}"
|
||||
@change="handleTabChange"
|
||||
v-model:activeKey="imageStore.homeTabActiveKey"
|
||||
style="width: 99%;">
|
||||
<template #rightExtra>
|
||||
<ASwitch size="small" v-model:checked="switchValue"/>
|
||||
</template>
|
||||
<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: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options" style="line-height: 0 !important;">
|
||||
<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.thumbnail"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
:height="200"
|
||||
:preview="{
|
||||
<ATabPane key="all" :tab="imageStore.homeTabMap['all']">
|
||||
<ASpin size="large" :spinning="loading" :delay="500">
|
||||
<div style="width:100%;height:100%;" v-if="images">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options"
|
||||
style="line-height: 0 !important;">
|
||||
<template #default="{ item }">
|
||||
<CheckCard :key="index"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.thumbnail"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
:height="200"
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy">
|
||||
<template #previewMask>
|
||||
</template>
|
||||
</AImage>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
loading="lazy">
|
||||
<template #previewMask>
|
||||
</template>
|
||||
</AImage>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty :image="empty">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
还没检测到任何图片,快去上传吧!
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</ASpin>
|
||||
</ATabPane>
|
||||
<ATabPane key="video" tab="视频">
|
||||
<div style="width:100%;height:100%;">
|
||||
|
||||
</div>
|
||||
<ATabPane key="video" :tab="imageStore.homeTabMap['video']">
|
||||
<ASpin size="large" :spinning="loading" :delay="500">
|
||||
<div style="width:100%;height:100%;" v-if="images">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options"
|
||||
style="line-height: 0 !important;">
|
||||
<template #default="{ item }">
|
||||
<CheckCard :key="index"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.thumbnail"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
:height="200"
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy">
|
||||
<template #previewMask>
|
||||
</template>
|
||||
</AImage>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty
|
||||
:image="empty"
|
||||
:image-style="{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}"
|
||||
>
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
还没检测到任何视频,快去上传吧!
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</ASpin>
|
||||
</ATabPane>
|
||||
<ATabPane key="gif" tab="动图">
|
||||
|
||||
<ATabPane key="gif" :tab="imageStore.homeTabMap['gif']">
|
||||
<ASpin size="large" :spinning="loading" :delay="500">
|
||||
<div style="width:100%;height:100%;" v-if="images">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options"
|
||||
style="line-height: 0 !important;">
|
||||
<template #default="{ item }">
|
||||
<CheckCard :key="index"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.thumbnail"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
:height="200"
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy">
|
||||
<template #previewMask>
|
||||
</template>
|
||||
</AImage>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty
|
||||
:image="empty"
|
||||
:image-style="{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}"
|
||||
>
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
还没检测到任何动图,快去上传吧!
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</ASpin>
|
||||
</ATabPane>
|
||||
<ATabPane key="screenshot" tab="截图">
|
||||
|
||||
<ATabPane key="screenshot" :tab="imageStore.homeTabMap['screenshot']">
|
||||
<ASpin size="large" :spinning="loading" :delay="500">
|
||||
<div style="width:100%;height:100%;" v-if="images">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options"
|
||||
style="line-height: 0 !important;">
|
||||
<template #default="{ item }">
|
||||
<CheckCard :key="index"
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.thumbnail"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
:height="200"
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy">
|
||||
<template #previewMask>
|
||||
</template>
|
||||
</AImage>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-content">
|
||||
<AEmpty
|
||||
:image="empty"
|
||||
:image-style="{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}"
|
||||
>
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
还没检测到任何屏幕截图,快去上传吧!
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</ASpin>
|
||||
</ATabPane>
|
||||
</ATabs>
|
||||
</div>
|
||||
@@ -80,9 +239,10 @@ import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||
import useStore from "@/store";
|
||||
import {queryAllImagesApi} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
const imageStore = useStore().image;
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const switchValue = ref<boolean>(false);
|
||||
const upload = useStore().upload;
|
||||
|
||||
@@ -91,20 +251,29 @@ const options = reactive({
|
||||
});
|
||||
|
||||
const images = ref<any[]>([]);
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* 获取所有图片
|
||||
*/
|
||||
async function getAllImages() {
|
||||
const res: any = await queryAllImagesApi("image", false, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
async function getAllImages(type: string) {
|
||||
images.value = [];
|
||||
loading.value = true;
|
||||
const res: any = await queryAllImagesApi(type, false, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
images.value = res.data.records;
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
async function handleTabChange(activeKey: string) {
|
||||
imageStore.homeTabActiveKey = activeKey;
|
||||
await getAllImages(activeKey);
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getAllImages();
|
||||
getAllImages(imageStore.homeTabActiveKey);
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@@ -144,4 +313,13 @@ onMounted(() => {
|
||||
//transform: scale(0.99);
|
||||
box-shadow: 0 0 10px 0 rgba(77, 167, 255, 0.89);
|
||||
}
|
||||
|
||||
.empty-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,18 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
const props = defineProps({
|
||||
selected: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<transition name="slide-fade">
|
||||
<div v-show="props.selected.length > 0" class="photo-toolbar-header">
|
||||
<div class="photo-toolbar-left">
|
||||
<AButton type="text" shape="circle" size="large" class="photo-toolbar-btn">
|
||||
<AButton type="text" shape="circle" size="large" class="photo-toolbar-btn" @click="clearSelected">
|
||||
<template #icon>
|
||||
<CloseOutlined class="photo-toolbar-icon"/>
|
||||
</template>
|
||||
@@ -20,7 +10,7 @@ const props = defineProps({
|
||||
<span style="font-size: 16px;font-weight: bold">
|
||||
已选择 {{ props.selected.length }} 张照片
|
||||
</span>
|
||||
<AButton type="text" shape="default" class="photo-toolbar-btn" size="middle">
|
||||
<AButton type="text" shape="default" class="photo-toolbar-btn" size="middle" @click="selectAll">
|
||||
全选
|
||||
</AButton>
|
||||
</div>
|
||||
@@ -43,7 +33,7 @@ const props = defineProps({
|
||||
</template>
|
||||
分享
|
||||
</AButton>
|
||||
<AButton type="text" shape="default" size="middle" class="photo-toolbar-btn">
|
||||
<AButton type="text" shape="default" size="middle" class="photo-toolbar-btn" @click="deleteImages">
|
||||
<template #icon>
|
||||
<DeleteOutlined class="photo-toolbar-icon"/>
|
||||
</template>
|
||||
@@ -53,7 +43,41 @@ const props = defineProps({
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import useStore from "@/store";
|
||||
import {Image, ImageList, ImageRecord} from "@/types/image";
|
||||
import {deletedImagesApi} from "@/api/storage";
|
||||
|
||||
const props = defineProps({
|
||||
selected: {
|
||||
type: Array as () => number[],
|
||||
default: () => []
|
||||
},
|
||||
imageList: {
|
||||
type: Array as () => ImageList,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
const imageStore = useStore().image;
|
||||
const uploadStore = useStore().upload;
|
||||
const clearSelected = () => {
|
||||
imageStore.selected = [];
|
||||
};
|
||||
|
||||
const selectAll = () => {
|
||||
imageStore.selected = props.imageList.flatMap((record: ImageRecord) => record.list.map((image: Image) => image.id));
|
||||
};
|
||||
|
||||
const deleteImages = async () => {
|
||||
const res: any = await deletedImagesApi(props.selected, uploadStore.storageSelected?.[0], uploadStore.storageSelected?.[1]);
|
||||
if (res.code === 200) {
|
||||
imageStore.selected = [];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.photo-toolbar-header {
|
||||
position: fixed;
|
||||
|
@@ -2,10 +2,7 @@
|
||||
<ADrawer v-model:open="upload.openUploadDrawer" placement="right" title="上传照片" width="40%" @close="cancelUpload">
|
||||
<template #extra>
|
||||
<AFlex :vertical="false" align="center" gap="large" justify="center">
|
||||
<ASelect size="middle" style="width: 150px">
|
||||
|
||||
</ASelect>
|
||||
<ASelect size="middle" style="width: 150px">
|
||||
<ASelect size="middle" style="width: 150px" placeholder="选择上传的相册">
|
||||
|
||||
</ASelect>
|
||||
</AFlex>
|
||||
@@ -42,6 +39,13 @@
|
||||
:status="progressStatus"
|
||||
:show-info="true" size="small" type="line" v-show="predicting" style="width: 80%"/>
|
||||
</AUploadDragger>
|
||||
<AEmpty :image="empty" v-if="fileList.length === 0">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
上传列表为空,可直接拖动文件到此区域上传。
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</ADrawer>
|
||||
</template>
|
||||
@@ -63,6 +67,7 @@ import exifr from 'exifr';
|
||||
import isScreenshot from "@/utils/imageUtils/isScreenshot.ts";
|
||||
import {getCategoryByLabel} from "@/constant/coco_ssd_label_category.ts";
|
||||
import {generateThumbnail} from "@/utils/imageUtils/generateThumb.ts";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
const predicting = ref<boolean>(false);
|
||||
const progressPercent = ref<number>(0);
|
||||
|
@@ -14,7 +14,7 @@
|
||||
创建相册
|
||||
</AButton>
|
||||
</div>
|
||||
<image-toolbar :selected="selected" />
|
||||
<image-toolbar :selected="imageStore.selected" :image-list="images"/>
|
||||
<div class="photo-list">
|
||||
<div style="width:100%;height:100%;" v-if="images.length !== 0">
|
||||
<div v-for="(itemList, index) in images" :key="index">
|
||||
@@ -26,7 +26,7 @@
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
v-model="imageStore.selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
@@ -43,6 +43,15 @@
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<AEmpty :image="empty">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
暂无照片,快去上传吧
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</div>
|
||||
<ImageUpload/>
|
||||
</div>
|
||||
@@ -55,8 +64,9 @@ import useStore from "@/store";
|
||||
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||
import {queryRecentImagesApi} from "@/api/storage";
|
||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const imageStore = useStore().image;
|
||||
const upload = useStore().upload;
|
||||
const images = ref<any[]>([]);
|
||||
const options = reactive({
|
||||
|
@@ -109,7 +109,13 @@ async function handleListenMessage() {
|
||||
userStore.token.expireAt = expire_at;
|
||||
message.success(t('login.loginSuccess'));
|
||||
setTimeout(() => {
|
||||
router.push('/main/photo/all');
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const redirect = currentUrl.searchParams.get('redirect');
|
||||
if (redirect) {
|
||||
router.push(redirect);
|
||||
} else {
|
||||
router.push('/main/photo/all');
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
message.warning(t('login.loginError'));
|
||||
|
@@ -20,25 +20,42 @@
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.url"
|
||||
<AImage :src="item.thumbnail"
|
||||
:alt="item.file_name"
|
||||
:key="index"
|
||||
style="height: 200px"
|
||||
:previewMask="false"
|
||||
loading="lazy"/>
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy">
|
||||
<template #previewMask>
|
||||
|
||||
</template>
|
||||
</AImage>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<AEmpty :image="empty">
|
||||
<template #description>
|
||||
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||
暂无照片,快去上传吧
|
||||
</span>
|
||||
</template>
|
||||
</AEmpty>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import {queryShareImageApi} from "@/api/share";
|
||||
import {getDeletedRecordApi} from "@/api/storage";
|
||||
import useStore from "@/store";
|
||||
import empty from "@/assets/svgs/empty.svg";
|
||||
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
@@ -46,14 +63,21 @@ const images = ref<any[]>([]);
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
const upload = useStore().upload;
|
||||
|
||||
|
||||
async function getImages() {
|
||||
const res = await queryShareImageApi("c09e3c571303448798c878095fbaa521", "123456");
|
||||
console.log(res);
|
||||
/**
|
||||
* 查询回收站
|
||||
*/
|
||||
async function queryRecyclingBin() {
|
||||
const res: any = await getDeletedRecordApi(upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||
if (res && res.code === 200) {
|
||||
images.value = res.data.records;
|
||||
}
|
||||
}
|
||||
|
||||
getImages();
|
||||
onMounted(() => {
|
||||
queryRecyclingBin();
|
||||
});
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
@@ -5,15 +5,19 @@
|
||||
<div class="image-share-left-title">
|
||||
<h3>数据概览</h3>
|
||||
</div>
|
||||
|
||||
<div class="image-share-left-content">
|
||||
<ACard class="image-share-left-content-item"
|
||||
type="inner"
|
||||
style="background: linear-gradient(102.74deg, rgb(66, 230, 171) -7.03%, rgb(103, 235, 187) 97.7%);">
|
||||
<div class="image-share-left-item-content">
|
||||
<span style="font-weight: bolder;font-size: 2.3vh">浏览次数(次)</span>
|
||||
<span style="font-weight: bolder;font-size: 5vh">1</span>
|
||||
<span style="font-weight: bolder;font-size: 5vh">{{ overviewData?overviewData.visit_count:0 }}</span>
|
||||
<p style="font-size: 2vh;color: hsla(0,0%,100%,.6);">今日浏览
|
||||
<span style="font-weight: bolder;font-size: 2.8vh;color: #fff;">+0</span>
|
||||
<span
|
||||
style="font-weight: bolder;font-size: 2.8vh;color: #fff;">+{{
|
||||
overviewData ? overviewData.visit_count_today : 0
|
||||
}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</ACard>
|
||||
@@ -22,9 +26,12 @@
|
||||
style="background: linear-gradient(101.63deg, rgb(82, 138, 250) -12.83%, rgb(122, 167, 255) 100%);">
|
||||
<div class="image-share-left-item-content">
|
||||
<span style="font-weight: bolder;font-size: 2.3vh">浏览人数(人)</span>
|
||||
<span style="font-weight: bolder;font-size: 5vh">1</span>
|
||||
<span style="font-weight: bolder;font-size: 5vh">{{ overviewData?overviewData.viewer_count:0 }}</span>
|
||||
<p style="font-size: 2vh;color: hsla(0,0%,100%,.6);">今日浏览人数
|
||||
<span style="font-weight: bolder;font-size: 2.8vh;color: #fff;">+0</span>
|
||||
<span
|
||||
style="font-weight: bolder;font-size: 2.8vh;color: #fff;">+{{
|
||||
overviewData?overviewData.viewer_count_today:0
|
||||
}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</ACard>
|
||||
@@ -33,13 +40,17 @@
|
||||
style="background: linear-gradient(102.99deg, rgb(126, 92, 255) 3.18%, rgb(162, 139, 255) 102.52%);">
|
||||
<div class="image-share-left-item-content">
|
||||
<span style="font-weight: bolder;font-size: 2.3vh">发布次数(次)</span>
|
||||
<span style="font-weight: bolder;font-size: 5vh">1</span>
|
||||
<span style="font-weight: bolder;font-size: 5vh">{{ overviewData?overviewData.publish_count:0 }}</span>
|
||||
<p style="font-size: 2vh;color: hsla(0,0%,100%,.6);">今日发布
|
||||
<span style="font-weight: bolder;font-size: 2.8vh;color: #fff;">+0</span>
|
||||
<span
|
||||
style="font-weight: bolder;font-size: 2.8vh;color: #fff;">+{{
|
||||
overviewData ? overviewData.publish_count_today : 0
|
||||
}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</ACard>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="image-share-left-bottom">
|
||||
<div class="image-share-left-bottom-title">
|
||||
@@ -88,8 +99,9 @@
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<ATooltip title="复制分享链接">
|
||||
<AButton type="text" size="small" @click="copyToClipboard(record.share_code)">
|
||||
<LinkOutlined />
|
||||
<AButton type="text" size="small"
|
||||
@click="copyToClipboard(record.invite_code)">
|
||||
<LinkOutlined/>
|
||||
</AButton>
|
||||
</ATooltip>
|
||||
<ATooltip title="删除快传记录">
|
||||
@@ -118,8 +130,8 @@
|
||||
<script setup lang="ts">
|
||||
import {Dayjs} from 'dayjs';
|
||||
import dayjs from 'dayjs';
|
||||
import ShareUpload from "@/views/ImageShare/ShareUpload.vue";
|
||||
import {queryShareRecordListApi} from "@/api/share";
|
||||
import ShareUpload from "@/views/Share/ImageShare/ShareUpload.vue";
|
||||
import {queryShareOverviewApi, queryShareRecordListApi} from "@/api/share";
|
||||
import {message} from "ant-design-vue";
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
||||
@@ -132,6 +144,7 @@ const selectedDateRange = ref<RangeValue>();
|
||||
const hackValue = ref<RangeValue>();
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
const overviewDataLoading = ref<boolean>(false);
|
||||
|
||||
|
||||
const disabledDate = (current: Dayjs) => {
|
||||
@@ -229,7 +242,8 @@ const formatValidityPeriod = (period: number) => {
|
||||
|
||||
// 复制功能
|
||||
function copyToClipboard(text: string) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
const url: string = import.meta.env.VITE_APP_WEB_URL + '/main/share/list/' + text;
|
||||
navigator.clipboard.writeText(url).then(() => {
|
||||
message.success('复制成功');
|
||||
}).catch(() => {
|
||||
message.error('复制失败');
|
||||
@@ -250,13 +264,125 @@ async function getShareRecords(dateRange: string[]) {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
const overviewData = ref<any>();
|
||||
|
||||
/**
|
||||
* 获取分享概览
|
||||
*/
|
||||
async function getShareOverview() {
|
||||
overviewDataLoading.value = true;
|
||||
const res: any = await queryShareOverviewApi();
|
||||
if (res && res.code === 200) {
|
||||
overviewData.value = res.data;
|
||||
}
|
||||
overviewDataLoading.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]);
|
||||
await getShareOverview();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss" src="./index.scss">
|
||||
<style scoped lang="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;
|
||||
|
||||
.ant-card {
|
||||
height: 100%;
|
||||
|
||||
.ant-table {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
@@ -23,18 +23,30 @@
|
||||
</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>
|
||||
<AButton type="primary" size="large" shape="round" style="width: 70%">
|
||||
<template #icon>
|
||||
<CloudUploadOutlined/>
|
||||
</template>
|
||||
上 传 照 片
|
||||
</AButton>
|
||||
<APopover placement="top" trigger="click">
|
||||
<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>
|
||||
<AButton @click.stop type="default" size="large" shape="round" style="width: 70%">
|
||||
<template #icon>
|
||||
<QrcodeOutlined/>
|
||||
</template>
|
||||
手 机 上 传
|
||||
</AButton>
|
||||
</APopover>
|
||||
|
||||
<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>
|
||||
@@ -143,7 +155,7 @@
|
||||
</div>
|
||||
<div v-if="loading && !uploadSuccess" class="image-share-right-bottom-loading">
|
||||
<div class="image-share-right-bottom-loading-content">
|
||||
<a-progress
|
||||
<AProgress
|
||||
type="circle"
|
||||
:stroke-color="{
|
||||
'0%': '#108ee9',
|
||||
@@ -428,6 +440,9 @@ onMounted(() => {
|
||||
}
|
||||
});
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
websocket.close(false);
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.image-share-right {
|
||||
@@ -674,6 +689,6 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2vh;
|
||||
gap: 5vh;
|
||||
}
|
||||
</style>
|
154
src/views/Share/ShareViewList/ShareSidebar.vue
Normal file
154
src/views/Share/ShareViewList/ShareSidebar.vue
Normal file
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<div class="share-sidebar" v-if="shareInfo">
|
||||
<div class="share-sidebar-header">
|
||||
<AAvatar size="default" shape="circle" :src="shareInfo.sharer_avatar"/>
|
||||
<p class="share-sidebar-title">来自
|
||||
<span class="ellipsis" style="color: #00aced;cursor: pointer">{{ shareInfo.sharer_name }}</span>
|
||||
的分享</p>
|
||||
</div>
|
||||
<div class="share-sidebar-body">
|
||||
<div class="share-sidebar-body-top">
|
||||
<AAvatar :size="coverImageSize" shape="square">
|
||||
<template #icon>
|
||||
<AImage width="100%" height="100%" :src="`data:image/png;base64,`+shareInfo.cover_image" :preview="false">
|
||||
</AImage>
|
||||
</template>
|
||||
</AAvatar>
|
||||
<p>{{ shareInfo.album_name }}</p>
|
||||
<p style="color: #de3333; font-size: 12px">{{ formatTimeWithChinese(shareInfo.expire_time) }} 失效</p>
|
||||
<AButton type="primary" size="large" shape="round">已加入,去查看</AButton>
|
||||
<AButton type="link" size="small" style="font-size: 13px">复制链接</AButton>
|
||||
</div>
|
||||
<div class="share-sidebar-body-bottom">
|
||||
<div class="share-sidebar-body-bottom-item">
|
||||
<p class="share-sidebar-body-bottom-item-title">照片</p>
|
||||
<p class="share-sidebar-body-bottom-item-title">{{ shareInfo.image_count }}</p>
|
||||
</div>
|
||||
<div class="share-sidebar-body-bottom-item">
|
||||
<p class="share-sidebar-body-bottom-item-title">共享人数</p>
|
||||
<p class="share-sidebar-body-bottom-item-title">{{ shareInfo.viewer_count }}</p>
|
||||
</div>
|
||||
<div class="share-sidebar-body-bottom-item">
|
||||
<p class="share-sidebar-body-bottom-item-title">浏览次数</p>
|
||||
<p class="share-sidebar-body-bottom-item-title">{{ shareInfo.visit_count }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="share-sidebar-footer">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import {queryShareInfoApi} from "@/api/share";
|
||||
|
||||
const coverImageSize = ref<number>(130);
|
||||
const shareInfo = ref<any>();
|
||||
const route = useRoute();
|
||||
|
||||
async function getShareInfo(invite_code: string) {
|
||||
const res: any = await queryShareInfoApi(invite_code);
|
||||
if (res && res.code === 200) {
|
||||
shareInfo.value = res.data;
|
||||
}
|
||||
}
|
||||
|
||||
function formatTimeWithChinese(dateString) {
|
||||
const date = new Date(dateString);
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1; // getMonth() 返回 0-11,需要加 1
|
||||
const day = date.getDate();
|
||||
|
||||
return `${year}年${month}月${day}日`;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const invite_code = route.params.id;
|
||||
const code = Array.isArray(invite_code) ? invite_code[0] : invite_code;
|
||||
getShareInfo(code);
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.share-sidebar {
|
||||
width: 220px;
|
||||
height: 100%;
|
||||
//height: calc(100vh - 271px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.share-sidebar-header {
|
||||
width: 100%;
|
||||
height: 15%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid #eee;
|
||||
gap: 10px;
|
||||
|
||||
.share-sidebar-title {
|
||||
font-size: 1.7vh;
|
||||
color: #333;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.share-sidebar-body {
|
||||
width: 100%;
|
||||
height: 70%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 20px;
|
||||
|
||||
.share-sidebar-body-top {
|
||||
width: 100%;
|
||||
height: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.share-sidebar-body-bottom {
|
||||
width: 80%;
|
||||
height: 20%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 15px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
|
||||
.share-sidebar-body-bottom-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.share-sidebar-body-bottom-item-title {
|
||||
font-size: 1.8vh;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.share-sidebar-footer {
|
||||
width: 100%;
|
||||
height: 15%;
|
||||
}
|
||||
}
|
||||
</style>
|
142
src/views/Share/ShareViewList/index.vue
Normal file
142
src/views/Share/ShareViewList/index.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
|
||||
<div class="share-view-main">
|
||||
<div class="main-header">
|
||||
<Header/>
|
||||
</div>
|
||||
<div class="share-view-content">
|
||||
<ShareSidebar/>
|
||||
<div class="share-view-content-container">
|
||||
<div class="share-content-header">
|
||||
<AButton type="link" size="large" class="share-content-header-button">图片列表</AButton>
|
||||
</div>
|
||||
<div class="share-content-verify" v-if="images.length <= 0">
|
||||
<AInputPassword size="large" placeholder="请输入访问密码" style="width: 20%" @pressEnter="getShareImages"/>
|
||||
<p style="font-size: 12px;color: #999;">回车后可查看图片列表</p>
|
||||
</div>
|
||||
<ASpin :spinning="loading" size="large">
|
||||
<div v-if="images.length !== 0">
|
||||
<AImagePreviewGroup>
|
||||
<Vue3JustifiedLayout v-model:list="images" :options="options">
|
||||
<template #default="{ item }">
|
||||
<CheckCard
|
||||
class="photo-item"
|
||||
margin="0"
|
||||
border-radius="0"
|
||||
v-model="selected"
|
||||
:showHoverCircle="true"
|
||||
:iconSize="20"
|
||||
:showSelectedEffect="true"
|
||||
:value="item.id">
|
||||
<AImage :src="item.thumbnail"
|
||||
:alt="item.file_name"
|
||||
style="height: 200px"
|
||||
:preview="{
|
||||
src: item.url,
|
||||
}"
|
||||
loading="lazy">
|
||||
<template #previewMask>
|
||||
|
||||
</template>
|
||||
</AImage>
|
||||
</CheckCard>
|
||||
</template>
|
||||
</Vue3JustifiedLayout>
|
||||
</AImagePreviewGroup>
|
||||
</div>
|
||||
</ASpin>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import Header from "@/layout/default/Header/Header.vue";
|
||||
import ShareSidebar from "@/views/Share/ShareViewList/ShareSidebar.vue";
|
||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||
import 'vue3-justified-layout/dist/style.css';
|
||||
import {queryShareImageApi} from "@/api/share";
|
||||
|
||||
const selected = ref<(string | number)[]>([]);
|
||||
const images = ref<any[]>([]);
|
||||
const options = reactive({
|
||||
targetRowHeight: 200 // 高度
|
||||
});
|
||||
const route = useRoute();
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* 获取分享图片列表
|
||||
* @param e
|
||||
*/
|
||||
async function getShareImages(e) {
|
||||
loading.value = true;
|
||||
const invite_code = route.params.id;
|
||||
const code = Array.isArray(invite_code) ? invite_code[0] : invite_code;
|
||||
const res: any = await queryShareImageApi(code, e.target.value);
|
||||
if (res && res.code === 200) {
|
||||
images.value = res.data.records;
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.share-view-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #eaeef6;
|
||||
color: var(--text-color);
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
|
||||
.share-view-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
height: calc(100vh - 70px);
|
||||
|
||||
.share-view-content-container {
|
||||
width: calc(100vw - 230px);
|
||||
height: calc(100vh - 100px);
|
||||
max-height: calc(100vh - 100px);
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
|
||||
.share-content-header {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
border-bottom: 1px solid #e2e2e2;
|
||||
|
||||
.share-content-header-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.share-content-verify {
|
||||
width: 100%;
|
||||
height: calc(100vh - 155px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
Reference in New Issue
Block a user