✨ 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']
|
AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
|
||||||
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
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']
|
AllPhoto: typeof import('./src/views/Photograph/AllPhoto/AllPhoto.vue')['default']
|
||||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||||
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||||
AMenuItemGroup: typeof import('ant-design-vue/es')['MenuItemGroup']
|
AMenuItemGroup: typeof import('ant-design-vue/es')['MenuItemGroup']
|
||||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||||
AnimatedNature: typeof import('./src/components/AnimatedNature/AnimatedNature.vue')['default']
|
AnimatedNature: typeof import('./src/components/AnimatedNature/AnimatedNature.vue')['default']
|
||||||
APagination: typeof import('ant-design-vue/es')['Pagination']
|
|
||||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||||
APopover: typeof import('ant-design-vue/es')['Popover']
|
APopover: typeof import('ant-design-vue/es')['Popover']
|
||||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||||
AQrcode: typeof import('ant-design-vue/es')['QRCode']
|
AQrcode: typeof import('ant-design-vue/es')['QRCode']
|
||||||
ARadio: typeof import('ant-design-vue/es')['Radio']
|
|
||||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
|
||||||
ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
|
ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
|
||||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||||
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
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']
|
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||||
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
||||||
ATable: typeof import('ant-design-vue/es')['Table']
|
ATable: typeof import('ant-design-vue/es')['Table']
|
||||||
ATabPane: typeof import('ant-design-vue/es')['TabPane']
|
ATabPane: typeof import('ant-design-vue/es')['TabPane']
|
||||||
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
||||||
ATag: typeof import('ant-design-vue/es')['Tag']
|
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||||
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
|
||||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||||
AUpload: typeof import('ant-design-vue/es')['Upload']
|
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||||
AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
|
AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
|
||||||
@@ -67,28 +59,25 @@ declare module 'vue' {
|
|||||||
Card3D: typeof import('./src/components/Card3D/Card3D.vue')['default']
|
Card3D: typeof import('./src/components/Card3D/Card3D.vue')['default']
|
||||||
CheckCard: typeof import('./src/components/CheckCard/CheckCard.vue')['default']
|
CheckCard: typeof import('./src/components/CheckCard/CheckCard.vue')['default']
|
||||||
CheckCircleOutlined: typeof import('@ant-design/icons-vue')['CheckCircleOutlined']
|
CheckCircleOutlined: typeof import('@ant-design/icons-vue')['CheckCircleOutlined']
|
||||||
CloseCircleOutlined: typeof import('@ant-design/icons-vue')['CloseCircleOutlined']
|
|
||||||
CloseOutlined: typeof import('@ant-design/icons-vue')['CloseOutlined']
|
CloseOutlined: typeof import('@ant-design/icons-vue')['CloseOutlined']
|
||||||
Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default']
|
Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default']
|
||||||
|
CloudUploadOutlined: typeof import('@ant-design/icons-vue')['CloudUploadOutlined']
|
||||||
CommentInput: typeof import('./src/components/CommentReply/src/CommentInput/CommentInput.vue')['default']
|
CommentInput: typeof import('./src/components/CommentReply/src/CommentInput/CommentInput.vue')['default']
|
||||||
CommentList: typeof import('./src/components/CommentReply/src/CommentList/CommentList.vue')['default']
|
CommentList: typeof import('./src/components/CommentReply/src/CommentList/CommentList.vue')['default']
|
||||||
CommentReply: typeof import('./src/components/CommentReply/index.vue')['default']
|
CommentReply: typeof import('./src/components/CommentReply/index.vue')['default']
|
||||||
CompareImage: typeof import('./src/views/Upscale/CompareImage.vue')['default']
|
CompareImage: typeof import('./src/views/Upscale/CompareImage.vue')['default']
|
||||||
CopyOutlined: typeof import('@ant-design/icons-vue')['CopyOutlined']
|
|
||||||
DeleteOutlined: typeof import('@ant-design/icons-vue')['DeleteOutlined']
|
DeleteOutlined: typeof import('@ant-design/icons-vue')['DeleteOutlined']
|
||||||
DownloadOutlined: typeof import('@ant-design/icons-vue')['DownloadOutlined']
|
DownloadOutlined: typeof import('@ant-design/icons-vue')['DownloadOutlined']
|
||||||
DownOutlined: typeof import('@ant-design/icons-vue')['DownOutlined']
|
DownOutlined: typeof import('@ant-design/icons-vue')['DownOutlined']
|
||||||
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
|
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
|
||||||
EyeInvisibleOutlined: typeof import('@ant-design/icons-vue')['EyeInvisibleOutlined']
|
EyeInvisibleOutlined: typeof import('@ant-design/icons-vue')['EyeInvisibleOutlined']
|
||||||
EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined']
|
|
||||||
FileImageOutlined: typeof import('@ant-design/icons-vue')['FileImageOutlined']
|
FileImageOutlined: typeof import('@ant-design/icons-vue')['FileImageOutlined']
|
||||||
Folder: typeof import('./src/components/Folder/Folder.vue')['default']
|
Folder: typeof import('./src/components/Folder/Folder.vue')['default']
|
||||||
ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default']
|
ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default']
|
||||||
GradientText: typeof import('./src/components/MyUI/GradientText/GradientText.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']
|
ImageToolbar: typeof import('./src/views/Photograph/ImageToolbar/ImageToolbar.vue')['default']
|
||||||
ImageUpload: typeof import('./src/views/Photograph/ImageUpload/ImageUpload.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']
|
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
|
||||||
LeftOutlined: typeof import('@ant-design/icons-vue')['LeftOutlined']
|
LeftOutlined: typeof import('@ant-design/icons-vue')['LeftOutlined']
|
||||||
LinkOutlined: typeof import('@ant-design/icons-vue')['LinkOutlined']
|
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']
|
Phoalbum: typeof import('./src/views/Album/Phoalbum/Phoalbum.vue')['default']
|
||||||
PhoalbumDetail: typeof import('./src/views/Album/Phoalbum/PhoalbumDetail.vue')['default']
|
PhoalbumDetail: typeof import('./src/views/Album/Phoalbum/PhoalbumDetail.vue')['default']
|
||||||
PhoalbumList: typeof import('./src/views/Album/Phoalbum/PhoalbumList.vue')['default']
|
PhoalbumList: typeof import('./src/views/Album/Phoalbum/PhoalbumList.vue')['default']
|
||||||
PhoneUpload: typeof import('./src/views/Phone/UpscalePhoneUpload/PhoneUpload.vue')['default']
|
PhoneOutlined: typeof import('@ant-design/icons-vue')['PhoneOutlined']
|
||||||
PhotoStack: typeof import('./src/components/PhotoStack/PhotoStack.vue')['default']
|
PhotoStack: typeof import('./src/components/PhotoStack/PhotoStack.vue')['default']
|
||||||
PlusOutlined: typeof import('@ant-design/icons-vue')['PlusOutlined']
|
PlusOutlined: typeof import('@ant-design/icons-vue')['PlusOutlined']
|
||||||
PlusSquareOutlined: typeof import('@ant-design/icons-vue')['PlusSquareOutlined']
|
PlusSquareOutlined: typeof import('@ant-design/icons-vue')['PlusSquareOutlined']
|
||||||
|
QrcodeOutlined: typeof import('@ant-design/icons-vue')['QrcodeOutlined']
|
||||||
QRLogin: typeof import('./src/views/QRLogin/QRLogin.vue')['default']
|
QRLogin: typeof import('./src/views/QRLogin/QRLogin.vue')['default']
|
||||||
QRLoginFooter: typeof import('./src/views/QRLogin/QRLoginFooter.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']
|
Rate: typeof import('./src/components/MyUI/Rate/Rate.vue')['default']
|
||||||
RecentUpload: typeof import('./src/views/Photograph/RecentUpload/RecentUpload.vue')['default']
|
RecentUpload: typeof import('./src/views/Photograph/RecentUpload/RecentUpload.vue')['default']
|
||||||
RecyclingBin: typeof import('./src/views/RecyclingBin/RecyclingBin.vue')['default']
|
RecyclingBin: typeof import('./src/views/RecyclingBin/RecyclingBin.vue')['default']
|
||||||
@@ -126,10 +117,11 @@ declare module 'vue' {
|
|||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
SafetyOutlined: typeof import('@ant-design/icons-vue')['SafetyOutlined']
|
SafetyOutlined: typeof import('@ant-design/icons-vue')['SafetyOutlined']
|
||||||
SearchOutlined: typeof import('@ant-design/icons-vue')['SearchOutlined']
|
SearchOutlined: typeof import('@ant-design/icons-vue')['SearchOutlined']
|
||||||
SendOutlined: typeof import('@ant-design/icons-vue')['SendOutlined']
|
|
||||||
ShareAltOutlined: typeof import('@ant-design/icons-vue')['ShareAltOutlined']
|
ShareAltOutlined: typeof import('@ant-design/icons-vue')['ShareAltOutlined']
|
||||||
SharePhoneUpload: typeof import('./src/views/Phone/SharePhoneUpload/SharePhoneUpload.vue')['default']
|
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']
|
Spin: typeof import('./src/components/MyUI/Spin/Spin.vue')['default']
|
||||||
StarButton: typeof import('./src/components/StarButton/StarButton.vue')['default']
|
StarButton: typeof import('./src/components/StarButton/StarButton.vue')['default']
|
||||||
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
|
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
|
||||||
@@ -138,11 +130,11 @@ declare module 'vue' {
|
|||||||
ThingAlbumList: typeof import('./src/views/Album/ThingAlbum/ThingAlbumList.vue')['default']
|
ThingAlbumList: typeof import('./src/views/Album/ThingAlbum/ThingAlbumList.vue')['default']
|
||||||
Tooltip: typeof import('./src/components/MyUI/Tooltip/Tooltip.vue')['default']
|
Tooltip: typeof import('./src/components/MyUI/Tooltip/Tooltip.vue')['default']
|
||||||
UploadImage: typeof import('./src/views/Upscale/UploadImage.vue')['default']
|
UploadImage: typeof import('./src/views/Upscale/UploadImage.vue')['default']
|
||||||
|
UploadOutlined: typeof import('@ant-design/icons-vue')['UploadOutlined']
|
||||||
Upscale: typeof import('./src/views/Upscale/index.vue')['default']
|
Upscale: typeof import('./src/views/Upscale/index.vue')['default']
|
||||||
UpscalePhoneUpload: typeof import('./src/views/Phone/UpscalePhoneUpload/UpscalePhoneUpload.vue')['default']
|
UpscalePhoneUpload: typeof import('./src/views/Phone/UpscalePhoneUpload/UpscalePhoneUpload.vue')['default']
|
||||||
UserInfoCard: typeof import('./src/components/CommentReply/src/UserInfoCard/UserInfoCard.vue')['default']
|
UserInfoCard: typeof import('./src/components/CommentReply/src/UserInfoCard/UserInfoCard.vue')['default']
|
||||||
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
|
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
|
||||||
VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default']
|
VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default']
|
||||||
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 ."
|
"docker-build": "docker build -t schisandra/schisandra-cloud-album-front ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alova/adapter-axios": "^2.0.12",
|
"@alova/adapter-axios": "^2.0.13",
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
"@intlify/eslint-plugin-vue-i18n": "^3.2.0",
|
"@intlify/eslint-plugin-vue-i18n": "^3.2.0",
|
||||||
"@mediapipe/face_detection": "^0.4.1646425229",
|
"@mediapipe/face_detection": "^0.4.1646425229",
|
||||||
@@ -27,10 +27,10 @@
|
|||||||
"@tensorflow/tfjs-backend-webgpu": "^4.22.0",
|
"@tensorflow/tfjs-backend-webgpu": "^4.22.0",
|
||||||
"@tensorflow/tfjs-converter": "^4.22.0",
|
"@tensorflow/tfjs-converter": "^4.22.0",
|
||||||
"@tensorflow/tfjs-core": "^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/crypto-js": "^4.2.2",
|
||||||
"@types/json-stringify-safe": "^5.0.3",
|
"@types/json-stringify-safe": "^5.0.3",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.5",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@vladmandic/face-api": "^1.7.15",
|
"@vladmandic/face-api": "^1.7.15",
|
||||||
"@vuepic/vue-datepicker": "^11.0.1",
|
"@vuepic/vue-datepicker": "^11.0.1",
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"eslint": "9.20.1",
|
"eslint": "9.21.0",
|
||||||
"exifr": "^7.1.3",
|
"exifr": "^7.1.3",
|
||||||
"go-captcha-vue": "^2.0.6",
|
"go-captcha-vue": "^2.0.6",
|
||||||
"gsap": "^3.12.7",
|
"gsap": "^3.12.7",
|
||||||
@@ -70,13 +70,13 @@
|
|||||||
"vue-i18n": "^11.1.1",
|
"vue-i18n": "^11.1.1",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"vue3-justified-layout": "^0.0.6",
|
"vue3-justified-layout": "^0.0.6",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.20.0",
|
"@eslint/js": "^9.21.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"eslint-plugin-vue": "^9.32.0",
|
"eslint-plugin-vue": "^9.32.0",
|
||||||
"globals": "^15.15.0",
|
"globals": "^16.0.0",
|
||||||
"sass": "^1.85.0",
|
"sass": "^1.85.0",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.24.1",
|
"typescript-eslint": "^8.24.1",
|
||||||
|
@@ -14,12 +14,12 @@ export const shareImageUploadApi = (formData) => {
|
|||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* 查询分享图片列表
|
* 查询分享图片列表
|
||||||
* @param share_code
|
* @param invite_code
|
||||||
* @param access_password
|
* @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', {
|
return service.Post('/api/auth/share/image/list', {
|
||||||
share_code: share_code,
|
invite_code: invite_code,
|
||||||
access_password: access_password,
|
access_password: access_password,
|
||||||
}, {
|
}, {
|
||||||
meta: {
|
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,
|
ignoreToken: false,
|
||||||
signature: 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,
|
ignoreToken: false,
|
||||||
signature: 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,
|
ignoreToken: false,
|
||||||
signature: false,
|
signature: false,
|
||||||
},
|
},
|
||||||
hitSource: ["upload-file"],
|
hitSource: ["upload-file", "delete-images"],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -221,7 +223,7 @@ export const queryRecentImagesApi = () => {
|
|||||||
ignoreToken: false,
|
ignoreToken: false,
|
||||||
signature: false,
|
signature: false,
|
||||||
},
|
},
|
||||||
hitSource: ["upload-file"],
|
hitSource: ["upload-file", "delete-images"],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
@@ -240,7 +242,7 @@ export const queryLocationAlbumApi = (provider: string, bucket: string) => {
|
|||||||
ignoreToken: false,
|
ignoreToken: false,
|
||||||
signature: 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,
|
ignoreToken: false,
|
||||||
signature: false,
|
signature: false,
|
||||||
},
|
},
|
||||||
hitSource: ["upload-file"],
|
hitSource: ["upload-file", "delete-images"],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -283,7 +285,7 @@ export const queryThingAlbumApi = (provider: string, bucket: string) => {
|
|||||||
ignoreToken: false,
|
ignoreToken: false,
|
||||||
signature: 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,
|
ignoreToken: false,
|
||||||
signature: false,
|
signature: false,
|
||||||
},
|
},
|
||||||
hitSource: ["upload-file"],
|
hitSource: ["upload-file", "delete-images"],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -327,6 +329,7 @@ export const getSingleImageApi = (id: number) => {
|
|||||||
ignoreToken: false,
|
ignoreToken: false,
|
||||||
signature: false,
|
signature: false,
|
||||||
},
|
},
|
||||||
|
name: "single-image-url",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
@@ -342,7 +345,68 @@ export const getStorageConfigListApi = () => {
|
|||||||
ignoreToken: false,
|
ignoreToken: false,
|
||||||
signature: 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">
|
<div class="sidebar-bottom">
|
||||||
<ACard :bordered="false" style="box-shadow: none">
|
<ACard :bordered="false" style="box-shadow: none">
|
||||||
<Folder/>
|
<Folder/>
|
||||||
<span class="sidebar-folder-text-title">30% In-used</span>
|
<span class="sidebar-folder-text-title">{{ bucketCapacityInfo?.percentage }}% In-used
|
||||||
<AProgress :percent="30" size="small" :showInfo="false" style="width: 150px"/>
|
<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">
|
<AFlex :vertical="false" align="center" justify="space-between" style="width: 150px">
|
||||||
<span class="sidebar-folder-info-text1">500G</span>
|
<span class="sidebar-folder-info-text1">{{ bucketCapacityInfo?.used }}</span>
|
||||||
<span class="sidebar-folder-info-text2">500G</span>
|
<span class="sidebar-folder-info-text2">{{ bucketCapacityInfo?.capacity }}</span>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
</ACard>
|
</ACard>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,11 +101,13 @@ import Folder from "@/components/Folder/Folder.vue";
|
|||||||
import ai from '@/assets/svgs/ai.svg';
|
import ai from '@/assets/svgs/ai.svg';
|
||||||
import share from '@/assets/svgs/share.svg';
|
import share from '@/assets/svgs/share.svg';
|
||||||
import useStore from "@/store";
|
import useStore from "@/store";
|
||||||
|
import {getBucketCapacityApi} from "@/api/storage";
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const menu = useStore().menu;
|
const menu = useStore().menu;
|
||||||
|
const upload = useStore().upload;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* handle click event of menu item
|
* handle click event of menu item
|
||||||
@@ -117,6 +123,14 @@ const menuCSSStyle: any = reactive({
|
|||||||
alignItems: 'center',
|
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(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
@@ -135,9 +149,10 @@ function scrollToSelectedMenuItem() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
menu.currentMenu = route.path.replace('/main', '').split('/').slice(0, 3).join('/').substring(1);
|
menu.currentMenu = route.path.replace('/main', '').split('/').slice(0, 3).join('/').substring(1);
|
||||||
scrollToSelectedMenuItem();
|
scrollToSelectedMenuItem();
|
||||||
|
await getBucketCapacity();
|
||||||
});
|
});
|
||||||
router.afterEach((_to) => {
|
router.afterEach((_to) => {
|
||||||
menu.currentMenu = route.path.replace('/main', '').split('/').slice(0, 3).join('/').substring(1);
|
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 photo from "@/router/modules/photos.ts";
|
||||||
import albums from "@/router/modules/albums.ts";
|
import albums from "@/router/modules/albums.ts";
|
||||||
import recycling_bin from "@/router/modules/recycling_bin.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 [
|
export default [
|
||||||
{
|
{
|
||||||
path: '/main',
|
path: '/main',
|
||||||
name: 'main',
|
name: 'main',
|
||||||
redirect: '/main/photos',
|
redirect: '/main/photo/all',
|
||||||
component: () => import('@/views/Main/MainPage.vue'),
|
component: () => import('@/views/Main/MainPage.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
@@ -16,25 +18,18 @@ export default [
|
|||||||
...photo,
|
...photo,
|
||||||
...albums,
|
...albums,
|
||||||
...recycling_bin,
|
...recycling_bin,
|
||||||
{
|
...share,
|
||||||
path: '/main/photo/upscale',
|
...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: '快传'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
}, {
|
||||||
|
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 {useUpscaleStore} from "@/store/modules/upscaleStore.ts";
|
||||||
import {useMenuStore} from "@/store/modules/menuStore.ts";
|
import {useMenuStore} from "@/store/modules/menuStore.ts";
|
||||||
import {useUploadStore} from "@/store/modules/uploadStore.ts";
|
import {useUploadStore} from "@/store/modules/uploadStore.ts";
|
||||||
|
import {useImageStore} from "@/store/modules/imageStore.ts";
|
||||||
|
|
||||||
export default function useStore() {
|
export default function useStore() {
|
||||||
return {
|
return {
|
||||||
@@ -17,5 +18,6 @@ export default function useStore() {
|
|||||||
upscale: useUpscaleStore(),
|
upscale: useUpscaleStore(),
|
||||||
menu: useMenuStore(),
|
menu: useMenuStore(),
|
||||||
upload: useUploadStore(),
|
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'));
|
message.success(t('login.loginSuccess'));
|
||||||
window.removeEventListener("message", messageHandler);
|
window.removeEventListener("message", messageHandler);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
const currentUrl = new URL(window.location.href);
|
||||||
|
const redirect = currentUrl.searchParams.get('redirect');
|
||||||
|
if (redirect) {
|
||||||
|
router.push(redirect);
|
||||||
|
} else {
|
||||||
router.push('/main/photo/all');
|
router.push('/main/photo/all');
|
||||||
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
message.error(t('login.loginError'));
|
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">
|
<div class="location-detail-content-nav">
|
||||||
<AButton size="large" type="text" class="location-detail-content-nav-title" @click="goBack">地点</AButton>
|
<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-separator"> > </span>
|
||||||
<span class="location-detail-content-nav-name">乌鲁木齐</span>
|
<span class="location-detail-content-nav-name">{{ route.query.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ImageToolbar :selected="imageStore.selected" :imageList="albumList"/>
|
||||||
<div class="location-album-detail-info">
|
<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>
|
||||||
<div class="location-album-detail-list">
|
<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">
|
<div v-for="(itemList, index) in albumList" :key="index">
|
||||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||||
<AImagePreviewGroup>
|
<AImagePreviewGroup>
|
||||||
@@ -21,7 +22,7 @@
|
|||||||
class="photo-item"
|
class="photo-item"
|
||||||
margin="0"
|
margin="0"
|
||||||
border-radius="0"
|
border-radius="0"
|
||||||
v-model="selected"
|
v-model="imageStore.selected"
|
||||||
:showHoverCircle="true"
|
:showHoverCircle="true"
|
||||||
:iconSize="20"
|
:iconSize="20"
|
||||||
:showSelectedEffect="true"
|
:showSelectedEffect="true"
|
||||||
@@ -41,6 +42,15 @@
|
|||||||
</AImagePreviewGroup>
|
</AImagePreviewGroup>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
@@ -50,8 +60,10 @@ import Vue3JustifiedLayout from "vue3-justified-layout";
|
|||||||
import 'vue3-justified-layout/dist/style.css';
|
import 'vue3-justified-layout/dist/style.css';
|
||||||
import {queryLocationDetailListApi} from "@/api/storage";
|
import {queryLocationDetailListApi} from "@/api/storage";
|
||||||
import useStore from "@/store";
|
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 albumList = ref<any[]>([]);
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<div class="location-album-content-item" v-for="(item, index) in locationAlbums" :key="index">
|
<div class="location-album-content-item" v-for="(item, index) in locationAlbums" :key="index">
|
||||||
<span class="location-album-description">{{ item.location }}</span>
|
<span class="location-album-description">{{ item.location }}</span>
|
||||||
<div class="location-album-location-list">
|
<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">
|
v-for="(itemList, indexItem) in item.list" :key="indexItem">
|
||||||
<img class="background-image" :src="itemList.cover_image" :alt="itemList.city"/>
|
<img class="background-image" :src="itemList.cover_image" :alt="itemList.city"/>
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
@@ -30,8 +30,8 @@ const route = useRoute();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const upload = useStore().upload;
|
const upload = useStore().upload;
|
||||||
|
|
||||||
function handleClick(id: number) {
|
function handleClick(id: number,name: string) {
|
||||||
router.push({path: route.path + `/${id}`});
|
router.push({path: route.path + `/${id}`, query: {name: name}});
|
||||||
}
|
}
|
||||||
|
|
||||||
const locationAlbums = ref<any[]>([]);
|
const locationAlbums = ref<any[]>([]);
|
||||||
|
@@ -11,16 +11,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="people-album-detail-toolbar">
|
<div class="people-album-detail-toolbar">
|
||||||
<AAvatar shape="circle" size="default"></AAvatar>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<ImageToolbar :selected="selected"/>
|
<ImageToolbar :selected="imageStore.selected" :imageList="images"/>
|
||||||
<div class="people-album-detail-info">
|
<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>
|
||||||
<div class="people-album-detail-list">
|
<div class="people-album-detail-list">
|
||||||
<div style="width:100%;height:100%;">
|
<div style="width:100%;height:100%;" v-if="images.length !== 0">
|
||||||
<div v-for="(itemList, index) in albumList" :key="index">
|
<div v-for="(itemList, index) in images" :key="index">
|
||||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||||
<AImagePreviewGroup>
|
<AImagePreviewGroup>
|
||||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options">
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
class="photo-item"
|
class="photo-item"
|
||||||
margin="0"
|
margin="0"
|
||||||
border-radius="0"
|
border-radius="0"
|
||||||
v-model="selected"
|
v-model="imageStore.selected"
|
||||||
:showHoverCircle="true"
|
:showHoverCircle="true"
|
||||||
:iconSize="20"
|
:iconSize="20"
|
||||||
:showSelectedEffect="true"
|
:showSelectedEffect="true"
|
||||||
@@ -49,6 +49,15 @@
|
|||||||
</AImagePreviewGroup>
|
</AImagePreviewGroup>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -58,10 +67,11 @@ import 'vue3-justified-layout/dist/style.css';
|
|||||||
import {getFaceSamplesDetailList} from "@/api/storage";
|
import {getFaceSamplesDetailList} from "@/api/storage";
|
||||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||||
import useStore from "@/store";
|
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 images = ref<any[]>([]);
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -74,7 +84,7 @@ const options = reactive({
|
|||||||
async function getAlbumList(id: number) {
|
async function getAlbumList(id: number) {
|
||||||
const res: any = await getFaceSamplesDetailList(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
const res: any = await getFaceSamplesDetailList(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
albumList.value = res.data.records;
|
images.value = res.data.records;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
<div class="people-album-header">
|
<div class="people-album-header">
|
||||||
<ADropdown trigger="click">
|
<ADropdown trigger="click">
|
||||||
<AButton type="text" size="large" class="people-album-button">
|
<AButton type="text" size="large" class="people-album-button">
|
||||||
{{ selecetedKey === '0' ? '人 物' : '已隐藏' }}
|
{{ selectedKey === '0' ? '人 物' : '已隐藏' }}
|
||||||
<DownOutlined class="people-album-icon"/>
|
<DownOutlined class="people-album-icon"/>
|
||||||
</AButton>
|
</AButton>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<AMenu selectable :selectedKeys="[selecetedKey]" @select="handleSelect">
|
<AMenu selectable :selectedKeys="[selectedKey]" @select="handleSelect">
|
||||||
<AMenuItem key="0">人 物</AMenuItem>
|
<AMenuItem key="0">人 物</AMenuItem>
|
||||||
<AMenuItem key="1">已隐藏</AMenuItem>
|
<AMenuItem key="1">已隐藏</AMenuItem>
|
||||||
</AMenu>
|
</AMenu>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<div class="people-album-toolbar-right">
|
<div class="people-album-toolbar-right">
|
||||||
|
|
||||||
<AButton type="text" shape="default" size="middle" class="people-album-toolbar-btn"
|
<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>
|
<template #icon>
|
||||||
<BlockOutlined class="people-album-toolbar-icon"/>
|
<BlockOutlined class="people-album-toolbar-icon"/>
|
||||||
</template>
|
</template>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<EyeInvisibleOutlined class="people-album-toolbar-icon"/>
|
<EyeInvisibleOutlined class="people-album-toolbar-icon"/>
|
||||||
</template>
|
</template>
|
||||||
{{ selecetedKey === '0' ? '隐藏人物' : '取消隐藏' }}
|
{{ selectedKey === '0' ? '隐藏人物' : '取消隐藏' }}
|
||||||
</AButton>
|
</AButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
<CheckCard
|
<CheckCard
|
||||||
v-for="(item, index) in faceList"
|
v-for="(item, index) in faceList"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click="handleClick(item.id)"
|
@click="handleClick(item.id, item.face_name)"
|
||||||
class="photo-item"
|
class="photo-item"
|
||||||
margin="0"
|
margin="0"
|
||||||
border-radius="0"
|
border-radius="0"
|
||||||
@@ -125,7 +125,7 @@ import {getFaceSamplesList, modifyFaceSampleName, modifyFaceTypeBatch} from "@/a
|
|||||||
|
|
||||||
const faceList = ref<any[]>([]);
|
const faceList = ref<any[]>([]);
|
||||||
const addNameInputValue = ref<string>('');
|
const addNameInputValue = ref<string>('');
|
||||||
const selecetedKey = ref<string>('0');
|
const selectedKey = ref<string>('0');
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const selected = ref<any[]>([]);
|
const selected = ref<any[]>([]);
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ async function modifyFaceName(id: number, index: number) {
|
|||||||
* @param key
|
* @param key
|
||||||
*/
|
*/
|
||||||
function handleSelect({key}) {
|
function handleSelect({key}) {
|
||||||
selecetedKey.value = key;
|
selectedKey.value = key;
|
||||||
getFaceList(parseInt(key));
|
getFaceList(parseInt(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ function cancelSelectPeople() {
|
|||||||
*/
|
*/
|
||||||
async function hiddenFace() {
|
async function hiddenFace() {
|
||||||
if (selected.value.length === 0) return;
|
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) {
|
if (res && res.code === 200) {
|
||||||
await getFaceList();
|
await getFaceList();
|
||||||
selected.value = [];
|
selected.value = [];
|
||||||
@@ -216,9 +216,10 @@ const router = useRouter();
|
|||||||
/**
|
/**
|
||||||
* 点击人物跳转到详情页
|
* 点击人物跳转到详情页
|
||||||
* @param id
|
* @param id
|
||||||
|
* @param name
|
||||||
*/
|
*/
|
||||||
function handleClick(id: number) {
|
function handleClick(id: number, name: string | null) {
|
||||||
router.push({path: route.path + `/${id}`});
|
router.push({path: route.path + `/${id}`, query: {name: name}});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@@ -26,17 +26,18 @@
|
|||||||
下载相册
|
下载相册
|
||||||
</AButton>
|
</AButton>
|
||||||
</div>
|
</div>
|
||||||
<ImageToolbar :selected="selected"/>
|
<ImageToolbar :selected="imageStore.selected" :image-list="albumList"/>
|
||||||
<div class="phoalbum-detail-content">
|
<div class="phoalbum-detail-content">
|
||||||
<div class="phoalbum-detail-content-nav">
|
<div class="phoalbum-detail-content-nav">
|
||||||
<div class="phoalbum-detail-content-nav-left">
|
<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>
|
</AButton>
|
||||||
<span class="phoalbum-detail-content-nav-separator"> > </span>
|
<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>
|
||||||
<div class="phoalbum-detail-content-nav-right">
|
<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>
|
</div>
|
||||||
<div class="phoalbum-detail-content-desc">
|
<div class="phoalbum-detail-content-desc">
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
<span>相册描述</span>
|
<span>相册描述</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="phoalbum-detail-content-list">
|
<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">
|
<div v-for="(itemList, index) in albumList" :key="index">
|
||||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||||
<AImagePreviewGroup>
|
<AImagePreviewGroup>
|
||||||
@@ -54,7 +55,7 @@
|
|||||||
class="photo-item"
|
class="photo-item"
|
||||||
margin="0"
|
margin="0"
|
||||||
border-radius="0"
|
border-radius="0"
|
||||||
v-model="selected"
|
v-model="imageStore.selected"
|
||||||
:showHoverCircle="true"
|
:showHoverCircle="true"
|
||||||
:iconSize="20"
|
:iconSize="20"
|
||||||
:showSelectedEffect="true"
|
:showSelectedEffect="true"
|
||||||
@@ -74,6 +75,15 @@
|
|||||||
</AImagePreviewGroup>
|
</AImagePreviewGroup>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,9 +94,10 @@ import 'vue3-justified-layout/dist/style.css';
|
|||||||
import {queryAlbumDetailListApi} from "@/api/storage";
|
import {queryAlbumDetailListApi} from "@/api/storage";
|
||||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||||
import useStore from "@/store";
|
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 albumList = ref<any[]>([]);
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@@ -31,7 +31,7 @@
|
|||||||
排序
|
排序
|
||||||
</AButton>
|
</AButton>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<AMenu selectable :selectedKeys="[selecetedKey]" @select="handleSelect">
|
<AMenu selectable :selectedKeys="[selectedKey]" @select="handleSelect">
|
||||||
<AMenuItem :key="true">按时间排序</AMenuItem>
|
<AMenuItem :key="true">按时间排序</AMenuItem>
|
||||||
<AMenuItem :key="false">按名称排序</AMenuItem>
|
<AMenuItem :key="false">按名称排序</AMenuItem>
|
||||||
</AMenu>
|
</AMenu>
|
||||||
@@ -49,18 +49,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="phoalbum-content">
|
<div class="phoalbum-content">
|
||||||
<ATabs size="small" :tabBarGutter="50" type="line" tabPosition="top" :tabBarStyle="{position:'unset'}"
|
<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>
|
<template #rightExtra>
|
||||||
<span
|
<span
|
||||||
style="color: #999; font-size: 12px;">已全部加载,共 {{ albumList ? albumList.length : 0 }} 个相册</span>
|
style="color: #999; font-size: 12px;">已全部加载,共 {{ albumList ? albumList.length : 0 }} 个相册</span>
|
||||||
</template>
|
</template>
|
||||||
<ATabPane key="-1" tab="全部相册">
|
<ATabPane key="-1" :tab="imageStore.tabMap[-1]">
|
||||||
<ASpin tip="Loading..." :spinning="loading" size="large">
|
<ASpin tip="Loading..." :spinning="loading" size="large">
|
||||||
<div class="phoalbum-item-container">
|
<div class="phoalbum-item-container">
|
||||||
<div class="phoalbum-item"
|
<div class="phoalbum-item"
|
||||||
v-for="(album, index) in albumList"
|
v-for="(album, index) in albumList"
|
||||||
:key="album.id"
|
:key="album.id"
|
||||||
@click.prevent="handleClick(album.id)"
|
@click.prevent="handleClick(album.id,album.name)"
|
||||||
@mouseover="isHovered = index"
|
@mouseover="isHovered = index"
|
||||||
@mouseleave="isHovered = null">
|
@mouseleave="isHovered = null">
|
||||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||||
@@ -104,13 +106,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</ASpin>
|
</ASpin>
|
||||||
</ATabPane>
|
</ATabPane>
|
||||||
<ATabPane key="0" tab="我的相册">
|
<ATabPane key="0" :tab="imageStore.tabMap[0]">
|
||||||
<ASpin tip="Loading..." :spinning="loading" size="large">
|
<ASpin tip="Loading..." :spinning="loading" size="large">
|
||||||
<div class="phoalbum-item-container">
|
<div class="phoalbum-item-container">
|
||||||
<div class="phoalbum-item"
|
<div class="phoalbum-item"
|
||||||
v-for="(album, index) in albumList"
|
v-for="(album, index) in albumList"
|
||||||
:key="album.id"
|
:key="album.id"
|
||||||
@click.prevent="handleClick(album.id)"
|
@click.prevent="handleClick(album.id,album.name)"
|
||||||
@mouseover="isHovered = index"
|
@mouseover="isHovered = index"
|
||||||
@mouseleave="isHovered = null">
|
@mouseleave="isHovered = null">
|
||||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||||
@@ -154,13 +156,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</ASpin>
|
</ASpin>
|
||||||
</ATabPane>
|
</ATabPane>
|
||||||
<ATabPane key="1" tab="我的分享">
|
<ATabPane key="1" :tab="imageStore.tabMap[1]">
|
||||||
<ASpin tip="Loading..." :spinning="loading" size="large">
|
<ASpin tip="Loading..." :spinning="loading" size="large">
|
||||||
<div class="phoalbum-item-container">
|
<div class="phoalbum-item-container">
|
||||||
<div class="phoalbum-item"
|
<div class="phoalbum-item"
|
||||||
v-for="(album, index) in albumList"
|
v-for="(album, index) in albumList"
|
||||||
:key="album.id"
|
:key="album.id"
|
||||||
@click.prevent="handleClick(album.id)"
|
@click.prevent="handleClick(album.id,album.name)"
|
||||||
@mouseover="isHovered = index"
|
@mouseover="isHovered = index"
|
||||||
@mouseleave="isHovered = null">
|
@mouseleave="isHovered = null">
|
||||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
||||||
@@ -204,13 +206,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</ASpin>
|
</ASpin>
|
||||||
</ATabPane>
|
</ATabPane>
|
||||||
<ATabPane key="2" tab="收藏相册">
|
<ATabPane key="2" :tab="imageStore.tabMap[2]">
|
||||||
<ASpin tip="Loading..." :spinning="loading" size="large">
|
<ASpin tip="Loading..." :spinning="loading" size="large">
|
||||||
<div class="phoalbum-item-container">
|
<div class="phoalbum-item-container">
|
||||||
<div class="phoalbum-item"
|
<div class="phoalbum-item"
|
||||||
v-for="(album, index) in albumList"
|
v-for="(album, index) in albumList"
|
||||||
:key="album.id"
|
:key="album.id"
|
||||||
@click.prevent="handleClick(album.id)"
|
@click.prevent="handleClick(album.id,album.name)"
|
||||||
@mouseover="isHovered = index"
|
@mouseover="isHovered = index"
|
||||||
@mouseleave="isHovered = null">
|
@mouseleave="isHovered = null">
|
||||||
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
|
<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 {albumListApi, createAlbumApi, deleteAlbumApi, renameAlbumApi} from "@/api/storage";
|
||||||
import {message} from "ant-design-vue";
|
import {message} from "ant-design-vue";
|
||||||
import default_cover from "@/assets/images/default-cover.png";
|
import default_cover from "@/assets/images/default-cover.png";
|
||||||
|
import useStore from "@/store";
|
||||||
|
|
||||||
const isHovered = ref<number | null>(null);
|
const isHovered = ref<number | null>(null);
|
||||||
|
|
||||||
const albumNameValue = ref<string>("未命名相册");
|
const albumNameValue = ref<string>("未命名相册");
|
||||||
const albumRenameValue = ref<string>("");
|
const albumRenameValue = ref<string>("");
|
||||||
|
|
||||||
const selecetedKey = ref<boolean>(true);
|
const selectedKey = ref<boolean>(true);
|
||||||
|
|
||||||
const albumList = ref<any[]>([]);
|
const albumList = ref<any[]>([]);
|
||||||
|
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
|
const imageStore = useStore().image;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建相册
|
* 创建相册
|
||||||
*/
|
*/
|
||||||
@@ -286,7 +291,7 @@ async function createAlbumSubmit() {
|
|||||||
const res: any = await createAlbumApi(albumNameValue.value);
|
const res: any = await createAlbumApi(albumNameValue.value);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
albumNameValue.value = "未命名相册";
|
albumNameValue.value = "未命名相册";
|
||||||
await getAlbumList(0, selecetedKey.value);
|
await getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||||
} else {
|
} else {
|
||||||
message.error("创建相册失败");
|
message.error("创建相册失败");
|
||||||
}
|
}
|
||||||
@@ -297,8 +302,8 @@ async function createAlbumSubmit() {
|
|||||||
* @param key
|
* @param key
|
||||||
*/
|
*/
|
||||||
async function handleSelect({key}) {
|
async function handleSelect({key}) {
|
||||||
selecetedKey.value = key;
|
selectedKey.value = key;
|
||||||
await getAlbumList(0, key);
|
await getAlbumList(parseInt(imageStore.tabActiveKey), key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -306,7 +311,8 @@ async function handleSelect({key}) {
|
|||||||
* @param activeKey
|
* @param activeKey
|
||||||
*/
|
*/
|
||||||
async function handleTabChange(activeKey: string) {
|
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);
|
const res: any = await renameAlbumApi(id, name);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
albumRenameValue.value = "";
|
albumRenameValue.value = "";
|
||||||
await getAlbumList(0, selecetedKey.value);
|
await getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,7 +354,7 @@ async function renameAlbum(id: number, name: string) {
|
|||||||
async function deleteAlbum(id: number) {
|
async function deleteAlbum(id: number) {
|
||||||
const res: any = await deleteAlbumApi(id);
|
const res: any = await deleteAlbumApi(id);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
await getAlbumList(0, selecetedKey.value);
|
await getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||||
} else {
|
} else {
|
||||||
message.error("删除相册失败");
|
message.error("删除相册失败");
|
||||||
}
|
}
|
||||||
@@ -360,13 +366,16 @@ const router = useRouter();
|
|||||||
/**
|
/**
|
||||||
* 点击相册跳转到详情页
|
* 点击相册跳转到详情页
|
||||||
* @param id
|
* @param id
|
||||||
|
* @param albumName
|
||||||
*/
|
*/
|
||||||
function handleClick(id: number) {
|
function handleClick(id: number, albumName: string) {
|
||||||
router.push({path: route.path + `/${id}`});
|
router.push({
|
||||||
|
path: route.path + `/${id}`, query: {name: albumName}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getAlbumList(0, selecetedKey.value);
|
getAlbumList(parseInt(imageStore.tabActiveKey), selectedKey.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@@ -2,14 +2,16 @@
|
|||||||
<div class="thing-album-detail">
|
<div class="thing-album-detail">
|
||||||
<div class="thing-album-detail-header">
|
<div class="thing-album-detail-header">
|
||||||
<div class="thing-detail-content-nav">
|
<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-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>
|
||||||
</div>
|
</div>
|
||||||
<ImageToolbar :selected="selected"/>
|
<ImageToolbar :selected="imageStore.selected" :image-list="albumList"/>
|
||||||
<div class="thing-album-detail-info">
|
<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>
|
||||||
<div class="thing-album-detail-list">
|
<div class="thing-album-detail-list">
|
||||||
<div style="width:100%;height:100%;">
|
<div style="width:100%;height:100%;">
|
||||||
@@ -22,7 +24,7 @@
|
|||||||
class="photo-item"
|
class="photo-item"
|
||||||
margin="0"
|
margin="0"
|
||||||
border-radius="0"
|
border-radius="0"
|
||||||
v-model="selected"
|
v-model="imageStore.selected"
|
||||||
:showHoverCircle="true"
|
:showHoverCircle="true"
|
||||||
:iconSize="20"
|
:iconSize="20"
|
||||||
:showSelectedEffect="true"
|
:showSelectedEffect="true"
|
||||||
@@ -53,9 +55,10 @@ import 'vue3-justified-layout/dist/style.css';
|
|||||||
import {queryThingDetailListApi} from "@/api/storage";
|
import {queryThingDetailListApi} from "@/api/storage";
|
||||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
||||||
import useStore from "@/store";
|
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 albumList = ref<any[]>([]);
|
||||||
const upload = useStore().upload;
|
const upload = useStore().upload;
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -113,7 +116,7 @@ function goBack(): void {
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
width: 1000%;
|
width: 1000%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
gap: 10px;
|
gap: 5px;
|
||||||
|
|
||||||
.thing-detail-content-nav-title {
|
.thing-detail-content-nav-title {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
@@ -121,6 +124,7 @@ function goBack(): void {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
padding: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thing-detail-content-nav-separator {
|
.thing-detail-content-nav-separator {
|
||||||
@@ -141,13 +145,13 @@ function goBack(): void {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
margin-left: 30px;
|
margin-left: 15px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thing-album-detail-list {
|
.thing-album-detail-list {
|
||||||
width: 99%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
//margin-left: 5px;
|
//margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
<span class="thing-album-title">{{ getZhCategoryNameByEnName(item.category) }}</span>
|
<span class="thing-album-title">{{ getZhCategoryNameByEnName(item.category) }}</span>
|
||||||
<div class="thing-album-wrapper">
|
<div class="thing-album-wrapper">
|
||||||
<div class="thing-album-container" v-for="(tags, indexList) in item.list" :key="indexList"
|
<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"/>
|
<img class="background-image" :src="tags.cover_image" :alt="tags.tag_name"/>
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
<span>{{ getZhLabelNameByEnName(tags.tag_name) }}</span>
|
<span>{{ getZhLabelNameByEnName(tags.tag_name) }}</span>
|
||||||
@@ -46,9 +46,11 @@ const upload = useStore().upload;
|
|||||||
/**
|
/**
|
||||||
* 点击事件
|
* 点击事件
|
||||||
* @param id
|
* @param id
|
||||||
|
* @param tag
|
||||||
|
* @param category
|
||||||
*/
|
*/
|
||||||
function handleClick(id: string) {
|
function handleClick(id: string, category: string, tag: string) {
|
||||||
router.push({path: route.path + `/${id}`});
|
router.push({path: route.path + `/${id}`, query: {category: category, tag: tag}});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
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'));
|
message.success(t('login.loginSuccess'));
|
||||||
loginLoading.value = false;
|
loginLoading.value = false;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
const currentUrl = new URL(window.location.href);
|
||||||
|
const redirect = currentUrl.searchParams.get('redirect');
|
||||||
|
if (redirect) {
|
||||||
|
router.push(redirect);
|
||||||
|
} else {
|
||||||
router.push('/main/photo/all');
|
router.push('/main/photo/all');
|
||||||
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
loginLoading.value = false;
|
loginLoading.value = false;
|
||||||
@@ -472,7 +478,13 @@ async function checkAccountLoginCaptcha(angle: number) {
|
|||||||
loginLoading.value = false;
|
loginLoading.value = false;
|
||||||
showAccountRotateCaptcha.value = false;
|
showAccountRotateCaptcha.value = false;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
const currentUrl = new URL(window.location.href);
|
||||||
|
const redirect = currentUrl.searchParams.get('redirect');
|
||||||
|
if (redirect) {
|
||||||
|
router.push(redirect);
|
||||||
|
} else {
|
||||||
router.push('/main/photo/all');
|
router.push('/main/photo/all');
|
||||||
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
showAccountRotateCaptcha.value = false;
|
showAccountRotateCaptcha.value = false;
|
||||||
|
@@ -14,25 +14,29 @@
|
|||||||
创建相册
|
创建相册
|
||||||
</AButton>
|
</AButton>
|
||||||
</div>
|
</div>
|
||||||
<image-toolbar :selected="selected"/>
|
<image-toolbar :selected="imageStore.selected" :image-list="images"/>
|
||||||
<div class="photo-list">
|
<div class="photo-list">
|
||||||
<ATabs size="small" :tabBarGutter="50" type="line" tabPosition="top" :tabBarStyle="{position:'unset'}"
|
<ATabs size="small" :tabBarGutter="50" type="line" tabPosition="top" :tabBarStyle="{position:'unset'}"
|
||||||
|
@change="handleTabChange"
|
||||||
|
v-model:activeKey="imageStore.homeTabActiveKey"
|
||||||
style="width: 99%;">
|
style="width: 99%;">
|
||||||
<template #rightExtra>
|
<template #rightExtra>
|
||||||
<ASwitch size="small" v-model:checked="switchValue"/>
|
<ASwitch size="small" v-model:checked="switchValue"/>
|
||||||
</template>
|
</template>
|
||||||
<ATabPane key="image" tab="全部">
|
<ATabPane key="all" :tab="imageStore.homeTabMap['all']">
|
||||||
<div style="width:100%;height:100%;">
|
<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">
|
<div v-for="(itemList, index) in images" :key="index">
|
||||||
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
<span style="margin-left: 10px;font-size: 13px">{{ itemList.date }}</span>
|
||||||
<AImagePreviewGroup>
|
<AImagePreviewGroup>
|
||||||
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options" style="line-height: 0 !important;">
|
<Vue3JustifiedLayout v-model:list="itemList.list" :options="options"
|
||||||
|
style="line-height: 0 !important;">
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<CheckCard :key="index"
|
<CheckCard :key="index"
|
||||||
class="photo-item"
|
class="photo-item"
|
||||||
margin="0"
|
margin="0"
|
||||||
border-radius="0"
|
border-radius="0"
|
||||||
v-model="selected"
|
v-model="imageStore.selected"
|
||||||
:showHoverCircle="true"
|
:showHoverCircle="true"
|
||||||
:iconSize="20"
|
:iconSize="20"
|
||||||
:showSelectedEffect="true"
|
:showSelectedEffect="true"
|
||||||
@@ -54,17 +58,172 @@
|
|||||||
</AImagePreviewGroup>
|
</AImagePreviewGroup>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ATabPane>
|
<div v-else class="empty-content">
|
||||||
<ATabPane key="video" tab="视频">
|
<AEmpty :image="empty">
|
||||||
<div style="width:100%;height:100%;">
|
<template #description>
|
||||||
|
<span style="color: #999999;font-size: 16px;font-weight: 500;line-height: 1.5;">
|
||||||
|
还没检测到任何图片,快去上传吧!
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</AEmpty>
|
||||||
</div>
|
</div>
|
||||||
|
</ASpin>
|
||||||
</ATabPane>
|
</ATabPane>
|
||||||
<ATabPane key="gif" tab="动图">
|
<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>
|
||||||
<ATabPane key="screenshot" 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="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>
|
</ATabPane>
|
||||||
</ATabs>
|
</ATabs>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,9 +239,10 @@ import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
|||||||
import useStore from "@/store";
|
import useStore from "@/store";
|
||||||
import {queryAllImagesApi} from "@/api/storage";
|
import {queryAllImagesApi} from "@/api/storage";
|
||||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
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 switchValue = ref<boolean>(false);
|
||||||
const upload = useStore().upload;
|
const upload = useStore().upload;
|
||||||
|
|
||||||
@@ -91,20 +251,29 @@ const options = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const images = ref<any[]>([]);
|
const images = ref<any[]>([]);
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有图片
|
* 获取所有图片
|
||||||
*/
|
*/
|
||||||
async function getAllImages() {
|
async function getAllImages(type: string) {
|
||||||
const res: any = await queryAllImagesApi("image", false, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
images.value = [];
|
||||||
|
loading.value = true;
|
||||||
|
const res: any = await queryAllImagesApi(type, false, upload.storageSelected?.[0], upload.storageSelected?.[1]);
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
images.value = res.data.records;
|
images.value = res.data.records;
|
||||||
}
|
}
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleTabChange(activeKey: string) {
|
||||||
|
imageStore.homeTabActiveKey = activeKey;
|
||||||
|
await getAllImages(activeKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getAllImages();
|
getAllImages(imageStore.homeTabActiveKey);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -144,4 +313,13 @@ onMounted(() => {
|
|||||||
//transform: scale(0.99);
|
//transform: scale(0.99);
|
||||||
box-shadow: 0 0 10px 0 rgba(77, 167, 255, 0.89);
|
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>
|
</style>
|
||||||
|
@@ -1,18 +1,8 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
selected: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<transition name="slide-fade">
|
<transition name="slide-fade">
|
||||||
<div v-show="props.selected.length > 0" class="photo-toolbar-header">
|
<div v-show="props.selected.length > 0" class="photo-toolbar-header">
|
||||||
<div class="photo-toolbar-left">
|
<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>
|
<template #icon>
|
||||||
<CloseOutlined class="photo-toolbar-icon"/>
|
<CloseOutlined class="photo-toolbar-icon"/>
|
||||||
</template>
|
</template>
|
||||||
@@ -20,7 +10,7 @@ const props = defineProps({
|
|||||||
<span style="font-size: 16px;font-weight: bold">
|
<span style="font-size: 16px;font-weight: bold">
|
||||||
已选择 {{ props.selected.length }} 张照片
|
已选择 {{ props.selected.length }} 张照片
|
||||||
</span>
|
</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>
|
</AButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -43,7 +33,7 @@ const props = defineProps({
|
|||||||
</template>
|
</template>
|
||||||
分享
|
分享
|
||||||
</AButton>
|
</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>
|
<template #icon>
|
||||||
<DeleteOutlined class="photo-toolbar-icon"/>
|
<DeleteOutlined class="photo-toolbar-icon"/>
|
||||||
</template>
|
</template>
|
||||||
@@ -53,7 +43,41 @@ const props = defineProps({
|
|||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</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">
|
<style scoped lang="scss">
|
||||||
.photo-toolbar-header {
|
.photo-toolbar-header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@@ -2,10 +2,7 @@
|
|||||||
<ADrawer v-model:open="upload.openUploadDrawer" placement="right" title="上传照片" width="40%" @close="cancelUpload">
|
<ADrawer v-model:open="upload.openUploadDrawer" placement="right" title="上传照片" width="40%" @close="cancelUpload">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<AFlex :vertical="false" align="center" gap="large" justify="center">
|
<AFlex :vertical="false" align="center" gap="large" justify="center">
|
||||||
<ASelect size="middle" style="width: 150px">
|
<ASelect size="middle" style="width: 150px" placeholder="选择上传的相册">
|
||||||
|
|
||||||
</ASelect>
|
|
||||||
<ASelect size="middle" style="width: 150px">
|
|
||||||
|
|
||||||
</ASelect>
|
</ASelect>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
@@ -42,6 +39,13 @@
|
|||||||
:status="progressStatus"
|
:status="progressStatus"
|
||||||
:show-info="true" size="small" type="line" v-show="predicting" style="width: 80%"/>
|
:show-info="true" size="small" type="line" v-show="predicting" style="width: 80%"/>
|
||||||
</AUploadDragger>
|
</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>
|
</div>
|
||||||
</ADrawer>
|
</ADrawer>
|
||||||
</template>
|
</template>
|
||||||
@@ -63,6 +67,7 @@ import exifr from 'exifr';
|
|||||||
import isScreenshot from "@/utils/imageUtils/isScreenshot.ts";
|
import isScreenshot from "@/utils/imageUtils/isScreenshot.ts";
|
||||||
import {getCategoryByLabel} from "@/constant/coco_ssd_label_category.ts";
|
import {getCategoryByLabel} from "@/constant/coco_ssd_label_category.ts";
|
||||||
import {generateThumbnail} from "@/utils/imageUtils/generateThumb.ts";
|
import {generateThumbnail} from "@/utils/imageUtils/generateThumb.ts";
|
||||||
|
import empty from "@/assets/svgs/empty.svg";
|
||||||
|
|
||||||
const predicting = ref<boolean>(false);
|
const predicting = ref<boolean>(false);
|
||||||
const progressPercent = ref<number>(0);
|
const progressPercent = ref<number>(0);
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
创建相册
|
创建相册
|
||||||
</AButton>
|
</AButton>
|
||||||
</div>
|
</div>
|
||||||
<image-toolbar :selected="selected" />
|
<image-toolbar :selected="imageStore.selected" :image-list="images"/>
|
||||||
<div class="photo-list">
|
<div class="photo-list">
|
||||||
<div style="width:100%;height:100%;" v-if="images.length !== 0">
|
<div style="width:100%;height:100%;" v-if="images.length !== 0">
|
||||||
<div v-for="(itemList, index) in images" :key="index">
|
<div v-for="(itemList, index) in images" :key="index">
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
class="photo-item"
|
class="photo-item"
|
||||||
margin="0"
|
margin="0"
|
||||||
border-radius="0"
|
border-radius="0"
|
||||||
v-model="selected"
|
v-model="imageStore.selected"
|
||||||
:showHoverCircle="true"
|
:showHoverCircle="true"
|
||||||
:iconSize="20"
|
:iconSize="20"
|
||||||
:showSelectedEffect="true"
|
:showSelectedEffect="true"
|
||||||
@@ -43,6 +43,15 @@
|
|||||||
</AImagePreviewGroup>
|
</AImagePreviewGroup>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
<ImageUpload/>
|
<ImageUpload/>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,8 +64,9 @@ import useStore from "@/store";
|
|||||||
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
import ImageUpload from "@/views/Photograph/ImageUpload/ImageUpload.vue";
|
||||||
import {queryRecentImagesApi} from "@/api/storage";
|
import {queryRecentImagesApi} from "@/api/storage";
|
||||||
import ImageToolbar from "@/views/Photograph/ImageToolbar/ImageToolbar.vue";
|
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 upload = useStore().upload;
|
||||||
const images = ref<any[]>([]);
|
const images = ref<any[]>([]);
|
||||||
const options = reactive({
|
const options = reactive({
|
||||||
|
@@ -109,7 +109,13 @@ async function handleListenMessage() {
|
|||||||
userStore.token.expireAt = expire_at;
|
userStore.token.expireAt = expire_at;
|
||||||
message.success(t('login.loginSuccess'));
|
message.success(t('login.loginSuccess'));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
const currentUrl = new URL(window.location.href);
|
||||||
|
const redirect = currentUrl.searchParams.get('redirect');
|
||||||
|
if (redirect) {
|
||||||
|
router.push(redirect);
|
||||||
|
} else {
|
||||||
router.push('/main/photo/all');
|
router.push('/main/photo/all');
|
||||||
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
message.warning(t('login.loginError'));
|
message.warning(t('login.loginError'));
|
||||||
|
@@ -20,25 +20,42 @@
|
|||||||
:iconSize="20"
|
:iconSize="20"
|
||||||
:showSelectedEffect="true"
|
:showSelectedEffect="true"
|
||||||
:value="item.id">
|
:value="item.id">
|
||||||
<AImage :src="item.url"
|
<AImage :src="item.thumbnail"
|
||||||
:alt="item.file_name"
|
:alt="item.file_name"
|
||||||
:key="index"
|
:key="index"
|
||||||
style="height: 200px"
|
style="height: 200px"
|
||||||
:previewMask="false"
|
:preview="{
|
||||||
loading="lazy"/>
|
src: item.url,
|
||||||
|
}"
|
||||||
|
loading="lazy">
|
||||||
|
<template #previewMask>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</AImage>
|
||||||
</CheckCard>
|
</CheckCard>
|
||||||
</template>
|
</template>
|
||||||
</Vue3JustifiedLayout>
|
</Vue3JustifiedLayout>
|
||||||
</AImagePreviewGroup>
|
</AImagePreviewGroup>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Vue3JustifiedLayout from "vue3-justified-layout";
|
import Vue3JustifiedLayout from "vue3-justified-layout";
|
||||||
import 'vue3-justified-layout/dist/style.css';
|
import 'vue3-justified-layout/dist/style.css';
|
||||||
import {queryShareImageApi} from "@/api/share";
|
import {getDeletedRecordApi} from "@/api/storage";
|
||||||
|
import useStore from "@/store";
|
||||||
|
import empty from "@/assets/svgs/empty.svg";
|
||||||
|
|
||||||
|
|
||||||
const selected = ref<(string | number)[]>([]);
|
const selected = ref<(string | number)[]>([]);
|
||||||
@@ -46,14 +63,21 @@ const images = ref<any[]>([]);
|
|||||||
const options = reactive({
|
const options = reactive({
|
||||||
targetRowHeight: 200 // 高度
|
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>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@@ -5,15 +5,19 @@
|
|||||||
<div class="image-share-left-title">
|
<div class="image-share-left-title">
|
||||||
<h3>数据概览</h3>
|
<h3>数据概览</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="image-share-left-content">
|
<div class="image-share-left-content">
|
||||||
<ACard class="image-share-left-content-item"
|
<ACard class="image-share-left-content-item"
|
||||||
type="inner"
|
type="inner"
|
||||||
style="background: linear-gradient(102.74deg, rgb(66, 230, 171) -7.03%, rgb(103, 235, 187) 97.7%);">
|
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">
|
<div class="image-share-left-item-content">
|
||||||
<span style="font-weight: bolder;font-size: 2.3vh">浏览次数(次)</span>
|
<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);">今日浏览
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</ACard>
|
</ACard>
|
||||||
@@ -22,9 +26,12 @@
|
|||||||
style="background: linear-gradient(101.63deg, rgb(82, 138, 250) -12.83%, rgb(122, 167, 255) 100%);">
|
style="background: linear-gradient(101.63deg, rgb(82, 138, 250) -12.83%, rgb(122, 167, 255) 100%);">
|
||||||
<div class="image-share-left-item-content">
|
<div class="image-share-left-item-content">
|
||||||
<span style="font-weight: bolder;font-size: 2.3vh">浏览人数(人)</span>
|
<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);">今日浏览人数
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</ACard>
|
</ACard>
|
||||||
@@ -33,13 +40,17 @@
|
|||||||
style="background: linear-gradient(102.99deg, rgb(126, 92, 255) 3.18%, rgb(162, 139, 255) 102.52%);">
|
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">
|
<div class="image-share-left-item-content">
|
||||||
<span style="font-weight: bolder;font-size: 2.3vh">发布次数(次)</span>
|
<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);">今日发布
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</ACard>
|
</ACard>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="image-share-left-bottom">
|
<div class="image-share-left-bottom">
|
||||||
<div class="image-share-left-bottom-title">
|
<div class="image-share-left-bottom-title">
|
||||||
@@ -88,8 +99,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'action'">
|
<template v-else-if="column.key === 'action'">
|
||||||
<ATooltip title="复制分享链接">
|
<ATooltip title="复制分享链接">
|
||||||
<AButton type="text" size="small" @click="copyToClipboard(record.share_code)">
|
<AButton type="text" size="small"
|
||||||
<LinkOutlined />
|
@click="copyToClipboard(record.invite_code)">
|
||||||
|
<LinkOutlined/>
|
||||||
</AButton>
|
</AButton>
|
||||||
</ATooltip>
|
</ATooltip>
|
||||||
<ATooltip title="删除快传记录">
|
<ATooltip title="删除快传记录">
|
||||||
@@ -118,8 +130,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {Dayjs} from 'dayjs';
|
import {Dayjs} from 'dayjs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import ShareUpload from "@/views/ImageShare/ShareUpload.vue";
|
import ShareUpload from "@/views/Share/ImageShare/ShareUpload.vue";
|
||||||
import {queryShareRecordListApi} from "@/api/share";
|
import {queryShareOverviewApi, queryShareRecordListApi} from "@/api/share";
|
||||||
import {message} from "ant-design-vue";
|
import {message} from "ant-design-vue";
|
||||||
import 'dayjs/locale/zh-cn';
|
import 'dayjs/locale/zh-cn';
|
||||||
|
|
||||||
@@ -132,6 +144,7 @@ const selectedDateRange = ref<RangeValue>();
|
|||||||
const hackValue = ref<RangeValue>();
|
const hackValue = ref<RangeValue>();
|
||||||
|
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
|
const overviewDataLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
const disabledDate = (current: Dayjs) => {
|
const disabledDate = (current: Dayjs) => {
|
||||||
@@ -229,7 +242,8 @@ const formatValidityPeriod = (period: number) => {
|
|||||||
|
|
||||||
// 复制功能
|
// 复制功能
|
||||||
function copyToClipboard(text: string) {
|
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('复制成功');
|
message.success('复制成功');
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
message.error('复制失败');
|
message.error('复制失败');
|
||||||
@@ -250,13 +264,125 @@ async function getShareRecords(dateRange: string[]) {
|
|||||||
loading.value = false;
|
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 () => {
|
onMounted(async () => {
|
||||||
const endDate = dayjs().format('YYYY-MM-DD'); // 当前日期
|
const endDate = dayjs().format('YYYY-MM-DD'); // 当前日期
|
||||||
const startDate = dayjs().subtract(30, 'day').format('YYYY-MM-DD'); // 30 天前的日期
|
const startDate = dayjs().subtract(30, 'day').format('YYYY-MM-DD'); // 30 天前的日期
|
||||||
await getShareRecords([startDate, endDate]);
|
await getShareRecords([startDate, endDate]);
|
||||||
|
await getShareOverview();
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</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>
|
</style>
|
@@ -23,9 +23,14 @@
|
|||||||
</ABadge>
|
</ABadge>
|
||||||
</p>
|
</p>
|
||||||
<p class="ant-upload-text" style="font-size: 2.6vh;font-weight: bolder">单击或拖动文件到此区域以上传</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>
|
||||||
<div class="qr">
|
<CloudUploadOutlined/>
|
||||||
|
</template>
|
||||||
|
上 传 照 片
|
||||||
|
</AButton>
|
||||||
|
<APopover placement="top" trigger="click">
|
||||||
|
<template #content>
|
||||||
<AQrcode :bordered="false" color="rgba(126, 126, 135, 0.48)"
|
<AQrcode :bordered="false" color="rgba(126, 126, 135, 0.48)"
|
||||||
:size="qrcodeSize"
|
:size="qrcodeSize"
|
||||||
:value="`git.landaiqing.cneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjI1MTEyMjE3MzQyMDIxIiwidHlwZSI6ImFjY2VzcyIsImV4cCI6MTczOTg3ODIyOCwibmJmIjoxNzM5ODcxMDI4LCJpYXQiOjE3Mzk4NzEwMjh9.EUiZsVjhGqHx1V5o90S3W5li6nIqucxy9eEY9LWgqXY`"
|
:value="`git.landaiqing.cneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjI1MTEyMjE3MzQyMDIxIiwidHlwZSI6ImFjY2VzcyIsImV4cCI6MTczOTg3ODIyOCwibmJmIjoxNzM5ODcxMDI4LCJpYXQiOjE3Mzk4NzEwMjh9.EUiZsVjhGqHx1V5o90S3W5li6nIqucxy9eEY9LWgqXY`"
|
||||||
@@ -33,8 +38,15 @@
|
|||||||
:iconSize="iconSize"
|
:iconSize="iconSize"
|
||||||
:status="`active`"
|
:status="`active`"
|
||||||
/>
|
/>
|
||||||
<span style="font-size: 2vh;color: #999999">手机扫码上传</span>
|
</template>
|
||||||
</div>
|
<AButton @click.stop type="default" size="large" shape="round" style="width: 70%">
|
||||||
|
<template #icon>
|
||||||
|
<QrcodeOutlined/>
|
||||||
|
</template>
|
||||||
|
手 机 上 传
|
||||||
|
</AButton>
|
||||||
|
</APopover>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</AUploadDragger>
|
</AUploadDragger>
|
||||||
</div>
|
</div>
|
||||||
@@ -143,7 +155,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="loading && !uploadSuccess" class="image-share-right-bottom-loading">
|
<div v-if="loading && !uploadSuccess" class="image-share-right-bottom-loading">
|
||||||
<div class="image-share-right-bottom-loading-content">
|
<div class="image-share-right-bottom-loading-content">
|
||||||
<a-progress
|
<AProgress
|
||||||
type="circle"
|
type="circle"
|
||||||
:stroke-color="{
|
:stroke-color="{
|
||||||
'0%': '#108ee9',
|
'0%': '#108ee9',
|
||||||
@@ -428,6 +440,9 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
websocket.close(false);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.image-share-right {
|
.image-share-right {
|
||||||
@@ -674,6 +689,6 @@ onMounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2vh;
|
gap: 5vh;
|
||||||
}
|
}
|
||||||
</style>
|
</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